Every error, one table.

status is always "error"; error.code is stable and safe to match on; error.message is for humans, not code. Derived from the published OpenAPI spec — see it match, byte for byte, at /openapi.json.

Every non-success response has the same shape:

{
  "status": "error",
  "error": {
    "code": "too_large",
    "message": "upload exceeds max size of 10485760 bytes; use ?url= for larger documents"
  }
}

Match on error.code — it's stable across releases. error.message is human-readable detail and can change; don't parse it.

codeHTTP statuscauseremediation
bad_request400The request was malformed — no file part or ?url= was given, or a retried request under a live Idempotency-Key had a different body/target than the original.Provide exactly one of a multipart file or ?url=. If you're reusing an Idempotency-Key, send the exact same request you sent the first time.
fetch_failed400The server-side ?url= fetch failed: a blocked/private address (SSRF-guarded), a connection error, a non-2xx response, or a timeout reaching the source.Confirm the URL is publicly reachable over https:// and returns a 2xx. txtfetch refuses to fetch private/internal/link-local addresses by design.
not_found404The job_id in GET /v1/extract/{job_id} is malformed, unknown, or has expired.Double-check the job_id from the 202 response. If it's expired, re-submit the original extraction to get a new one.
too_large413The direct upload exceeds the maximum accepted size for the multipart file path.Use ?url= instead of a direct upload for large documents — txtfetch fetches it server-side with a much higher size ceiling.
unsupported_format415The input's real, byte-detected type isn't a supported document format.Check /formats for the current list. txtfetch sniffs the actual bytes, not the file extension, so a mislabeled file won't help.
encrypted422The document is encrypted or password-protected, so Tika can't open it.Remove the password/encryption before submitting, or supply an already-decrypted copy.
extraction_failed422Extraction ran but produced no text — e.g. a blank scan, a corrupt file, or an image with no recoverable content.Confirm the source actually contains extractable text or legible imagery. Failed extractions don't count against your quota, so retries are free.
abuse_detected429This API key's abuse circuit breaker has tripped: a burst of abusive outcomes — repeated malformed input, and especially oversized/body-bomb rejections — not raw request volume. Distinct from rate_limited (per-minute volume) and quota_exceeded (monthly successes); legitimate throttling and successes never contribute to this.Stop sending the offending requests and back off until the Retry-After delta-seconds elapse. If this is unexpected, check your integration isn't retrying oversized/malformed payloads in a loop.
quota_exceeded429This API key's monthly document quota is exhausted for the current billing period. This counts only successful extractions and is distinct from the per-minute rate_limited check — failed extractions don't count against it.Wait for the monthly reset (X-Quota-Reset unix seconds, or the Retry-After delta-seconds), or upgrade to a larger plan in your dashboard (app.txtfetch.com).
rate_limited429This API key's per-minute request rate was exceeded. This counts every accepted request (even ones that go on to fail) and is unrelated to your monthly document quota (see quota_exceeded).Back off until the Retry-After delta-seconds elapse, or until X-RateLimit-Reset (unix seconds) passes, then retry.
not_implemented501The request asked for a valid but not-yet-shipped response mode — a format/quality combination other than the default text/standard.Omit format/quality overrides for now; only the default text extraction mode is implemented.
timeout504Extraction did not complete within the processing budget (large/complex documents, slow OCR).Retry the request, or force the async path with ?async=true so it isn't bound by the synchronous request timeout.

rate_limited-vs-quota

Both are 429s, but they mean different things. rate_limited is your API key's per-minute request rate — it counts every accepted request (including ones that go on to fail) and resets every minute, carrying Retry-After (seconds) and X-RateLimit-Limit/X-RateLimit-Remaining/X-RateLimit-Reset headers so you can back off precisely. quota_exceeded is your plan's monthly document quota — it counts only successful extractions, is checked after the rate-limit gate, and additionally carries X-Quota-Limit/X-Quota-Remaining/X-Quota-Reset. Wait for the reset or upgrade your plan.

quota_exceeded (429) is your plan's monthly document quota — it counts only successful extractions, so failed requests never burn it. It resets once per UTC month. The response carries Retry-After (seconds until that reset) and X-Quota-Limit/X-Quota-Remaining/X-Quota-Reset headers, plus the X-RateLimit-* triad — the rate check runs first, so its headers are always present too. Match on error.code to tell the two 429s apart, not on the status alone. X-Quota-* headers also ride on every successful 200/202 response, so you can budget ahead of time instead of waiting to hit the limit.

authentication-failures

A missing Authorization header returns 401; an invalid or revoked key returns 403. Both come back with API Gateway's own {"message": "…"} body — deliberately not the typed {"error": {"code": …}} envelope above, since that's a platform limit of the HTTP-API authorizer, not something this API controls. Integrators should match on HTTP status for auth failures, never on a code field. A revoked key can take up to 60 seconds to stop working (the authorizer caches its allow/deny decision for that long). Create a key in your dashboard.

typed-exceptions

Both official SDKs map every code above to its own exception class (e.g. TooLargeError, UnsupportedFormatError) deriving from a common TxtfetchError — see the quickstarts for the full class list.

machine-readable-spec

This table is generated at build time from the published OpenAPI 3.1 spec's ErrorCode enum and per-code HTTP status — the build fails if this page's editorial guidance ever falls out of sync with the contract.

Stop parsing. Start shipping.

Create an account and get an API key in minutes — the free Hobby plan needs no card.

Get started →