Errors
The error envelope, every code, and which ones are worth retrying.
Failures return a consistent JSON envelope. Branch on error_code, not on
detail — the wording can change, the code will not.
{
"status": "client_error",
"error_code": "profile_not_found",
"detail": "Profile not found or unavailable"
}status is derived from the HTTP status class: client_error for 4xx,
server_error for 5xx.
Only errors carry this envelope. A successful call returns the object, or the list envelope, directly — so branch on the HTTP status code first.
Codes
400 · invalid_cursor
The cursor you sent is not a cursor we issued. Cursors are opaque and must
be passed back byte-for-byte from a previous next_cursor. Not retryable —
restart the list from the first page.
400 · invalid_parameter
A parameter is present but unusable: a malformed media id, a pk/code pair
where exactly one was required, an unknown value. The detail names the
problem. Not retryable.
401 · unauthorized
Missing or invalid X-Api-Key. Also returned briefly if key verification is
offline — the API fails closed rather than serving unauthenticated requests.
Check the key, then retry once.
403 · private_account
The account exists but its content is private. Nothing to retry: this is Instagram's answer, and no amount of retrying changes it.
404 · profile_not_found
No such username or user id — deleted, banned, renamed, or never existed. Not retryable.
404 · not_found
The requested object is gone: a deleted post, an expired story, a hashtag or location with no such id. Not retryable.
422 · unprocessable_entity
Request validation failed before the call ran — a required query parameter is
missing, or a value has the wrong type. detail lists the offending fields.
Not retryable.
429 · too_many_requests
You exceeded your per-key rate limit of 10 requests per second. detail
carries the retry delay. Back off and retry.
502 · upstream_error
The call reached Instagram and could not be completed. Retry — it usually succeeds on the next attempt.
503 · upstream_unavailable
No capacity right now, or a transient failure that needs backing off. Retryable after a short wait.
Retry policy
| Code | Retry? | How |
|---|---|---|
upstream_error | Yes | Immediately, up to ~3 times. |
upstream_unavailable | Yes | Back off — 1s, 2s, 4s. |
too_many_requests | Yes | Wait the delay in detail. |
unauthorized | Once | After checking the key. |
| everything else | No | Fix the request. |
import time
import httpx
RETRYABLE = {502, 503}
def call(http: httpx.Client, path: str, **params) -> dict:
for attempt in range(3):
response = http.get(path, params=params)
if response.status_code not in RETRYABLE:
response.raise_for_status()
return response.json()
time.sleep(2**attempt)
response.raise_for_status()
return response.json()Failed calls are free
Only 2xx responses are billed, so a retry loop cannot drain your balance.
See Pricing.
Reporting a problem
Every response carries x-request-id. Include it — with the endpoint and
roughly when it happened — and we can find the exact call in the logs.