Envelope and errors

What every response looks like, every error code, and what to retry.

The envelope

{
  "data": { "…": "…" },
  "meta": { "count": 20, "limit": 20, "next_cursor": "…" },
  "request_id": "req_01J…"
}

data is one object, or an array on list endpoints. meta appears on lists. request_id is on every response, including errors.

Two headers travel with every billed response: X-Credits-Charged and X-Credits-Remaining. x-cache says hit or miss.

Errors

{
  "error": {
    "code": "not_found",
    "message": "reel not found",
    "hint": "…",
    "request_id": "req_01J…"
  }
}
StatusCodeWhen
400invalid_url, invalid_handle, invalid_queryMissing or malformed url, handle or query
400unsupported_platform_urlThe URL is not an instagram.com URL
400invalid_limitlimit is not an integer in range
400upstream_spend_ceilingThe request would exceed the per-request cost ceiling; lower limit
401missing_api_key, invalid_api_keyNo key, or a wrong or revoked one
402out_of_creditsBalance too low for the call
402plan_requiredA free key was refused by an endpoint’s per-call minimum; the message names the endpoint and the minimum, and a plan is the fix
404not_foundPrivate, deleted, no transcript, or the URL resolves to nothing
409job_not_readyJob results requested before the job finished
429rate_limitedPlan requests-per-minute exceeded
500internal_errorOur side; safe to retry
502upstream_failureTransient fetch failure; safe to retry
504upstream_timeoutThe source took too long; safe to retry

Retry 429 and 5xx with exponential backoff. Do not retry 400, 401, 402 or 404; the request itself needs to change.

import httpx, time

for attempt in range(5):
    r = httpx.get(url, headers=headers, timeout=120)
    if r.status_code == 429 or r.status_code >= 500:
        time.sleep(2 ** attempt * 2)
        continue
    break

Failed calls are not charged. Empty results are free up to 25 a day per account; after that an empty result costs one credit.