---
title: "Processing runs"
canonical_url: "https://www.nutrient.io/guides/dws-data-extraction/runs/"
md_url: "https://www.nutrient.io/guides/dws-data-extraction/runs.md"
last_updated: "2026-10-14T00:00:00.000Z"
description: "Read, list, download, and delete stored Data Extraction runs with the /extraction/runs endpoints, and learn how run status, assets, and retention work."
---

# Processing runs

A processing run is the stored record of one request to the parse, extract, or classify endpoint. It holds the input document, the instructions, the result, and what the request cost. Every stored run has a public ID and lives at the run endpoint:

```

GET https://api.nutrient.io/extraction/runs/{runId}

```

Use runs when a result has to outlive the request that produced it. A synchronous request returns its result in the response body, and that’s the last you see of it unless you stored the run. An [asynchronous request](https://www.nutrient.io/guides/dws-data-extraction/runs/async.md) returns nothing but a run, so polling the run and downloading its assets is the whole workflow.

## When a run is stored

The synchronous endpoints don’t store runs by default. Add `storeRun: true` to the instructions, and the response carries the run’s ID in the body as `runId` and in the `x-nutrient-run-id` response header. The asynchronous endpoints always store their runs, so they reject `storeRun: false`.

Runs you start from the [Data Extraction Studio](https://dashboard.nutrient.io/data-extraction-api/studio/) are stored too, and the Studio’s run history reads the same endpoint. Runs you submit with an API key appear there next to the Studio’s own, with their source shown as API.

## Run status

A run’s `status` tells you where it is in its life:

| Status     | Meaning                                                                                                  |
| ---------- | -------------------------------------------------------------------------------------------------------- |
| `queued`   | Accepted and stored, waiting for a worker to pick it up. Only asynchronous runs pass through this state. |
| `running`  | Being processed.                                                                                         |
| `done`     | Finished with a result. The run carries what it cost in `usage`.                                         |
| `error`    | Finished without a result. The run carries an `error` object.                                            |
| `canceled` | Canceled before it finished. Any result was discarded and nothing was charged.                           |

The API writes a synchronous run when the request completes, so it’s already `done` or `error` the first time you can read it. An asynchronous run starts as `queued` and moves through `running` to one of the three terminal states. While it’s `running`, it reports where it is in `progress`. Refer to the [run asynchronously](https://www.nutrient.io/guides/dws-data-extraction/runs/async.md) guide for what each terminal state means for billing.

## Read a run

Fetch a run by its ID:

### curl

```shell

curl https://api.nutrient.io/extraction/runs/7KPSBBDRPEZ510NKVN0E42VHBH \
  -H "Authorization: Bearer your_api_key_goes_here"

```

### Python

```python

import requests

run_id = "7KPSBBDRPEZ510NKVN0E42VHBH"
response = requests.get(
    f"https://api.nutrient.io/extraction/runs/{run_id}",
    headers={"Authorization": "Bearer your_api_key_goes_here"},
)

run = response.json()
print(run["status"])

```

### JavaScript

```javascript

const runId = "7KPSBBDRPEZ510NKVN0E42VHBH";
const response = await fetch(`https://api.nutrient.io/extraction/runs/${runId}`, {
  headers: { Authorization: "Bearer your_api_key_goes_here" },
});

const run = await response.json();
console.log(run.status);

```

The response describes the run and lists the files you can download from it:

```json

{
  "id": "7KPSBBDRPEZ510NKVN0E42VHBH",
  "operation": "extraction_parse",
  "surface": "api",
  "status": "done",
  "label": null,
  "fileName": "invoice.pdf",
  "createdAt": "2026-09-23T21:00:34Z",
  "expiresAt": "2026-10-23T21:00:36Z",
  "instructions": { "mode": "structure" },
  "metadata": { "fileName": "invoice.pdf" },
  "metrics": { "pagesProcessed": 2, "processingTimeMs": 115 },
  "usage": { "data_extraction_credits": { "cost": 3.0 } },
  "assets": [
    {
      "direction": "input",
      "key": "input",
      "contentType": "application/pdf",
      "byteSize": 13264,
      "downloadUrl": "/extraction/runs/7KPSBBDRPEZ510NKVN0E42VHBH/assets/input"
    },
    {
      "direction": "output",
      "key": "result",
      "contentType": "application/json",
      "byteSize": 941,
      "downloadUrl": "/extraction/runs/7KPSBBDRPEZ510NKVN0E42VHBH/assets/result"
    }
  ],
  "error": null,
  "shared": false,
  "shareUrl": null,
  "evaluation": null
}

```

- `operation` — Which endpoint produced the run: `extraction_parse`, `extraction_extract`, `extraction_classify`, `extraction_form`, or `extraction_split`.

- `surface` — Where the run came from: `api` for requests with your API key, `studio` for runs started in the Studio, and `evaluation` for runs an evaluation set produced.

- `instructions` — The instructions the request was processed with, so you can tell later how a result was produced.

- `metrics` — Pages processed and processing time, once the run is done.

- `usage` — The credits the run was charged, under the same `data_extraction_credits.cost` key the synchronous response uses. It’s empty until the run is `done`, and it stays empty for runs that failed or were canceled, since those cost nothing.

- `assets` — The files stored with the run. Refer to the next section.

- `error` — Present on failed runs. It carries the HTTP `status` the request would have returned, the `requestId`, and `details.source`. For failures you can act on, it also carries `details.code`.

- `progress` — Present only while the run is `running`: the worker’s last report, with `phase`, `percent`, `pagesTotal`, `pagesStarted`, `pagesCompleted`, and `updatedAt`. The phases are `preparing`, `processing`, and `storing`. More will be added, so treat a phase you don’t know as the run still being in progress. The outcome is always `status`, never this field.

- `shared` and `shareUrl` — Whether the run has an active public share link from the Studio, and the link itself.

- `evaluation` — For a run an evaluation set produced, the set, evaluation run, and item it belongs to. It’s `null` for every other run.

- `expiresAt` — When the run leaves your history, or `null` on plans that keep runs indefinitely. Refer to the retention section below.

## Download assets

Each entry in `assets` is a file you can download from the run. The `key` is the last path segment of the asset endpoint, and the response `Content-Type` matches the stored file:

```shell

curl https://api.nutrient.io/extraction/runs/7KPSBBDRPEZ510NKVN0E42VHBH/assets/result \
  -H "Authorization: Bearer your_api_key_goes_here" \
  -o result.json

```

Every run carries its `input`, the document as it was uploaded or downloaded from the URL you sent. A `done` run adds `result`, a JSON file holding exactly the body the synchronous endpoint would have returned, with `output`, `metrics`, `usage`, and `configuration`. A failed run may also carry `result`, holding the error body instead.

A run that’s still `queued` or `running` lists only its input. Asking for its `result` returns `404` with the code `run_asset_not_found` until the run is done.

## List runs

List your stored runs, newest first, with `GET /extraction/runs`:

```shell

curl "https://api.nutrient.io/extraction/runs?operation=extraction_parse&status=done&page_size=20" \
  -H "Authorization: Bearer your_api_key_goes_here"

```

Filter the list with these query parameters. Each filter accepts a single value, comma-separated values, or the parameter repeated.

| Parameter   | Description                                                                                                                         |
| ----------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `operation` | Limit to one or more operations, such as `extraction_parse`.                                                                        |
| `surface`   | Limit to `api`, `studio`, or `evaluation` runs.                                                                                     |
| `status`    | Limit to one or more of the statuses above. Without it, the list includes in-flight and canceled runs next to finished ones.        |
| `ui_status` | A coarser filter: `processed` for done runs and `error` for failed ones. Queued, running, and canceled runs fall in neither bucket. |
| `mode`      | Limit to one or more parse modes. Matches `instructions.mode` on parse runs and `instructions.parseConfig.mode` on extract runs.    |
| `query`     | Search by file name, label, or run ID.                                                                                              |
| `order`     | Sort order. Defaults to `created_at_desc`.                                                                                          |
| `cursor`    | The opaque cursor from a previous page’s `pagination`, to fetch the next page. Keep the same filters and order when you pass it.    |
| `page_size` | Runs per page, up to 100. Defaults to 50.                                                                                           |

Each item in the list carries the same fields as the run detail, except `assets` and `error`. Fetch a single run when you need those.

## Delete a run

Delete a run to remove it from your history and, on plans that don’t retain data, to purge its stored files:

```shell

curl -X DELETE https://api.nutrient.io/extraction/runs/7KPSBBDRPEZ510NKVN0E42VHBH \
  -H "Authorization: Bearer your_api_key_goes_here"

```

A successful delete answers `204`, and deleting a run that’s already gone answers `404`. A run that’s still `queued` or `running` can’t be deleted, so the request answers `409` with the code `run_in_flight` until you cancel the run. Refer to the [run asynchronously](https://www.nutrient.io/guides/dws-data-extraction/runs/async.md) guide for canceling.

## Retention

How long a run stays readable depends on your plan. The run’s `expiresAt` tells you the exact time; on plans that keep runs indefinitely, it’s `null`. Once a run expires, reading it or any of its assets answers `410` with the code `run_expired`. The API removes the run and its files shortly after.

The retention window starts when the run settles, not when you submitted it. An asynchronous run never expires while it’s `queued` or `running`, and its window starts fresh when it reaches `done`, `error`, or `canceled`. A long wait therefore never eats into the time you have to read the result.

For what Nutrient keeps beyond your run history and how to opt out, refer to the [privacy](https://www.nutrient.io/guides/dws-data-extraction/privacy.md) guide.

## Next steps

- Refer to the [run asynchronously](https://www.nutrient.io/guides/dws-data-extraction/runs/async.md) guide to submit long documents without holding a connection open, cancel runs, and retry safely.

- Refer to the [error handling](https://www.nutrient.io/guides/dws-data-extraction/errors.md) guide for the run endpoint status codes.

- Refer to the [API reference](https://www.nutrient.io/api/reference/data-extraction/public/) for the full run schema and every list parameter.
---

## Related pages

- [Run asynchronously](/guides/dws-data-extraction/runs/async.md)

