Async jobs & webhooks
On this page
Create and poll jobs
POST /v1/jobs accepts the same body as /v1/extract and returns 202 { job_id } before any model call. Poll GET /v1/jobs/:id, or set options.webhook_url per job. The same `Idempotency-Key` header works here.
A succeeded job’s result stays available on GET /v1/jobs/:id for 24 hours; after that the lookup returns 404. Fetch the result and store it on your side. Polling GET /v1/jobs/:id is not rate limited.
Additional job options
- options.webhook_url string optional
- An
httpsURL, max 2048 characters, on a plain public hostname: no ports, no IP literals, nolocalhostor internal suffixes. Called when the job completes or fails. - options.batch boolean optional
- Opts the job into the batch tier.
Verify webhook deliveries
Deliveries follow the Standard Webhooks scheme. Each POST carries three headers: webhook-id, webhook-timestamp (unix seconds) and webhook-signature (v1,<base64>), an HMAC-SHA256 over id.timestamp.body.
The secret is the whole whsec_… string from the dashboard: Keys, on the “Webhook signing secret” card. Pass it as is to any standard-webhooks library, or use the SDK helper: verifyWebhook in TypeScript, verify_webhook in Python. Verify the signature before parsing the body; verification rejects timestamps more than 300 seconds from your clock.
Rotate the secret from the same card. For 24 hours after a rotation, deliveries carry signatures under both the new and the previous secret (webhook-signature is a space-delimited list; standard-webhooks libraries accept any matching entry), so the old secret keeps verifying while you switch. After that, only the new secret verifies.
- Acknowledge
- Any 2xx acknowledges the delivery. Redirects are not followed; every other outcome is retried.
- Retries
- Delivery is at-least-once: 5 attempts with exponential backoff from 20 seconds, capped at 15 minutes, then the delivery is dead-lettered. Dedupe on
webhook-id; it is identical across redeliveries of the same event. - Events
- One terminal event per job:
job.completedorjob.failed. Onlyjob.completedcarries aresult_url. Ignore event types you do not recognize; new types are additive.
Bodies carry ids and page counts, never extracted content. Fetch the result from result_url with your API key.