# Batches API Reference — Start, Export & Delete

> Start extraction on a batch of uploaded documents, list and filter your batches, poll aggregate status, fetch reshaped JSON results, export to Excel/Word, and delete batches for the ImageToTable.ai v1 API.

A **Batch** is a named group of one or more [Documents](/developers/reference/documents) that you process, poll, and fetch results for together. You don't create a batch explicitly — it's created implicitly the first time you upload a document with that `batch_name` (see [Upload a document](/developers/reference/documents#upload-a-document)).

## Start processing a batch

Starts extraction on every eligible document currently in the batch (anything not already `processing` or `succeeded`). This is the endpoint that actually spends credits — one per document queued.

`POST /api/v1/batches/{batch_name}/process`

### Parameters

| Name | Location | Type | Description |
| --- | --- | --- | --- |
| batch_name | path | string | The batch to process. |
| template_id | body (JSON) | integer, optional | A saved Template to apply. Takes priority over fields if both are given. |
| fields | body (JSON) | array, optional | Ad-hoc field list for this run only — [{"name": "...", "format_requirement": "..."}] or a plain array of name strings. Ignored if template_id is given. Omit both to let the model infer columns on its own. |
| quality | body (JSON) | string, optional | "fast" or "high" . Omit to fall back to your account's thinking_type setting — see Account Settings & API Behavior . |
| webhook_url | body (JSON) | string, optional | Registers (or updates) this batch's completion callback in the same call — equivalent to also calling Register a batch webhook . Must be http:// or https:// . |
| Idempotency-Key | header, optional | string | Strongly recommended — this endpoint deducts credits. See Idempotency . |

`quality` is the one account setting the API lets you override per call — every other account-level preference (bbox auto-annotation, retention policy) is read from your account and can't be overridden per request. See [Account Settings & API Behavior](/developers/guides/account-settings) for the full picture, including why `auto_annotate_bbox` can mean this call ends up billing you for bbox annotation too, even though you never called that endpoint.

**The response's `webhook_registered` reflects only this specific call** — it's `true` iff *this* request included `webhook_url`, not whether the batch has a webhook at all. A batch whose webhook was set up earlier via [Register a batch webhook](/developers/reference/webhooks) (and not repeated here) will correctly fire its callback on completion, but this field still comes back `false` for that call — it does not look up whether a `BatchWebhook` already exists. Don't treat a `false` here as "no webhook will fire for this batch."

Calling this endpoint again on a batch you've already processed and been notified about — after uploading more documents into it — automatically re-arms that batch's webhook if it had already fired, so the new wave's completion notifies too. No extra call is needed to make this happen; see the [Webhooks guide](/developers/guides/webhooks#reprocessing-a-batch)'s "Reprocessing a batch" section for the exact semantics (including what happens if two waves overlap).

### Possible errors

- `missing_api_key` / `invalid_api_key` / `plan_required` — see [Error Handling](/developers/guides/errors).
- `batch_not_found` — no documents exist under this `batch_name` on your account.
- `invalid_parameter` — bad `quality`/`webhook_url`/`template_id` value, or no documents in the batch are currently eligible for processing (all already completed/processing, or the batch is empty).
- `template_not_found`
- `insufficient_credits` — not enough available credits to cover the documents being queued.

## List batches

Returns a paginated, filterable summary list of your batches — the "Files Filter" equivalent for API consumers. This returns summaries only (`document_count`, aggregate `status`); use [Get batch results](#get-batch-results) for the full per-document data of a specific batch.

`GET /api/v1/batches`

### Parameters

| Name | Location | Type | Description |
| --- | --- | --- | --- |
| source | query, optional | string | One of direct , collect , email_inbox , api (uploaded via POST /documents , distinct from direct which is the main web app), or share (an alias covering both collect and email_inbox ). Omit for all sources. |
| q | query, optional | string | Case-insensitive substring match against filenames within the batch. |
| date_from | query, optional | string ( YYYY-MM-DD ) | Inclusive lower bound on upload time. |
| date_to | query, optional | string ( YYYY-MM-DD ) | Inclusive upper bound on upload time (end of day). |
| status | query, optional | string | Comma-separated list of public statuses ( queued,processing,succeeded,failed,canceled ) to filter by. |
| template_id | query, optional | integer | Only batches that used this template. |
| mode | query, optional | string | Only accepted value this round is "table" (the only mode v1 currently supports). Reserved for future extraction modes. |
| limit | query, optional | integer | 1–100. Default 20. |
| page_token | query, optional | string | Opaque cursor from a previous response's next_page_token . See Pagination . |

### Possible errors

- `missing_api_key` / `invalid_api_key` / `plan_required` — see [Error Handling](/developers/guides/errors).
- `invalid_parameter` — bad `mode`, `limit`, `status`, or `page_token`.

## Get batch status

Lightweight aggregate status for one batch — counts per public status plus an `all_done` flag, useful for a cheap polling loop that doesn't need the full result payload yet.

`GET /api/v1/batches/{batch_name}`

### Parameters

| Name | Location | Type | Description |
| --- | --- | --- | --- |
| batch_name | path | string | The batch to check. |

### Possible errors

- `missing_api_key` / `invalid_api_key` / `plan_required` — see [Error Handling](/developers/guides/errors).
- `batch_not_found`

## Get batch results

The primary way to retrieve extracted data. Every document in the batch is returned with its reshaped `line_items` — an array of `{field_name: value}` objects, one per extracted row. Field-level values are bare scalars by default (a string, number, etc.).

`GET /api/v1/batches/{batch_name}/results`

### Parameters

| Name | Location | Type | Description |
| --- | --- | --- | --- |
| batch_name | path | string | The batch to fetch results for. |
| include | query, optional | string | "bbox" — when set, every field value becomes {"value": ..., "bbox": {...}|null} instead of a bare scalar, and each document gets a bbox_status field. See below — this never triggers a new bbox job, it only returns whatever has already been computed. |

`?include=bbox` reads back-filled results only — it does not call [Trigger bbox annotation](/developers/reference/documents#trigger-bbox-annotation) for you. If bbox was never triggered for a document (manually, or via your account's `auto_annotate_bbox` setting), that document's fields simply come back with `"bbox": null`.

**Pure-location fields are a third, distinct shape.** Some template fields ask the model to locate something rather than transcribe text (e.g. "locate the portrait photo"). For those fields, the entire value *is* a location — so instead of a scalar or the `{"value","bbox"}` pair above, you get `{"type": "image_region", "bbox": {...}, "image_url": "..."}`, **regardless of whether `?include=bbox` was set** — the box isn't optional metadata here, it's the field's only content. `image_url` points at a ready-to-fetch cropped JPEG (see [Get a document's image](/developers/reference/documents#get-a-document-image)) so you don't have to crop the original yourself from four numbers.

Every `bbox` object — in either shape — uses `"unit": "normalized"`: coordinates are floats from 0 to 1 relative to the page's width/height, not pixels and not a 0–1000 scale. See the [Bounding Boxes](/developers/guides/bbox) guide for the accuracy caveat — these coordinates come straight from the model with no pixel-level verification pass, so treat them as "close" rather than exact on dense or complex documents.

### Possible errors

- `missing_api_key` / `invalid_api_key` / `plan_required` — see [Error Handling](/developers/guides/errors).
- `batch_not_found`

## Export a batch

A convenience download — `results` above is the canonical, structured format this API is built around; this endpoint exists for pulling the same data into a spreadsheet without writing any reshaping code yourself. xlsx only — v1 only supports table (extract) mode, and Word (docx) export on the main app is exclusively the page_word mode's native output (a different prompt and result shape entirely), which v1 doesn't expose. There's no "table data as a Word doc" option here.

`GET /api/v1/batches/{batch_name}/export`

### Parameters

| Name | Location | Type | Description |
| --- | --- | --- | --- |
| batch_name | path | string | The batch to export. |
| format | query, optional | string | Only xlsx (the default) is accepted. |

The response is a file download (`Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`), not JSON — there's no response JSON example for this endpoint.

### Possible errors

- `missing_api_key` / `invalid_api_key` / `plan_required` — see [Error Handling](/developers/guides/errors).
- `batch_not_found`
- `invalid_parameter` (`param: "format"`) — anything other than `xlsx`.

## Delete a batch

Permanently deletes every document in the batch. Any document still `queued` is refunded before deletion. This also removes the batch's webhook registration (if any) and any bbox-annotation jobs tied to its documents.

`DELETE /api/v1/batches/{batch_name}`

### Parameters

| Name | Location | Type | Description |
| --- | --- | --- | --- |
| batch_name | path | string | The batch to delete. |

### Possible errors

- `missing_api_key` / `invalid_api_key` / `plan_required` — see [Error Handling](/developers/guides/errors).
- `batch_not_found` — unlike some other resources, deleting a `batch_name` you don't own (or that doesn't exist) is a 404 here, not a silent no-op.

## Code Examples

### Start processing a batch

POST /api/v1/batches/{batch_name}/process

**cURL**

```bash
curl -X POST https://imagetotable.ai/api/v1/batches/july-invoices/process \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
        "fields": [
          {"name": "invoice_number"},
          {"name": "invoice_date", "format_requirement": "YYYY-MM-DD"},
          {"name": "total_amount"}
        ],
        "quality": "high",
        "webhook_url": "https://example.com/webhooks/imagetotable"
      }'
```

**Python**

```python
import os
import uuid
import requests

response = requests.post(
    "https://imagetotable.ai/api/v1/batches/july-invoices/process",
    headers={
        "Authorization": f"Bearer {os.environ['API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={
        "fields": [
            {"name": "invoice_number"},
            {"name": "invoice_date", "format_requirement": "YYYY-MM-DD"},
            {"name": "total_amount"},
        ],
        "quality": "high",
        "webhook_url": "https://example.com/webhooks/imagetotable",
    },
)
print(response.json())
```

**Javascript**

```javascript
const response = await fetch("https://imagetotable.ai/api/v1/batches/july-invoices/process", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    fields: [
      { name: "invoice_number" },
      { name: "invoice_date", format_requirement: "YYYY-MM-DD" },
      { name: "total_amount" },
    ],
    quality: "high",
    webhook_url: "https://example.com/webhooks/imagetotable",
  }),
});
console.log(await response.json());
```

### Response

```json
{
  "batch_name": "july-invoices",
  "queued": 3,
  "quality": "high",
  "webhook_registered": true
}
```

### Start processing — with a saved template

POST /api/v1/batches/{batch_name}/process

The other of the two ways to specify what to extract — `template_id` takes priority over `fields` if you send both, and is the more common choice once you're reusing the same column list across runs instead of declaring it ad-hoc every time.

**cURL**

```bash
curl -X POST https://imagetotable.ai/api/v1/batches/july-invoices/process \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"template_id": 42}'
```

**Python**

```python
import os
import uuid
import requests

response = requests.post(
    "https://imagetotable.ai/api/v1/batches/july-invoices/process",
    headers={
        "Authorization": f"Bearer {os.environ['API_KEY']}",
        "Idempotency-Key": str(uuid.uuid4()),
    },
    json={"template_id": 42},
)
print(response.json())
```

**Javascript**

```javascript
const response = await fetch("https://imagetotable.ai/api/v1/batches/july-invoices/process", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ template_id: 42 }),
});
console.log(await response.json());
```

### Response

```json
{
  "batch_name": "july-invoices",
  "queued": 3,
  "quality": "fast",
  "webhook_registered": false
}
```

`quality` here is `"fast"` because it was omitted from the request and fell back to the account's `thinking_type` setting, not because a template was used — `template_id`/`fields` and `quality` are independent parameters.

### List batches

GET /api/v1/batches

**cURL**

```bash
curl "https://imagetotable.ai/api/v1/batches?status=succeeded&limit=20" \
  -H "Authorization: Bearer $API_KEY"
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://imagetotable.ai/api/v1/batches",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    params={"status": "succeeded", "limit": 20},
)
print(response.json())
```

**Javascript**

```javascript
const url = new URL("https://imagetotable.ai/api/v1/batches");
url.searchParams.set("status", "succeeded");
url.searchParams.set("limit", "20");

const response = await fetch(url, {
  headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
});
console.log(await response.json());
```

### Response

```json
{
  "data": [
    {
      "batch_name": "july-invoices",
      "status": "succeeded",
      "document_count": 3,
      "created_at": "2026-07-16T09:12:03+00:00"
    }
  ],
  "has_more": false,
  "next_page_token": null
}
```

### Get batch status

GET /api/v1/batches/{batch_name}

**cURL**

```bash
curl https://imagetotable.ai/api/v1/batches/july-invoices \
  -H "Authorization: Bearer $API_KEY"
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://imagetotable.ai/api/v1/batches/july-invoices",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
)
print(response.json())
```

**Javascript**

```javascript
const response = await fetch("https://imagetotable.ai/api/v1/batches/july-invoices", {
  headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
});
console.log(await response.json());
```

### Response

```json
{
  "batch_name": "july-invoices",
  "document_count": 3,
  "status_counts": {
    "queued": 0,
    "processing": 0,
    "succeeded": 3,
    "failed": 0,
    "canceled": 0
  },
  "all_done": true,
  "created_at": "2026-07-16T09:12:03+00:00"
}
```

### Get batch results

GET /api/v1/batches/{batch_name}/results

**cURL**

```bash
curl https://imagetotable.ai/api/v1/batches/july-invoices/results \
  -H "Authorization: Bearer $API_KEY"
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://imagetotable.ai/api/v1/batches/july-invoices/results",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
)
print(response.json())
```

**Javascript**

```javascript
const response = await fetch("https://imagetotable.ai/api/v1/batches/july-invoices/results", {
  headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
});
console.log(await response.json());
```

### Response — default (no include)

```json
{
  "batch_name": "july-invoices",
  "status": "succeeded",
  "created_at": "2026-07-16T09:12:03+00:00",
  "started_at": "2026-07-16T09:12:05+00:00",
  "completed_at": "2026-07-16T09:12:14+00:00",
  "documents": [
    {
      "document_id": "8f14e45f-ceea-467e-9de1-a3e9c93a9c95",
      "filename": "invoice_042.jpg",
      "status": "succeeded",
      "created_at": "2026-07-16T09:12:03+00:00",
      "started_at": "2026-07-16T09:12:05+00:00",
      "completed_at": "2026-07-16T09:12:14+00:00",
      "line_items": [
        {
          "invoice_number": "INV-1042",
          "invoice_date": "2026-07-01",
          "total_amount": "1,204.50"
        }
      ]
    }
  ]
}
```

### Get batch results — with bbox

GET /api/v1/batches/{batch_name}/results?include=bbox

**cURL**

```bash
curl "https://imagetotable.ai/api/v1/batches/july-invoices/results?include=bbox" \
  -H "Authorization: Bearer $API_KEY"
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://imagetotable.ai/api/v1/batches/july-invoices/results",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    params={"include": "bbox"},
)
print(response.json())
```

**Javascript**

```javascript
const url = new URL("https://imagetotable.ai/api/v1/batches/july-invoices/results");
url.searchParams.set("include", "bbox");

const response = await fetch(url, {
  headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
});
console.log(await response.json());
```

### Response — `?include=bbox`

```json
{
  "batch_name": "july-invoices",
  "status": "succeeded",
  "created_at": "2026-07-16T09:12:03+00:00",
  "started_at": "2026-07-16T09:12:05+00:00",
  "completed_at": "2026-07-16T09:12:14+00:00",
  "documents": [
    {
      "document_id": "8f14e45f-ceea-467e-9de1-a3e9c93a9c95",
      "filename": "invoice_042.jpg",
      "status": "succeeded",
      "created_at": "2026-07-16T09:12:03+00:00",
      "started_at": "2026-07-16T09:12:05+00:00",
      "completed_at": "2026-07-16T09:12:14+00:00",
      "bbox_status": "succeeded",
      "line_items": [
        {
          "invoice_number": {
            "value": "INV-1042",
            "bbox": {"x1": 0.121, "y1": 0.084, "x2": 0.418, "y2": 0.112, "unit": "normalized"}
          },
          "total_amount": {
            "value": "1,204.50",
            "bbox": null
          },
          "portrait_photo": {
            "type": "image_region",
            "bbox": {"x1": 0.740, "y1": 0.060, "x2": 0.920, "y2": 0.260, "unit": "normalized"},
            "image_url": "https://imagetotable.ai/api/v1/documents/8f14e45f-ceea-467e-9de1-a3e9c93a9c95/image?crop=0.7241%2C0.0426%2C0.9359%2C0.2774"
          }
        }
      ]
    }
  ]
}
```

### Export a batch

GET /api/v1/batches/{batch_name}/export

**cURL**

```bash
curl "https://imagetotable.ai/api/v1/batches/july-invoices/export?format=xlsx" \
  -H "Authorization: Bearer $API_KEY" \
  -o july-invoices.xlsx
```

**Python**

```python
import os
import requests

response = requests.get(
    "https://imagetotable.ai/api/v1/batches/july-invoices/export",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    params={"format": "xlsx"},
)
with open("july-invoices.xlsx", "wb") as f:
    f.write(response.content)
```

**Javascript**

```javascript
import { writeFile } from "node:fs/promises";

const url = new URL("https://imagetotable.ai/api/v1/batches/july-invoices/export");
url.searchParams.set("format", "xlsx");

const response = await fetch(url, {
  headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
});
await writeFile("july-invoices.xlsx", Buffer.from(await response.arrayBuffer()));
```

### Delete a batch

DELETE /api/v1/batches/{batch_name}

**cURL**

```bash
curl -X DELETE https://imagetotable.ai/api/v1/batches/july-invoices \
  -H "Authorization: Bearer $API_KEY"
```

**Python**

```python
import os
import requests

response = requests.delete(
    "https://imagetotable.ai/api/v1/batches/july-invoices",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
)
print(response.json())
```

**Javascript**

```javascript
const response = await fetch("https://imagetotable.ai/api/v1/batches/july-invoices", {
  method: "DELETE",
  headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
});
console.log(await response.json());
```

### Response

```json
{
  "batch_name": "july-invoices",
  "deleted": 3,
  "canceled": 1
}
```

---

Source: https://imagetotable.ai/developers/reference/batches
