Trend API error handling patterns

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.

Map HTTP status codes before writing retry logic

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.

Retry decision tree

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.

Exponential backoff with jitter for 429 responses

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.

Distinguish not_found from data_unavailable

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.

Guard agent and MCP tool loops

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.

Idempotency and deduplication in webhook pipelines

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.

Empty results that return HTTP 200

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.

Monitoring and alerting thresholds

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-specific error reference

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.

When to fail open vs fail closed

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.

Common questions

Usually no. A 404 or not_found response means no time series matched the keyword and source combination. Retrying the same request will return the same result unless the keyword spelling or source value was wrong. Fix the input, try an alternate keyword phrasing, or mark the series as absent in the database before retrying.
Read the Retry-After header when present and wait at least that many seconds. When no header exists, use exponential backoff starting at 2-5 seconds with full jitter: sleep = random(0, min(cap, base * 2^attempt)). Cap total retries at 3-5 for interactive agents and higher for batch jobs with deadline slack.
not_found means the API understood the request but has no series for that keyword-source pair. data_unavailable means the upstream pipeline could not return data temporarily, often due to an empty scrape or a short-lived upstream gap. Retry data_unavailable with backoff; treat not_found as a permanent miss unless inputs change.
Set a per-session request budget in the agent system prompt, return structured error JSON to the model instead of raw stack traces, and wrap the API client with a circuit breaker that stops calls after consecutive 429 or 500 responses. Log keyword, source, and error code together so the agent can adjust inputs instead of repeating the same failing call.