Errors
A failed request returns the failure envelope and charges nothing:
{
"ok": false,
"error": { "code": "upstream_blocked", "message": "The platform blocked this fetch; retry (a fresh exit is used).", "retryable": true },
"credits_charged": 0,
"request_id": "req_0123456789abcdef01234567"
}Branch on error.code and error.retryable. Messages are for people and can change; codes are stable. There are 21 codes, each with one HTTP status.
Mistakes in the request#
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
invalid_params | 400 | no | A parameter is missing, unknown or has the wrong type. |
unsupported_url | 400 | no | The URL is not one this endpoint accepts (wrong platform or form). |
unauthorized | 401 | no | The x-api-key header is missing or not a valid key. |
insufficient_credits | 402 | no | The team behind the key has too few credits for this request. |
forbidden | 403 | no | The key or team is suspended or revoked. |
rate_limited | 429 | yes | Too many requests for this team's plan; wait for Retry-After. |
conflict | 409 | no | This Idempotency-Key was already used with a different request. |
The thing you asked for#
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
not_found | 404 | no | The platform says this profile, post or item does not exist. |
private | 403 | no | The content is private; only public, logged-out data is served. |
age_restricted | 403 | no | The platform hides this content from logged-out visitors (age gate). |
removed | 410 | no | The content existed but was deleted or taken down. |
unavailable_in_region | 451 | no | The platform does not serve this content in the requested region. |
transcript_unavailable | 404 | no | No captions exist and AI transcription is not possible for this media. |
The platform failed#
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
upstream_rate_limited | 503 | yes | The platform is rate limiting us; retry shortly. |
upstream_blocked | 503 | yes | The platform blocked this fetch; retry (a fresh exit is used). |
upstream_error | 502 | yes | The platform returned an error or the connection failed. |
upstream_changed | 502 | no | The platform's response no longer parses; we are fixing it. |
upstream_timeout | 504 | yes | The platform did not answer in time. |
Our side#
| Code | HTTP | Retry | Meaning |
|---|---|---|---|
overloaded | 503 | yes | This endpoint's queue is full; retry with backoff. |
not_implemented | 501 | no | This endpoint is declared but not built yet. |
internal | 500 | yes | Something broke on our side; it is logged. |
Retrying#
Retry when retryable is true: wait for the retry-after header if present, otherwise back off exponentially with some jitter. Do not retry the rest; the same request will fail the same way.
import random
import time
import requests
def get(url, headers, params, attempts=5):
for attempt in range(attempts):
res = requests.get(url, headers=headers, params=params, timeout=60)
body = res.json()
if body["ok"] or not body["error"]["retryable"]:
return body
wait = float(res.headers.get("retry-after", 2 ** attempt))
time.sleep(wait + random.random())
return bodyasync function get(url: string, headers: Record<string, string>, attempts = 5) {
let body: any;
for (let attempt = 0; attempt < attempts; attempt++) {
const res = await fetch(url, { headers });
body = await res.json();
if (body.ok || !body.error.retryable) return body;
const wait = Number(res.headers.get("retry-after") ?? 2 ** attempt);
await new Promise((r) => setTimeout(r, (wait + Math.random()) * 1000));
}
return body;
}Retrying a failure costs nothing. Retrying a POST that may have succeeded needs an Idempotency-Key; see Batch and jobs.
Where to look first#
not_found,private,removed: the item is not available to a logged-out visitor. Check the URL or handle in a browser, signed out.unsupported_url: you passed a URL from the wrong platform or an unrecognised form. The endpoint page lists the accepted hosts.upstream_changed: the platform changed and our parser no longer reads it. We are alerted automatically and fix it; it is not retryable until we do.invalid_params: the message names the parameter.
When you write to support, include the request_id.