Errors and retries

ErrorResponse envelopes, typed SDK errors, safe retries, idempotency, and timeouts.

Non-success HTTP responses return a JSON ErrorResponse envelope:

{
  "error": {
    "code": "limit_exceeded",
    "message": "source exceeds 50 MiB admission limit",
    "request_id": "req_01h2example",
    "retryable": false,
    "details": {
      "limit": "max_input_bytes",
      "max_value": 52428800
    }
  }
}

Include request_id when contacting support. When retryable is false, fix the request before sending it again.

Common errors

CodeTypical HTTPMeaning
idempotency_conflict409The key was already used with different input.
limit_exceeded413A file, rendered page, or batch exceeds a limit.
unsupported_media_type415The upload is not a supported PDF, PNG, or JPEG.
unsupported_ocr_model422ocr_model is unknown.
unsupported_llm_model422llm_model is unknown or deprecated.
pipeline_name_conflict409The pipeline name or slug already exists.

Synchronous parse and extract requests also return 422 if the job reaches failed during the wait window and 504 if it reaches indeterminate.

Admission limits

LimitValue
File uploaded for parse or extract50 MiB
Reusable file uploaded to POST /files100 MiB
Rendered pixels per page100 million
Aggregate batch input100 MiB
Child jobs per batch100

There is no page-count limit. OpenParser accepts PDF, PNG, and JPEG; encrypted PDFs and source URLs are not supported.

Typed errors

The TypeScript and Python SDKs map HTTP status codes to typed subclasses of OpenParserError. Each carries status, code, requestId / request_id, retryable, and the parsed API body. curl returns the response body directly. The CLI writes a concise error to stderr and exits non-zero; use --json for structured successful output.

HTTPClass
400OpenParserValidationError
401OpenParserAuthError
402OpenParserPaymentRequiredError
403OpenParserForbiddenError
404OpenParserNotFoundError
409OpenParserConflictError
413OpenParserLimitExceededError
415OpenParserUnsupportedMediaError
422OpenParserUnprocessableError
429OpenParserRateLimitError (retryAfter / retry_after)
503OpenParserServiceUnavailableError (retryAfter / retry_after)
504OpenParserGatewayTimeoutError
5xxOpenParserServerError
0OpenParserTimeoutError (client-side deadline, not an API response)

429 and 503 also expose the Retry-After response header on the exception when the server sends it.

curl -sS -w '\n%{http_code}\n' ... | tee /tmp/op-response.json
jq '.error' /tmp/op-response.json
if ! openparser jobs get "$JOB_ID" --json; then
  echo "OpenParser request failed" >&2
  exit 1
fi
import {
  OpenParserRateLimitError,
  OpenParserTimeoutError,
} from '@openparser/sdk';

try {
  await client.parse.sync({ ocr_model: 'paddleocr-vl-1.6' }, file);
} catch (err) {
  if (err instanceof OpenParserRateLimitError) {
    const waitSeconds = err.retryAfter ?? 60;
    // ...
  }
  if (err instanceof OpenParserTimeoutError) {
    // Per-request deadline (default 300_000 ms) — not retried
  }
  throw err;
}
from pathlib import Path
from openparser.errors import OpenParserRateLimitError, OpenParserTimeoutError

try:
    client.parse.sync(
        {"ocr_model": "paddleocr-vl-1.6"},
        file=Path("document.pdf"),
    )
except OpenParserRateLimitError as err:
    wait_seconds = err.retry_after or 60
except OpenParserTimeoutError:
    # Per-request deadline (default 300 s) or wait_for_job timeout
    raise

Safe retries

OpenParser does not silently retry work server-side. Clients may retry only when replaying the same request is safe.

Retriable HTTP statuses: 429 and 5xx. Other 4xx responses fail fast — fix the input or credentials before trying again.

Retriable requests:

RequestRetries on 429 / 5xx / transport error?
GET, HEAD (jobs, models, pipelines)Yes
Parse/extract admission (POST with Idempotency-Key)Yes
File upload, pipeline create/update/delete, suggest-schemaNo

The TypeScript and Python SDKs retry eligible requests up to maxRetries / max_retries (default 3, so up to four attempts including the first). Between attempts they wait for Retry-After when the header is present and parseable; otherwise they use exponential backoff starting at 250 ms (250 × 2^attempt).

Client-side request timeouts (OpenParserTimeoutError, status = 0) are not retried in the TypeScript SDK. In Python, an httpx timeout on a retriable request may still be retried until attempts are exhausted.

Idempotency

Parse and extract admission (POST /parse, /parse/async, /parse/batch, /extract, /extract/async, /extract/batch) requires an Idempotency-Key header (1–256 characters).

  • Generate a new key for each distinct request body.
  • Reuse the same key only when retrying the exact same admission after a network error, ambiguous timeout, or other unclear outcome.
  • Reusing a key with different input returns 409 with idempotency_conflict.

Helper routes (GET, file pool, pipelines) do not use idempotency keys.

curl -X POST 'https://api.openparser.dev/parse' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H "Idempotency-Key: $(uuidgen 2>/dev/null || openssl rand -hex 16)" \
  -F 'request={"ocr_model":"paddleocr-vl-1.6","output_format":"openparser@1"};type=application/json' \
  -F 'file=@./document.pdf'
openparser parse sync ./invoice.pdf --idempotency-key "invoice-2026-04-01"
const key = 'invoice-2026-04-01';
const retryingClient = new OpenParserClient({ maxRetries: 3 });

const parsed = await retryingClient.parse.sync(
  { ocr_model: 'paddleocr-vl-1.6' },
  file,
  { idempotencyKey: key },
);
key = "invoice-2026-04-01"
retrying_client = OpenParserClient(max_retries=3)

parsed = retrying_client.parse.sync(
    {"ocr_model": "paddleocr-vl-1.6"},
    file=Path("invoice.pdf"),
    idempotency_key=key,
)

Retry-After

For 429 Too Many Requests and 503 Service Unavailable, the API may send a Retry-After header with the number of seconds to wait before trying again. A temporary lack of OCR capacity does not reject a valid job — it remains queued.

When retrying admission after rate limiting or overload:

  1. Wait for Retry-After (or the retryAfter / retry_after field on the SDK exception).
  2. Reuse the same Idempotency-Key and request body.

The TypeScript SDK parses Retry-After as a non-negative delta in seconds. The Python SDK also accepts an HTTP-date. Malformed or missing values fall back to exponential backoff.

Timeouts

Two different timeout concepts apply:

KindHTTP statusMeaning
Client deadline0 (OpenParserTimeoutError)The SDK or HTTP client stopped waiting (default 300 s per request).
Sync gateway timeout504 (OpenParserGatewayTimeoutError)The server returned before the job reached a terminal state during a synchronous wait (indeterminate).

Defaults: timeoutMs: 300_000 (TypeScript) and timeout_seconds: 300.0 (Python). Set timeoutMs <= 0 in TypeScript to disable the per-request deadline.

Disconnecting does not cancel an admitted job. OpenParser API v1 has no job-cancellation endpoint, and a result published after you stop waiting is still billable.

curl --max-time 300 ...
openparser parse sync ./document.pdf --json
const client = new OpenParserClient({
  timeoutMs: 300_000, // default; 0 disables
  maxRetries: 3,
});
client = OpenParserClient(timeout_seconds=300.0, max_retries=3)

# Poll async jobs (separate from per-request HTTP timeout)
job = client.wait_for_job(job_id, timeout_seconds=300.0, poll_interval_seconds=2.0)

On this page