Docs / Instagram API Errors and Error Codes

Instagram API Errors and Error Codes

Every response tells you what happened and how many credits it cost. Billing depends on actual scraper resource use, not the HTTP status; check credits_charged on each response.

Maintained by the InScrape API team · Last reviewed 2026-09-19

How errors are billed

A request is billed only when scraping actually uses an Instagram or H API resource. Validation, authentication, and failures before resource use are free; check credits_charged on every response.

The permanent ones: 400, 401, 404

None of these get better on their own, and retrying them just delays the moment you find out. A 400 means a required parameter is missing or the wrong type and the message names the parameter. A 401 means the key is unknown, mistyped or revoked; check the header name before you check the key, because a wrong header name produces the identical response. Confirmed private accounts return 200 with available profile details. Profile also returns 200 with data.profile=null when the account cannot be found. A 404 is reserved for missing API routes. Confirmed missing targets on list endpoints return HTTP 200 with empty arrays.

402 is not a failure, it is a balance

INSUFFICIENT_CREDITS means the balance is below this endpoint's cost. The request itself was fine and can be replayed unchanged once you top up. Because credits_remaining is on every response including this one, you can catch a 402 before it happens by watching that number in ordinary traffic and alerting when it dips below a day of expected usage.

The retryable ones: 500 and 504

SCRAPE_FAILED means the upstream fetch failed. UPSTREAM_TIMEOUT means it exceeded the 25 second budget the gateway allows a single scrape. Both are transient, both are free, and both are worth retrying with exponential backoff. What is not worth doing is retrying immediately in a tight loop: if the upstream is under pressure, a fast retry loop makes the situation worse for everyone including you. Three or four attempts with backoff starting around half a second is a sensible default; anything still failing past that is an upstream condition rather than a blip, and is better surfaced than retried harder.

A retry policy in twenty lines

The whole policy is a set of two status codes. Everything else raises immediately, which keeps a bad parameter from spending four seconds pretending it might recover.

const RETRYABLE = new Set([500, 504]);
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

async function call(path, params, attempts = 4) {
  const url = new URL(path, "https://api.socialscrape.dev");
  for (const [k, v] of Object.entries(params)) url.searchParams.set(k, String(v));

  for (let attempt = 0; attempt < attempts; attempt++) {
    const res = await fetch(url, {
      headers: { "x-api-key": process.env.INSCRAPE_KEY },
    });
    const body = await res.json();

    if (res.ok) return body;
    if (!RETRYABLE.has(res.status)) {
      throw new Error(`${body.error.code}: ${body.error.message}`);
    }
    await sleep(500 * 2 ** attempt);
  }

  throw new Error("upstream still failing after retries");
}

Handling lookup outcomes in a batch job

If you are processing a list of handles, private accounts are a normal outcome rather than an exception. A completed lookup returns 200 and is billed at the profile endpoint rate. Record the handle as unreadable and move on; do not retry it unless its privacy status may have changed.

const results = [];
const skipped = [];

for (const handle of handles) {
  const body = await call("/v1/instagram/profile", { handle });
  if (body.data?.profile === null) {
    skipped.push({ handle, profile: body.data.profile });
    continue;
  }
  results.push(body);
}

The 429 you may have read about

A DEMO_RATE_LIMITED code sits in the error table with status 429, reserved for the unauthenticated demo on the marketing site. No keyed request can produce it, because there is no per-key request counter to trip. If your integration sees a 429, it is talking to the demo path rather than to the API.

Error reference

StatusCodeCauseRetry
400INVALID_PARAMSA required parameter is missing or the wrong typeNo. Fix the request
401INVALID_API_KEYKey unknown, mistyped or revoked, or wrong header nameNo. Check the header, then the key
402INSUFFICIENT_CREDITSBalance is below this endpoint's costNo. Top up, then replay unchanged
404ENDPOINT_NOT_FOUNDThe API endpoint does not exist; no credits chargedNo. Correct the API URL and endpoint path
500SCRAPE_FAILEDThe upstream fetch failedYes, with exponential backoff
504UPSTREAM_TIMEOUTThe scrape exceeded the 25 second budgetYes, with exponential backoff

FAQ

Am I charged for failed requests?

Requests are billed only when scraping actually uses an Instagram or H API resource, including private-account and not-found results discovered after resource use. Validation and authentication failures are free; service failures are free only if they happen before resource use.

The Instagram API is not working, what should I check first?

Read error.code rather than the status alone. A 401 is nearly always the header name or a revoked key, a 404 means a missing API route. Profile returns available profile details or data.profile=null on a 200 response. Only 500 and 504 indicate anything on our side.

Why can profile details be available while posts are inaccessible?

Check whether you are logged in when you look. An account that is visible to your logged-in session but private to a logged-out visitor is private as far as this API is concerned, because the API never logs in.

Should I retry a 402?

Not immediately. The request will fail identically until the balance covers the endpoint cost. Top up first, then replay.

How many retries are sensible?

Three or four with exponential backoff, on 500 and 504 only. Retrying the permanent codes achieves nothing.

Related