Extract asynchronously and poll
Submit without holding a connection open, then collect the result.
Goal. Submit a document, get an ID back immediately, and collect the result when it is ready — without a 90-second connection hanging open.
This is the pattern most production integrations use. The request body is identical to the synchronous quickstart; only the URL and the retrieval change.
Generated endpoint reference
Use Extract document data asynchronously to submit the job. Use Get extraction details to retrieve it.
Before you start
An API key, exported as
$DOCAI_API_KEY.Somewhere to keep the returned ID between the submit and the poll.
Step 1 — Submit the job
Drop /sync from the URL. Everything else stays the same.
curl -X POST "https://api-docai-uat.uptiq.ai/extract" \
-H "X-Api-Key: $DOCAI_API_KEY" \
-H "Content-Type: application/json" \
--data-binary @request.jsonIt returns in well under a second:
{
"data": {
"_id": "3f2a91c4-7b58-4e12-9d63-0a5e8c1b74df",
"documentStatus": "Pending",
"documentType": "BalanceSheet",
"requestId": "a7d0e514-63b2-4f97-8c05-1d4f9b28e3a6",
"subtype": null
},
"message": "Document queued for extraction",
"status": "success"
}Keep data._id. That is what you poll with.
Step 2 — Understand the retrieval shape before you write the loop
This is where first attempts go wrong, so it is worth reading the response carefully before writing any code against it.
Compare that with what you submitted, because two things changed:
Wrapper
data
extraction
Job status field
documentStatus
status
The retrieve response has two status fields at different levels. The top-level status is the envelope — it reads "success" whenever the HTTP call worked and never changes. The job state is extraction.status.
A loop that polls on the top-level status sees "success" on the first attempt and spins forever. This is not hypothetical: it happened while writing this page, and the loop ran to its iteration limit against an extraction that had already finished.
The field you want is extraction.status.
Step 3 — Poll until terminal
Pending and Processing are transient; Processed and Failed are terminal. Poll every 5–10 seconds — a tighter loop will not make the job finish sooner, and extraction takes on the order of 90 seconds per page.
Give the loop a hard iteration cap so a stuck job cannot spin indefinitely.
Step 4 — Show progress while you wait
If a person is watching, processingStage is more useful than the raw status:
Step 5 — Read the result
Once extraction.status is Processed, the payload is under extraction.result and has the same shape the synchronous endpoint returns:
The asynchronous record carries more than the synchronous response does — every parameter the job ran with (model, enableCaching, processorId), plus creditReservation, webhookStatus and fraudResults. See Reading fraud and control checks for the last of those.
When it goes wrong
Loop never exits, status always success
Reading the top-level status
Read extraction.status
404 right after submitting
Polled before the record was visible
Wait a second and retry; treat an early 404 as transient only here
Stuck on Processing
Long or complex document
Cap your attempts and alert; check processingStage.message
status: Failed
The document could not be processed
Inspect result.documentQuality; do not blindly resubmit
Stop polling altogether
Polling is the simple option, not the efficient one. If you control a public HTTPS endpoint, an event-driven pipeline removes the loop entirely — the platform posts to you when the job lands.
Related pages
Extraction API —
POST /extractandGET /document-extractions/{extraction_id}.Conventions — the envelope differences and the status lifecycle.
Build an event-driven pipeline — the push alternative.
Extract many documents at once — the same pattern for a batch.
Last updated
Was this helpful?

