> For the complete documentation index, see [llms.txt](https://docs.uptiq.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.uptiq.ai/document-ai/cookbooks/async-extraction-and-polling.md).

# Extract asynchronously and poll

**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](/document-ai/developer-documentation/getting-started/quickstart-extract-a-document.md); only the URL and the retrieval change.

### Generated endpoint reference

Use [Extract document data asynchronously](/document-ai/developer-documentation/extraction-1/extract-document-data-async.md) to submit the job. Use [Get extraction details](/document-ai/developer-documentation/extraction-1/get-extraction-details.md) 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.

```bash
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.json
```

It returns in well under a second:

```json
{
  "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.

{% hint style="info" %}
This returns `200`, not the `202 Accepted` you might expect for queued work — `200` here means *accepted*, not *finished*, and `documentStatus` is `Pending`.

The same endpoint returns **`202`** when you send `documentTypes` for a multi-extraction instead of a single `documentType`. The code varies with the request, not just the endpoint, so accept any `2xx` rather than testing for one value.
{% endhint %}

### 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.

```bash
curl -s "https://api-docai-uat.uptiq.ai/document-extractions/3f2a91c4-7b58-4e12-9d63-0a5e8c1b74df" \
  -H "X-Api-Key: $DOCAI_API_KEY"
```

```json
{
  "extraction": {
    "_id": "3f2a91c4-7b58-4e12-9d63-0a5e8c1b74df",
    "status": "Processed",
    "documentType": "BalanceSheet",
    "processingStage": { "stage": "completed", "message": "Extraction complete" },
    "result": { }
  },
  "status": "success"
}
```

Compare that with what you submitted, because **two things changed**:

|                  | Submit response  | Retrieve response |
| ---------------- | ---------------- | ----------------- |
| Wrapper          | `data`           | `extraction`      |
| Job status field | `documentStatus` | `status`          |

{% hint style="danger" %}
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.
{% endhint %}

The field you want is `extraction.status`.

### Step 3 — Poll until terminal

```bash
ID=3f2a91c4-7b58-4e12-9d63-0a5e8c1b74df

for i in $(seq 1 40); do
  STATUS=$(curl -s "https://api-docai-uat.uptiq.ai/document-extractions/$ID" \
    -H "X-Api-Key: $DOCAI_API_KEY" | jq -r '.extraction.status')

  echo "attempt $i: $STATUS"
  case "$STATUS" in
    Processed|Failed) break ;;
  esac
  sleep 10
done
```

`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:

```json
{ "stage": "completed", "message": "Extraction complete", "updatedAt": "2026-07-29T03:23:15.820000" }
```

### 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:

```bash
jq '.extraction.result.extractedData' response.json
jq '.extraction.result.extractionMetrics.accuracyScore' response.json
```

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](/document-ai/cookbooks/fraud-and-control-checks.md) for the last of those.

### When it goes wrong

| Symptom                                   | Cause                                | Fix                                                                  |
| ----------------------------------------- | ------------------------------------ | -------------------------------------------------------------------- |
| 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](/document-ai/cookbooks/event-driven-pipeline.md) removes the loop entirely — the platform posts to you when the job lands.

### Related pages

* [Extraction API](/document-ai/developer-documentation/integration-guides/extraction.md) — `POST /extract` and `GET /document-extractions/{extraction_id}`.
* [Conventions](/document-ai/developer-documentation/getting-started/conventions.md) — the envelope differences and the status lifecycle.
* [Build an event-driven pipeline](/document-ai/cookbooks/event-driven-pipeline.md) — the push alternative.
* [Extract many documents at once](/document-ai/cookbooks/bulk-extraction.md) — the same pattern for a batch.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.uptiq.ai/document-ai/cookbooks/async-extraction-and-polling.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
