Reference

POST /v1/extractions

The contract voice: parameters, response schema, and every failure state the endpoint can produce.

Submits a document for extraction against a named schema. Returns an extraction in the processing state; the result arrives on the extraction.succeeded webhook or by retrieving the extraction by id.

POST https://api.sift.dev/v1/extractions

Requires a secret key with the extractions:write scope.

Request headers

Header Required Description
Authorization Yes Bearer sk_live_… or Bearer sk_test_…. Test keys are accepted only by the sandbox host.
Idempotency-Key Yes Client-generated, 16 to 255 characters, one per logical operation. Use a UUIDv4 or anything else with at least 128 bits of entropy; the server enforces the length, not the entropy. Stored for 24 hours.
Content-Type Yes application/json, or multipart/form-data when uploading file bytes directly.
Sift-Version No Pins a dated release, for example 2026-09-01. Omitting it pins to the release current at the account's first call.

Request body

Exactly one of document_url or document_id is required. Supplying both returns 422 mutually_exclusive_fields; supplying neither returns 422 validation_failed.

Field Type Required Description
document_url string (uri) Conditional HTTPS URL of the source document. Must be reachable without authentication and must resolve within 10 seconds.
document_id string Conditional Identifier of a document previously uploaded to /v1/documents. Prefixed doc_.
schema string Yes The extraction schema. One of invoice, receipt, contract, purchase_order, or a custom schema id prefixed sch_.
pages string No Page range to extract, for example 1-4 or 2. Defaults to all pages. A range beyond the document length is truncated, not rejected.
webhook_url string (uri) No Overrides the account-level webhook for this extraction only.
metadata object No Up to 20 key-value pairs, returned unchanged on the extraction and on every webhook for it. Values are strings of at most 500 characters.

Maximum document size is 50 MB or 500 pages, whichever is reached first. Accepted types: PDF, PNG, JPEG, TIFF, HEIC.

Example request

curl https://api.sift.dev/v1/extractions \
  -H "Authorization: Bearer $SIFT_KEY" \
  -H "Idempotency-Key: 3b9f1c2e-7a41-4d8e-9c65-0f2a8e71d4b3" \
  -H "Content-Type: application/json" \
  -d '{
    "document_url": "https://files.example.com/inv/2026-004182.pdf",
    "schema": "invoice",
    "metadata": { "ap_batch": "2026-09-W1" }
  }'

Response

202 Accepted with an Extraction in the processing state. The Location header carries the canonical URL of the extraction.

{
  "id": "ext_Hq3Vx8Nc2T",
  "object": "extraction",
  "status": "processing",
  "schema": "invoice",
  "document_id": "doc_R7fTm2Wq9E",
  "metadata": { "ap_batch": "2026-09-W1" },
  "created_at": "2026-09-01T14:32:07Z"
}

Extraction

Field Type Description
id string Opaque identifier, prefixed ext_.
object string Always extraction. Present so a heterogeneous collection or a webhook payload can be dispatched without inspecting the URL it arrived from.
status enum processing, succeeded, or failed. succeeded means the extraction completed, not that the values are correct.
schema string The schema the document was extracted against.
schema_match number 0.0–1.0. How well the document matches the requested schema. Present only when status is succeeded. A value below 0.5 usually means the wrong schema was requested.
document_id string The stored source document, prefixed doc_. Present even when the request supplied document_url: the URL is fetched once at submission and stored, so a later retry does not depend on the URL still serving.
fields object Map of field name to Field. Present only when status is succeeded.
document object Source document properties, including quality. Present only when status is succeeded.
pages integer Page count of the source document. Present only when status is succeeded.
metadata object The key-value pairs supplied on the request, returned unchanged. {} when none were supplied.
failure_code string | null Populated only when status is failed. See Failure codes.
created_at string RFC 3339 timestamp, UTC. When the extraction was accepted.
completed_at string | null RFC 3339 timestamp, UTC. null while processing.

Field

Field Type Description
value any | null The extracted value, typed per the schema. null means the field was not found in the document.
confidence number 0.0–1.0, calibrated per field against human-verified documents. 0.0 with a null value means absence, not low certainty.
page integer | null 1-indexed page the value was read from.
bbox array | null [x0, y0, x1, y1] in PDF user space, for highlighting the source region. null for values inferred rather than read.
alternatives array Other readings the model considered, each with value and confidence, ordered by descending confidence. Omitted when empty. Its presence indicates a contested reading and should trigger human review regardless of the headline confidence.

document.quality

Field Type Description
score number 0.0–1.0 overall legibility.
issues array Zero or more of blur, skew, crop, low_contrast, glare, handwriting.

A score below 0.4 means no threshold will rescue the extraction. Reject the document at intake and request a better capture.

Errors

Errors use application/problem+json per RFC 9457. Branch on code, which is stable, never on title or detail.

Status code Cause Retry
400 malformed_body The body is not valid JSON, or not JSON at all. No, not until the body parses
400 missing_idempotency_key The Idempotency-Key header is absent or shorter than 16 characters. No, not without a key
401 invalid_api_key Key revoked, malformed, or sent to the wrong host. No
403 insufficient_scope Key lacks extractions:write. No
409 idempotency_in_progress The original request is still running. Yes, after a short delay
409 idempotency_key_reused The key was used with a different body. Caller bug. No
413 document_too_large Over 50 MB or 500 pages. No
415 unsupported_media_type File type is not an accepted format. No
422 validation_failed A required field is missing or a value is out of range. errors[] names every one as a JSON Pointer, not just the first. No, not until the body changes
422 mutually_exclusive_fields Both document_url and document_id supplied. No
422 unknown_schema schema is not a built-in name or a visible sch_ id. No
422 document_unreachable document_url did not resolve within 10 seconds or returned a non-2xx status. Yes, once the URL is serving
429 rate_limit_exceeded Quota exhausted for the window. Yes, after Retry-After, with jitter
500 internal_error Failure on our side. Outcome is unknown, not failed. Yes, with the same Idempotency-Key

Failure codes

These appear on failure_code when status is failed. They are not HTTP errors: the request succeeded and the extraction did not.

failure_code Means Action
document_corrupt The file could not be decoded. Request a new file.
document_encrypted Password-protected PDF. Request an unprotected copy.
document_empty No text or image content on any requested page. Check the pages range.
quality_below_threshold Legibility too low to attempt extraction. Request a better capture.
processing_timeout Exceeded the 10-minute ceiling. Retry, or split the document.

Idempotency

Idempotency-Key is required. A replay with an identical body returns the stored response verbatim with Idempotent-Replay: true and does not create a second extraction. A replay with a different body returns 409 idempotency_key_reused and processes nothing. Keys are forgotten after 24 hours.

A 5xx on this endpoint means the outcome is unknown. Retry with the same key. Retrying with a new key is how duplicate extractions, and duplicate charges, are created.

Rate limits

100 requests per 60-second window per account by default. Responses carry RateLimit and RateLimit-Policy using the structured-field syntax of draft-ietf-httpapi-ratelimit-headers, which is an Internet-Draft rather than a published RFC; treat those two as advisory pacing information and depend on Retry-After, which is standardized in RFC 9110.

Submission is cheap and retrieval is not. Use webhooks rather than polling. An integration that polls every extraction every second will exhaust its quota on retrieval before it exhausts it on work.

← Back to Portfolio