Jobs
Ingest is asynchronous. Uploading a document returns a job, and the document is not queryable until that job finishes. This is how you watch it.
Statuses
| status | App label | Means |
|---|---|---|
NOT_STARTED | Pending | Uploaded, not yet picked up. Also what a null status means. |
IN_QUEUE | Queued | Waiting for a free slot. The organisation's tier caps how many jobs run at once — 2 at Starter, 16 at Gold — and work over that cap waits. It is never refused: a tier governs how fast work drains, not whether it happens. |
IN_PROGRESS | Processing | Being read, split and indexed. Credits are spent here, per page. |
SUCCESS | Ready | Done. The file is queryable, and its pages exist. |
ERROR | Failed | Something went wrong — or the job was cancelled. statusText carries which; see below. |
Uploading documents is the same vocabulary in customer language. Two things to build against: status can be null — the underlying column permits it, and an absent status means NOT_STARTED, never "finished" — and the two terminal states are SUCCESS and ERROR. There is no CANCELED status on an ingest job.
These three responses say datasetId
Everywhere else in v2, an uploaded document is a file and its id field is fileId. The three endpoints on this page are the exception: they answer datasetId, because they are the v1 handlers mounted unchanged. The value is the same ds_… id.
POST /files and POST /files/upload_session/end return a job through a different handler and do say fileId. So a client that uploads and then polls has to read both spellings for the same field.
List jobs
GET/v2/workspaces/:workspaceId/jobs
Requires Read on the workspace
Jobs in the workspace. The response echoes the statuses filter it applied, which is worth reading back if you are building a dashboard.
Filtering to the three unfinished statuses is the cheap way to answer "is anything still ingesting" without holding a list of job ids.
Query parameters
statuses | stringComma-separated statuses to include. Omit for all. |
|---|---|
offset | numberRows to skip. Default 0. |
limit | numberRows to return. Default 10, clamped to 100. |
curl "https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/jobs?statuses=NOT_STARTED,IN_QUEUE,IN_PROGRESS" \
-H "x-api-key: $RAGEXTRACT_API_KEY"{
"success": true,
"total": 1,
"data": [ {
"id": "dsj_L8vB2mQ6xR3kW9pZ",
"datasetId": "ds_M3xJ8pQ1vK5nB7wT",
"type": "extract",
"status": "IN_PROGRESS",
"statusText": null,
"startedAt": 1756108830000,
"finishedAt": null,
"canceledAt": null,
"createdAt": 1756108800000,
"updatedAt": 1756108830000
}],
"offset": 0,
"limit": 10,
"statuses": ["NOT_STARTED", "IN_QUEUE", "IN_PROGRESS"]
}Get a job
GET/v2/workspaces/:workspaceId/jobs/:jobId
Requires Read on the workspace
One job. This is what you poll after an upload. There is no webhook and no long-poll — poll on a back-off, and remember the read limit is 100 requests a minute per key, which is generous for one upload and not for a thousand.
A large document can sit at IN_QUEUE for a while on a low tier without anything being wrong.
curl https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/jobs/dsj_L8vB2mQ6xR3kW9pZ \
-H "x-api-key: $RAGEXTRACT_API_KEY"{
"success": true,
"total": 1,
"data": {
"id": "dsj_L8vB2mQ6xR3kW9pZ",
"datasetId": "ds_M3xJ8pQ1vK5nB7wT",
"type": "extract",
"status": "IN_PROGRESS",
"statusText": null,
"startedAt": 1756108830000,
"finishedAt": null,
"canceledAt": null,
"createdAt": 1756108800000,
"updatedAt": 1756108830000
}
}Cancel a job
POST/v2/workspaces/:workspaceId/jobs/:jobId/cancel
Requires Read & write on the workspace
POST, not DELETE. v1 overloaded DELETE for this, which reads as destroying the record — the job row survives cancellation and is exactly what you poll afterwards, so v2 gave it its own verb.
A cancelled job comes back as ERROR with statusText: "Canceled", and finishedAt — not canceledAt, which stays null. There is no separate cancelled state, so a client distinguishing "failed" from "stopped on purpose" has to read statusText.
Pages already read have already been charged. Cancelling stops further work; it is not a refund.
An unknown job id answers 400 here rather than 404.
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/jobs/dsj_L8vB2mQ6xR3kW9pZ/cancel \
-H "x-api-key: $RAGEXTRACT_API_KEY"{
"success": true,
"total": 1,
"data": {
"id": "dsj_L8vB2mQ6xR3kW9pZ",
"datasetId": "ds_M3xJ8pQ1vK5nB7wT",
"type": "extract",
"status": "ERROR",
"statusText": "Canceled",
"startedAt": 1756108830000,
"finishedAt": 1756108900000,
"canceledAt": null,
"createdAt": 1756108800000,
"updatedAt": 1756108900000
}
}