API reference

Errors

Every error code the API returns, what causes it, and whether retrying is worth your time.

Failures come back with the same shape every time.

{
  "ok": false,
  "code": "PRIVATE",
  "message": "That post is private, deleted, or age restricted. Public posts only."
}

Branch on code. The message is written for a person to read and the wording may change.

The codes

Code Status Cause Retry
INVALID_URL 400 Not an Instagram post link No
UNSUPPORTED 400 A link type we do not read, such as a Story No
PRIVATE 422 The post needs a login, is deleted, or is age restricted No
TOO_LONG 422 Video is over the duration cap No
TOO_LARGE 422 Audio is over the size the provider accepts No
RATE_LIMITED 429 You are over your limit After retry-after
CHALLENGE_FAILED 403 A bot check did not pass. Browser visitors only; API keys skip it No
FETCH_FAILED 502 Instagram would not serve us the post Yes, with backoff
TRANSCRIBE_FAILED 500 Something broke on our side Yes, with backoff
NOT_CONFIGURED 503 Transcription is off for this deployment No

The two you will actually see

PRIVATE

The most common failure, and it is permanent. Mark the link as unreadable and move on. A retry an hour later gives you the same answer.

Watch for it in bulk jobs. A run over a large list will hit plenty of these, and treating them as transient will waste the whole budget on retries that cannot succeed.

RATE_LIMITED

Read retry-after and sleep for that many seconds.

if (!body.ok && body.code === "RATE_LIMITED") {
  const wait = Number(response.headers.get("retry-after") ?? 60);
  await new Promise((resolve) => setTimeout(resolve, wait * 1000));
}

Retrying properly

Only FETCH_FAILED and TRANSCRIBE_FAILED deserve a retry. Use exponential backoff and stop after three attempts.

async function withRetry<T>(run: () => Promise<T>, attempts = 3): Promise<T> {
  for (let attempt = 1; ; attempt++) {
    try {
      return await run();
    } catch (error) {
      if (attempt >= attempts) throw error;
      await new Promise((resolve) => setTimeout(resolve, 2 ** attempt * 1000));
    }
  }
}

FETCH_FAILED usually means Instagram answered us with a login shell rather than the post. It often clears on its own within a minute or two.