Conventions and errors
Data lookup errors
Error codes returned when a post or profile cannot be read, and which ones are safe to retry.
Data lookups can fail because the post or profile is not readable. They return the standard error envelope; branch on error, never on message.
{
"error": "POST_AGE_RESTRICTED",
"message": "This post is age-restricted, so it cannot be scraped. Ask the creator to repost without an age restriction."
}error | HTTP | Retry? | Meaning |
|---|---|---|---|
POST_AGE_RESTRICTED | 422 | No | The post is age-gated and could not be read. Most age-gated posts are still read and succeed; for one that is not, only its author can fix this by reposting without the restriction |
POST_NOT_ACCESSIBLE | 422 | No | The post is private or otherwise not public |
POST_NOT_FOUND | 404 | No | The post no longer exists at that URL |
PROFILE_UNAVAILABLE | 404 | No | The profile does not exist or was deactivated |
PROFILE_RESTRICTED | 403 | No | The profile exists, but the platform withholds it from public lookups |
SCRAPING_RATE_LIMITED | 429 | Yes | Wait for the Retry-After header, then retry |
SCRAPING_UNAVAILABLE | 503 | Later | Reading the platform is temporarily unavailable |
SCRAPING_ERROR | 502 | Yes | The read failed for an unclassified reason |
403, 404 and 422 are permanent for that post or profile. Retrying them will not change the result. 429, 502, and 503 are transient.
In a batch, a failed URL carries the same code in error.code, and a URL the batch did not finish within 100 seconds carries BATCH_TIMEOUT; send it again in a new batch.
Routes that return these codes
GET /v1/statsandPOST /v1/stats/batchGET /v1/commentsGET /v1/profile(the only route that returns thePROFILE_*codes)
On the engagement verification routes:
GET /admin/posts/thumbnailPOST …/comments/refreshPOST …/metrics/refreshPOST /admin/creators/{userId}/refresh(for profiles,404means the profile no longer exists)
Liker fetching reports failures on the run's status and errorMessage instead; see Instagram likers.
Handling pattern
const params = new URLSearchParams({ url: postUrl });
const res = await fetch(`${API_BASE_URL}/v1/stats?${params}`, {
headers: { Authorization: `Bearer ${API_TOKEN}` },
});
if (res.ok) return await res.json();
const { error } = await res.json();
switch (error) {
case "RATE_LIMITED":
case "SCRAPING_RATE_LIMITED":
return retryAfter(Number(res.headers.get("Retry-After") ?? 60));
case "SCRAPING_UNAVAILABLE":
case "SCRAPING_ERROR":
return retryWithBackoff();
default:
// POST_*, PROFILE_* and VALIDATION_ERROR: permanent
return markPostUnreadable(error);
}Last updated on