---
title: "Read an extraction's progress"
summary: "One frame of a job's state without its Markdown, for a progress bar that should not carry a page's text with every poll."
category: "developer"
tags: ["kerf", "reference", "progress", "polling"]
updated: "2026-09-14T04:25:19.230Z"
url: "https://support.quorumtech.ch/articles/kerf-extraction-progress"
markdown: "https://support.quorumtech.ch/articles/kerf-extraction-progress.md"
---

# Read an extraction's progress

`GET https://api.quorumtech.ch/kerf/v1/jobs/{jobId}/progress`

Read an extraction's progress without collecting it.

The job without its Markdown: state, how long it queued, how long the fetch has taken and
which edge is doing it. For a progress bar that should not carry a page's text with every
poll. Free.

The job as it stands, never its `result`. Use it where the page's text would be wasted bandwidth: a progress bar, a queue view, a health check on a long fetch.

`queuedMs` is how long the job waited for an edge to take it, and `renderMs` stays null until it finishes. A job that has queued for a long time means the fleet is busy rather than the page being slow.

## Request

Path parameters:

| Parameter | Description |
| --- | --- |
| `jobId` | The `id` from the answer to `POST /kerf/v1/extract/url`, for example `6f1ec0d2-...`. |

## Example request

```bash
curl -X GET "https://api.quorumtech.ch/kerf/v1/jobs/<jobId>/progress" \
  -H "Authorization: Bearer $QUORUM_API_KEY"
```

## Responses

### 200

The job as it stands, without `result`.

| Field | Type | Description |
| --- | --- | --- |
| `id` | string |  |
| `state` | `queued` or `running` |  |
| `url` | string |  |
| `queuedMs` | integer | Milliseconds the job waited before an edge took it. |
| `renderMs` | integer | Null until the job finishes. May be null. |
| `edge` | string | Which edge is fetching it. May be null. |
| `attempts` | integer |  |

## Errors

The body is `{ "error": { "code", "message" } }`; the full catalogue with what to do about each code is on the Errors page.

| Status | Codes | When |
| --- | --- | --- |
| 401 |  | The key is missing, not recognised, or revoked. |
| 403 | `insufficient_scope`, `url_not_allowed`, `robots_disallowed` | The key is not scoped to Kerf (`insufficient_scope`), the address is one this service does not fetch (`url_not_allowed`: a private or loopback address, or a redirect to one), or the site's robots.txt disallows the page and you asked for it to be followed (`robots_disallowed`). |
| 404 | `job_not_found` | No extraction with that id (`job_not_found`). Either it was never started here, it has been forgotten, or it belongs to another workspace, which reads the same way on purpose. |
| 429 | `rate_limited`, `claims_rate_limited`, `claim_budget_exhausted`, `monthly_cap_reached`, `cap` | Over the key's requests-per-minute limit (`rate_limited`), its claims-per-minute ceiling (`claims_rate_limited`), its claims-per-day budget (`claim_budget_exhausted`), or, for a postpaid workspace, its monthly credit cap (`monthly_cap_reached`, with `cap` and `resetsAt`). `Retry-After` says when to try again; the daily budget resets at midnight UTC and the cap on the first of the month. |

## Cost

Free.
