Skip to content
RagextractAPIStart freeSign-up and sign-in are temporarily unavailable — please check back shortly.

Runs

A run is the act of filling cells in, and it is the thing that spends credits. Price it first — the preview is free and uses the same arithmetic the charge does.

Scoping a run: the two axes union

rowIds and columnIds are both optional. Omit both to run the whole table. Given together they union: every cell of those rows, plus every cell of those columns.

Request
# the whole table
-d '{}'

# every cell of three rows
-d '{"rowIds": ["rrow_A…", "rrow_B…", "rrow_C…"]}'

# every cell of one column
-d '{"columnIds": ["rcol_D…"]}'

# BOTH LINES WHOLE — not the nine cells where they cross
-d '{"rowIds": ["rrow_A…", "rrow_B…", "rrow_C…"],
     "columnIds": ["rcol_D…", "rcol_E…", "rcol_F…"]}'

They do not intersect. Three rows and three columns is three whole rows and three whole columns, not the nine cells where they meet. Reading it the other way is the one mistake on this page that costs money — and it is a mistake a preview catches for free.

What a run skips

Not everything in scope runs, and nothing that is skipped is charged.

  • A row whose document is still processing, or whose bundle is empty. Reported by id in skippedRowIds — poll the job, then run again.
  • A cell that already carries a human override. A rerun never clobbers a correction. These are not in skippedRowIds: skipping there is row-granular, and a partly-corrected row runs its other cells perfectly well.

If everything in scope is skipped, the call still succeeds — with run: null and cells: 0. That is not an error. It means the work you asked for is already done or not yet possible.

Two ways a run is refused

Both are 402, both happen before any work, and the order matters:

  1. The monthly spend cap first. If the run would pass a cap the organisation set, it is refused — and the body carries cap. Buying credits does not help; the cap is a self-imposed ceiling, raised in the app.
  2. Then the balance. The body carries cost and balance.

Checking the cap first is deliberate: telling someone to buy credits when the problem is a ceiling they set themselves is the wrong answer to the right error.

When credits actually move

Nothing is charged when a run starts. The extraction engine debits per cell, after that cell has succeeded. Consequences worth building against:

  • A cell that fails is not charged. A cell that is cancelled before it runs is not charged.
  • There is no refund path, because there is nothing to refund — cancellation is not a reversal.
  • The preview's cost is an upper bound, not an invoice.

Run statuses

NOT_STARTED, IN_QUEUE, IN_PROGRESS, SUCCESS, ERROR, CANCELED. Watch pendingCells against totalCells for progress; a cancelled run has pendingCells: 0 and a finishedAt, so a progress bar built on those two never sticks.

Price a run

GET/v2/workspaces/:workspaceId/tables/:tableId/runs/preview

Requires Read on the workspace

Free, and it charges nothing. It exists so a caller can see the price before committing, which is why it needs only read access — gating it at write level would defeat it.

The arithmetic is the same code the run itself uses, so the preview and the charge cannot disagree about scope, skips or the bundle surcharge. cost is credits, and it is an upper bound — cells that fail are not billed.

The scope parameters are comma-separated strings here rather than JSON arrays, because this is a GET.

Query parameters

rowIdsstringComma-separated row ids. Omit both for the whole table.
columnIdsstringComma-separated column ids. Unions with rowIds — see above.
Request
curl "https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/tables/rev_Qm5xC9bV3nK7sAeR/runs/preview?columnIds=rcol_D9sW4kF7nJ2xB6mV" \
  -H "x-api-key: $RAGEXTRACT_API_KEY"
Response
{
  "success": true,
  "total": 1,
  "data": {
    "cost": 184,
    "cells": 42,
    "skippedRowIds": ["rrow_T1yB7nK4mQ8xV2sD"]
  }
}

Start a run

POST/v2/workspaces/:workspaceId/tables/:tableId/runs

Requires Read & write on the workspace

Queues the cells and returns immediately — the answers arrive asynchronously. Poll the run for progress, then read the cells when it lands.

Spends credits, per cell as each succeeds.

run is null when nothing was runnable — a success, not a failure.

Parameters

rowIdsstring[]Up to 500 row ids. Omit both fields to run the whole table.
columnIdsstring[]Up to 100 column ids. Unions with rowIds.
Request
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/tables/rev_Qm5xC9bV3nK7sAeR/runs \
  -H "x-api-key: $RAGEXTRACT_API_KEY" \
  -H "content-type: application/json" \
  -d '{"columnIds": ["rcol_D9sW4kF7nJ2xB6mV"]}'
Response
{
  "success": true,
  "total": 1,
  "data": {
    "run": {
      "id": "rrun_X8dF2sB6nM4kQ9pL",
      "tableId": "rev_Qm5xC9bV3nK7sAeR",
      "status": "IN_PROGRESS",
      "triggeredBy": "ukey_N5tR8mW3xC7bV1jH",
      "totalCells": 42,
      "pendingCells": 17,
      "startedAt": 1756281600000,
      "finishedAt": null,
      "createdAt": 1756281600000,
      "updatedAt": 1756281800000
    },
    "cells": 42,
    "skippedRowIds": ["rrow_T1yB7nK4mQ8xV2sD"]
  }
}

List runs

GET/v2/workspaces/:workspaceId/tables/:tableId/runs

Requires Read on the workspace

Every run this table has had. triggeredBy tells them apart: an API key id (ukey_… or wkskey_…), a user id for a click in the app, or standing for the auto-mode sweep.

A table can have several runs in flight at once — each rerun is its own, and a standing table's sweep starts its own alongside whatever a person started.

Request
curl https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/tables/rev_Qm5xC9bV3nK7sAeR/runs \
  -H "x-api-key: $RAGEXTRACT_API_KEY"
Response
{
  "success": true,
  "total": 1,
  "data": [  {
    "id": "rrun_X8dF2sB6nM4kQ9pL",
    "tableId": "rev_Qm5xC9bV3nK7sAeR",
    "status": "IN_PROGRESS",
    "triggeredBy": "ukey_N5tR8mW3xC7bV1jH",
    "totalCells": 42,
    "pendingCells": 17,
    "startedAt": 1756281600000,
    "finishedAt": null,
    "createdAt": 1756281600000,
    "updatedAt": 1756281800000
  }]
}

Get a run

GET/v2/workspaces/:workspaceId/tables/:tableId/runs/:runId

Requires Read on the workspace

The endpoint to poll. One small response carrying totalCells and pendingCells — far cheaper than refetching the grid, which is what a naive progress loop reaches for.

Request
curl https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/tables/rev_Qm5xC9bV3nK7sAeR/runs/rrun_X8dF2sB6nM4kQ9pL \
  -H "x-api-key: $RAGEXTRACT_API_KEY"
Response
{
  "success": true,
  "total": 1,
  "data": {
  "id": "rrun_X8dF2sB6nM4kQ9pL",
  "tableId": "rev_Qm5xC9bV3nK7sAeR",
  "status": "IN_PROGRESS",
  "triggeredBy": "ukey_N5tR8mW3xC7bV1jH",
  "totalCells": 42,
  "pendingCells": 17,
  "startedAt": 1756281600000,
  "finishedAt": null,
  "createdAt": 1756281600000,
  "updatedAt": 1756281800000
}
}

Cancel a run

POST/v2/workspaces/:workspaceId/tables/:tableId/runs/:runId/cancel

Requires Read & write on the workspace

POST, not DELETEDELETE on a table resource in v2 means archive, and a run is history the credit reconciliation reads. Nothing is removed.

Cancellation is cooperative: the engine re-reads the run's status once per cell and skips the cell when it reads CANCELED. A cell already being extracted finishes and is charged; everything behind it goes back to blank, not to error.

An already-finished run answers 409, not a silent 200. A cancel is a race by nature, and answering "done" to a request that did nothing hides the one fact you wanted.

Cancelling needs the same level as starting, and stops runs other people started — including the auto-mode sweep. A run is workspace-shared state, and stopping one is the strictly safer half: it can only ever reduce spend.

Request
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/tables/rev_Qm5xC9bV3nK7sAeR/runs/rrun_X8dF2sB6nM4kQ9pL/cancel \
  -H "x-api-key: $RAGEXTRACT_API_KEY"
Response
{
  "success": true,
  "total": 1,
  "data": {
  "id": "rrun_X8dF2sB6nM4kQ9pL",
  "tableId": "rev_Qm5xC9bV3nK7sAeR",
  "status": "CANCELED",
  "triggeredBy": "ukey_N5tR8mW3xC7bV1jH",
  "totalCells": 42,
  "pendingCells": 0,
  "startedAt": 1756281600000,
  "finishedAt": 1756281900000,
  "createdAt": 1756281600000,
  "updatedAt": 1756281800000
}
}

Cancel everything in flight

POST/v2/workspaces/:workspaceId/tables/:tableId/runs/cancel

Requires Read & write on the workspace

"Stop this table" as one request. It exists because a table can have several runs going at once, and a client should not have to hold a list of run ids a refresh would lose.

Idempotent. With nothing in flight it cancels nothing and reports canceled: 0 — no 409, unlike the run-scoped route above, because here you never named a specific run.

runs is every run it moved to CANCELED, newest first.

Request
curl -X POST https://api.ragextract.com/v2/workspaces/wks_7Kq2mB4nR8vXpL3d/tables/rev_Qm5xC9bV3nK7sAeR/runs/cancel \
  -H "x-api-key: $RAGEXTRACT_API_KEY"
Response
{
  "success": true,
  "total": 1,
  "data": {
    "canceled": 2,
    "runs": [ { … ]
  }
}