Errors

Error envelopes, request IDs, outcome handling, and common retry decisions

Social Fetch uses two response shapes: { data, meta } for successes and { error } for failures. HTTP 200 can still carry not_found or private inside data — check both the status code and the outcome fields.

Success envelope

Successful JSON responses use a { data, meta } envelope.

  • data — endpoint-specific payload
  • meta — response metadata, including a support-friendly request ID

Example (200):

{
  "data": {
    "lookupStatus": "found",
    "profile": {
      "platform": "tiktok",
      "handle": "charlidamelio",
      "displayName": "charli",
      "bio": "hey",
      "avatarUrl": "https://example.com/avatar-large.jpg",
      "verified": true,
      "profileUrl": "https://www.tiktok.com/@charlidamelio",
      "privateAccount": false
    },
    "metrics": {
      "followers": 150000000,
      "following": 123,
      "likes": 12345678901,
      "posts": 456
    }
  },
  "meta": {
    "requestId": "req_01example",
    "creditsCharged": 1,
    "version": "v1"
  }
}

The exact data shape varies by endpoint — see the operation's 200 schema in the API reference.

Outcome semantics

Some endpoints report domain outcomes inside a 200 rather than as a 4xx. Example — profile lookupStatus: "not_found":

{
  "data": {
    "lookupStatus": "not_found",
    "profile": null,
    "metrics": null
  },
  "meta": {
    "requestId": "req_01example",
    "creditsCharged": 1,
    "version": "v1"
  }
}

Instagram post lookup returns lookupStatus: "restricted" when the URL is valid but the media isn't publicly accessible (for example, age-gated):

{
  "data": {
    "lookupStatus": "restricted",
    "post": null,
    "owner": null,
    "metrics": null,
    "media": null,
    "downloads": []
  },
  "meta": {
    "requestId": "req_01example",
    "creditsCharged": 1,
    "version": "v1"
  }
}

Rule of thumb:

  • HTTP 200 — parse the success envelope and check outcome fields (like data.lookupStatus).
  • Non-200 — parse the error envelope below.

Outcome values

  • found — target resolved; expected data is present.
  • not_found — target doesn't exist or couldn't be resolved.
  • private — target exists, but data isn't public.
  • restricted — target reachable but can't be returned (age-gated posts, bot-protected pages, etc.).

Web extraction routes (GET /v1/web/markdown, /html, and /ask) return lookupStatus: "restricted" with HTTP 200 when a page can't be fetched because of access protection. Content fields (markdown, html, answer) come back null. Don't retry these as transient failures.

Confirm the exact response shape on the endpoint page in the API reference.

Empty result vs. not found

An empty collection doesn't always mean "not found." It can mean the profile has no media, the target is private, a post is restricted, or the target is missing. Keep these cases distinct.

Disambiguating list routes

Some list routes return HTTP 200 with an empty collection for more than one reason and don't expose data.lookupStatus. When that distinction matters:

  1. Call the platform profile route for the same handle (GET /v1/{platform}/profiles/{handle}).
  2. Read data.lookupStatus from that response.
  3. Call the list route only if you still need feed items.

If you only need "posts or nothing," skip the profile preflight.

Client logic

  1. Check the HTTP status.
  2. On 200, inspect outcome fields.
  3. Fall into error handling only on non-200.

With the TypeScript SDK, that maps to result.ok and result.value.data.

Credits and outcomes

Metered endpoints can still charge when a lookup completes — including not_found, private, or restricted. See Credits.

Error body

Error responses use a shared { error } envelope:

{
  "error": {
    "code": "unauthorized",
    "message": "Missing API key.",
    "requestId": "req_01example"
  }
}

error.code

error.code is machine-readable. Stable values:

  • bad_request
  • unauthorized
  • insufficient_credits
  • payment_required
  • payment_settlement_failed
  • lookup_failed
  • transcript_target_not_video
  • video_too_long_for_transcription
  • temporarily_unavailable
  • internal_error

Check the API reference for the exact codes each route returns per HTTP status (for example 400, 401, 402, 502, 503).

Request ID

Every response carries a request ID — meta.requestId on success, error.requestId on failure. Log it on failures and quote it in support requests.

Status codes at a glance

Retrying safely

On 503, honor Retry-After when present; otherwise use exponential backoff. Don't blindly retry 4xx errors — fix the request first. HTTP 402 has credits, x402 challenge, and settle-failure meanings — classify before retrying.

  • 400 bad_request — invalid request, or a known limitation such as transcript_target_not_video or video_too_long_for_transcription.
  • 401 unauthorized — missing or invalid API key.
  • 402three related meanings: (1) JSON insufficient_credits when using an API key without enough credits (optional checkoutUrl); (2) x402 payment required when no API key is sent — JSON payment_required plus the PAYMENT-REQUIRED header; (3) JSON payment_settlement_failed when payment verified and the handler succeeded but USDC settle failed (resource body withheld). See x402. Do not treat every 402 as credits exhaustion.
  • 429 temporarily_unavailable — free routes only (whoami, balance); per-key rate limit. Paid metered saturation uses 503, not 429.
  • 500 internal_error — unexpected server error.
  • 502 lookup_failed — the lookup couldn't be completed.
  • 503 temporarily_unavailable — service briefly unavailable (including queue/capacity saturation under extreme concurrency). Safe to retry; honor Retry-After when present. Not charged on metered lookup routes — see Credits.

On this page