Trend API clients fail in predictable ways: rate limits return 429, stale keywords return 404, upstream gaps return data_unavailable, and agents retry too aggressively and burn quotas. This page documents production error-handling patterns for trend pipelines: which errors to retry, how long to wait, and when to stop.
Trend API failures look like generic HTTP errors until quota is gone. A 429 during a launch-week keyword sweep can stall an entire pipeline. A 404 on a misspelled TikTok hashtag triggers wasteful agent retries. Empty arrays that return HTTP 200 are harder to catch than hard errors because monitoring tools mark the call as success.
These patterns apply to any trend data client, whether the backend is a SERP proxy, a Google Trends wrapper, or a multi-source API like Trends MCP. The error shapes differ; the retry logic does not.
Trend APIs converge on a small set of status codes. The handler should branch on status first, then on vendor-specific error bodies.
| Status | Typical meaning | Retry? | Action |
|---|---|---|---|
| 400 | Missing or invalid parameter | No | Fix request body; log invalid_source or missing_parameter |
| 401 | Bad or missing API key | No | Rotate key; alert ops; do not retry in a loop |
| 404 / not_found | No series for keyword-source pair | No | Try alternate keyword phrasing once; then mark absent |
| 429 | Rate limit or monthly quota exhausted | Yes, with backoff | Respect Retry-After; pause batch jobs |
| 500 | Server error | Yes, limited | 2-3 retries with backoff; then circuit break |
| 200 with empty data | Upstream gap or zero-volume keyword | Maybe once | Treat as data_unavailable if vendor documents it |
Trends MCP returns structured errors in JSON: {"error": "missing_parameter", "message": "The 'keyword' parameter is required."} Common codes include missing_parameter, invalid_source, not_found, data_unavailable, rate_limited, and internal_error (Trends MCP API docs, June 2026).
Scraping-based vendors often return 200 with an empty interest_over_time array when Google has no data. Treat empty 200 responses as a separate branch from HTTP errors. Log them as empty_series, not success.
Request failed or returned empty
│
├─ 401 / invalid API key → stop, alert, no retry
├─ 400 / missing_parameter / invalid_source → fix input, no retry
├─ 404 / not_found → try one spelling variant, then stop
├─ 429 / rate_limited → backoff retry (max 5) or pause batch
├─ 500 / internal_error → backoff retry (max 3)
├─ 200 but empty → if data_unavailable documented, retry once after 60s
└─ 200 with data → success, cache result
Agents violate this tree constantly because the model sees "error" and asks the tool again with identical parameters. The fix is server-side: the API client wrapper enforces the tree, not the prompt.
Blind retries on 429 make quota problems worse. Each retry counts against the limit on most vendors.
Recommended pattern for batch pipelines:
import random
import time
def retry_after_429(func, max_attempts=5, base_delay=2.0, cap=120.0):
for attempt in range(max_attempts):
response = func()
if response.status_code != 429:
return response
retry_after = response.headers.get("Retry-After")
if retry_after:
time.sleep(float(retry_after))
else:
delay = min(cap, base_delay * (2 ** attempt))
time.sleep(random.uniform(0, delay))
return response # caller handles final 429
Full jitter (random.uniform(0, delay)) prevents synchronized retries when a cron job fires 50 workers at the same second. AWS and Google publish this pattern; it applies equally to trend API batch jobs.
For monthly quota exhaustion (Trends MCP returns 429 with rate_limited when the monthly request limit is reached), backoff alone does not help. The handler should distinguish hourly throughput limits from monthly caps. Hourly limits clear after a short wait. Monthly caps require plan upgrade or pausing the pipeline until the next billing cycle.
These two outcomes need different database treatment.
not_found means the keyword-source pair is valid syntactically but has no stored series. Example: a Reddit source request for a subreddit that does not exist, or a Steam query for a game title with no match. Store a tombstone record with status: absent so the pipeline does not re-query daily.
data_unavailable means the vendor could not fetch upstream data at request time. Example: a temporary scrape failure or an upstream rate limit on the vendor's side. Store status: pending and schedule one retry in 1-24 hours.
Mixing them pollutes analytics. Absent keywords should drop off reports. Pending keywords should backfill on the next successful run.
MCP-connected agents call trend tools in tight loops when the model misinterprets an error message. Three controls reduce quota burn:
Per-session budget. Hard-cap tool calls at 20-50 per conversation for research tasks. Return a structured message when the cap hits: {"error": "session_budget_exhausted", "remaining": 0}.
Circuit breaker. After three consecutive 429 or 500 responses, open the circuit for 60 seconds. Return a single error to the agent explaining the pause. Libraries like pybreaker implement this in Python; a 10-line counter works for smaller stacks.
Input validation before the HTTP call. Reddit sources require subreddit names without the r/ prefix. TikTok expects hashtags without #. npm package names are case-sensitive. Catching invalid_source locally costs zero API credits. Trends MCP documents keyword formats per source in its API reference.
For MCP testing workflows before production, see Postman trends API testing.
Webhook-driven trend pipelines often receive duplicate events. Without deduplication, the same keyword triggers three API calls in one minute.
Store a dedup key: hash(source + keyword + date_bucket) where date_bucket is the hour or day for batch refreshes. Skip the HTTP call when the key exists and the cached result is younger than the TTL.
For webhook architecture patterns, see webhook trend pipeline.
Idempotency also matters on retry. If a POST creates a task (DataForSEO Standard Queue) and the client times out before reading the response, the retry may create a duplicate billed task. Use client-generated task IDs where the vendor supports them, or query pending tasks before resubmitting.
Google Trends relative indices can be empty for low-volume queries. SERP proxies return {"interest_over_time": []}. The HTTP status is 200. Dashboards show a flat line; alerts do not fire.
Add a post-response check:
def is_empty_series(response_json, vendor="generic"):
if vendor == "serpapi":
timeline = response_json.get("interest_over_time", {}).get("timeline_data", [])
return len(timeline) == 0
if vendor == "trendsmcp":
data = response_json.get("data", [])
return response_json.get("count", 0) == 0 or len(data) == 0
return False
Log empty_series separately from errors. Analysts need to know the keyword returned nothing versus the call failed.
Production trend stacks should alert on:
Tag every log line with source, keyword, error_code, and attempt_number. Without attempt_number, ops cannot tell whether the pipeline retried successfully or burned three credits on one keyword.
Trends MCP uses POST https://api.trendsmcp.ai/api with Bearer auth. Relevant error codes for handler design:
| Error code | HTTP status | Handler action |
|---|---|---|
| missing_parameter | 400 | Fix body; no retry |
| invalid_source | 400 | Check source string against docs; no retry |
| not_found | 404 | Tombstone keyword; optional spelling retry |
| data_unavailable | varies | Single retry after 60-300 seconds |
| rate_limited | 429 | Backoff; check monthly vs hourly limit |
| internal_error | 500 | Backoff retry max 3; then alert |
One Get Trends call equals one request against the monthly allocation. One Get Growth call also equals one request, with all percent_growth periods included in that single charge. Failed calls that return an error JSON before a successful response may still count depending on vendor billing rules; Trends MCP documents that only successful data retrieval consumes quota.
For Python client setup and request examples, see Python trends API. For orchestrating multiple sources behind one handler, see API orchestration for trend data.
Fail closed (skip the report row) when the keyword is business-critical and stale data is worse than no data. Earnings-preview dashboards and paid campaign launch checks fit here.
Fail open (show last cached value with a staleness badge) when the keyword is exploratory and continuity matters more than same-day freshness. Weekly content calendar scans fit here.
The choice should be explicit per pipeline, not left to whatever the retry loop happens to return on the last attempt.
FAQ