A single POST call returns ranked X trending topics as JSON. This page covers polling intervals, deduplication, alert thresholds, and monthly request budgeting so a cron job or serverless worker can watch the feed without burning quota.
Pulling the live X trending feed once is straightforward. Keeping a reliable monitor running for weeks without duplicate alerts, quota overruns, or silent failures takes explicit scheduling and state. The X trending topics API returns ranked topic names as JSON; this page covers how to wire that response into cron, serverless, and alert pipelines.
Each call uses the same body documented on the X API page:
{
"mode": "top_trends",
"type": "X (Twitter)",
"limit": 50
}
The response includes as_of_ts, count, and a data array of [rank, topic_name] pairs. Store the full payload, not just topic names. Rank position matters when the alert logic cares about a topic climbing from #18 to #3 in one interval.
Trends MCP counts one source per get_top_trends call. The free tier allows 100 requests per month with no credit card. Paid plans raise that ceiling; see pricing for current limits.
| Polling interval | Requests per day (24h) | Requests per 30 days |
|---|---|---|
| Every 15 minutes | 96 | 2,880 |
| Every 30 minutes | 48 | 1,440 |
| Every 60 minutes | 24 | 720 |
| Every 4 hours | 6 | 180 |
| Twice daily (9am, 5pm) | 2 | 60 |
Continuous polling at sub-hourly intervals exceeds the free tier within days. Practical free-tier setups:
Linux cron (hourly, weekdays 8am-6pm UTC)
0 8-18 * * 1-5 /usr/local/bin/poll-x-trends.sh
GitHub Actions (twice daily)
on:
schedule:
- cron: "0 13 * * 1-5"
- cron: "0 21 * * 1-5"
jobs:
poll:
runs-on: ubuntu-latest
steps:
- name: Fetch X trending topics
env:
TRENDS_API_KEY: ${{ secrets.TRENDS_API_KEY }}
run: |
curl -s -X POST https://api.trendsmcp.ai/api \
-H "Authorization: Bearer $TRENDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"mode":"top_trends","type":"X (Twitter)","limit":50}' \
-o /tmp/x-trends.json
AWS EventBridge + Lambda: trigger on a rate expression of rate(4 hours) and keep the handler under 30 seconds. Persist state in DynamoDB or S3.
import json
import requests
from pathlib import Path
STATE_FILE = Path("x_trends_state.json")
API_URL = "https://api.trendsmcp.ai/api"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY"}
def load_state():
if STATE_FILE.exists():
return json.loads(STATE_FILE.read_text())
return {"topics": {}, "last_poll": None}
def save_state(state):
STATE_FILE.write_text(json.dumps(state, indent=2))
def poll_x_trends():
res = requests.post(
API_URL,
headers=HEADERS,
json={"mode": "top_trends", "type": "X (Twitter)", "limit": 50},
timeout=30,
)
if res.status_code == 429:
raise RuntimeError("Monthly quota exhausted; stop polling until next cycle")
res.raise_for_status()
return res.json()
def diff_topics(current, previous):
alerts = []
prev_names = set(previous.get("topics", {}).keys())
for rank, name in current["data"]:
name_lower = name.lower()
if name_lower not in prev_names:
alerts.append({"type": "new", "topic": name, "rank": rank})
else:
old_rank = previous["topics"].get(name_lower, {}).get("rank", 99)
if old_rank - rank >= 5:
alerts.append({"type": "climb", "topic": name, "from": old_rank, "to": rank})
return alerts
def main():
state = load_state()
payload = poll_x_trends()
alerts = diff_topics(payload, state)
new_state = {
"last_poll": payload["as_of_ts"],
"topics": {t[1].lower(): {"rank": t[0]} for t in payload["data"]},
}
save_state(new_state)
for alert in alerts:
print(json.dumps(alert))
if __name__ == "__main__":
main()
Run this script from cron. Pipe alert JSON to a Slack relay as described on the Slack trend alerts page.
Raw diffs fire on every minor rank shuffle. Tighter rules:
Skip alerts when count in the response is zero or when as_of_ts is older than two polling intervals. Stale timestamps usually mean an upstream fetch failed silently.
A topic can trend on X without broader search interest. Before paging a team, one extra request validates whether the spike is isolated:
growth = requests.post(
API_URL,
headers=HEADERS,
json={
"source": "google search",
"keyword": alert["topic"],
"percent_growth": ["7D", "30D"],
},
).json()
If 7-day Google Search growth is flat or negative, label the Slack message as "X-only spike" so reviewers know the signal may be platform-specific chatter.
| Status | Action |
|---|---|
| 401 | Fix API key in secrets manager; do not retry |
| 429 | Stop scheduled jobs until next billing cycle |
| 500 | Retry once after 60 seconds; log and alert ops if second attempt fails |
| Timeout | Retry once; if feed is consistently slow, widen the cron interval |
Compared with the official X API pricing model, where each post read costs $0.005, a monitoring stack that tried to infer trends by reading thousands of tweets would burn credits fast. The aggregated trending feed avoids that cost structure entirely.
X trending data is live-only. There is no historical time series for rank position over months. Teams that need longitudinal social volume should pair this monitor with Reddit or Google Search series from the same API. For brand-specific mention tracking at post level, enterprise social listening platforms remain the fit; see the social listening API pricing comparison for contract-scale options.
FAQ