# API Quickstart — Get Results in 5 Minutes

> A five-minute, end-to-end walkthrough of the ImageToTable.ai v1 API: get your key, upload an invoice, start processing, and fetch the extracted JSON.

This walks through the whole loop — get a key, upload a document, start processing, get results back — using an invoice as the example. If you haven't installed `curl` or set up an environment variable for your key yet, see [Environment Setup](/developers/environment-setup) first.

## Step 1 — Get your API key

Your API key lives on your account's [profile settings page](/profile/), under API Key. The v1 API requires a paid plan (Basic or above) — Free-plan keys are accepted by the auth check but every call after that returns a `plan_required` error. Keys generated going forward look like `itt_live_<64 hex characters>`; export yours as an environment variable so it doesn't end up hardcoded in a script:

```bash
export API_KEY="itt_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
```

## Step 2 — Upload a document

`POST /api/v1/documents` takes either a `file` (multipart) or a `url` the server fetches for you — pass exactly one. `batch_name` is optional — omit it and the API generates one for you (returned in the response) and every document you want processed together should share the same `batch_name`. This example uses `url` against a real sample invoice hosted on our own site, so it's runnable exactly as written, with no local file needed:

**cURL**

```bash
curl -X POST https://imagetotable.ai/api/v1/documents \
  -H "Authorization: Bearer $API_KEY" \
  -F "url=https://imagetotable.ai/static/samples/invoice.webp"
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://imagetotable.ai/api/v1/documents",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    data={"url": "https://imagetotable.ai/static/samples/invoice.webp"},
)
print(response.json())
```

**Javascript**

```javascript
const formData = new FormData();
formData.append("url", "https://imagetotable.ai/static/samples/invoice.webp");

const response = await fetch("https://imagetotable.ai/api/v1/documents", {
  method: "POST",
  headers: { "Authorization": `Bearer ${process.env.API_KEY}` },
  body: formData,
});
console.log(await response.json());
```

Got a local file instead? Swap `url` for a multipart `file` field — see [Upload a document](/developers/reference/documents#upload-a-document) in the Reference for the full parameter list including `batch_name`, `template_id`, and `password`.

The response returns the new document's ID and the batch it landed in — save `batch_name`, you'll need it in the next two steps:

```json
{
  "document_id": "3f9c9e2a-1b7d-4e2b-9a3e-2f5b6c7d8e9f",
  "batch_name": "260716-4K9P",
  "remaining_batch_capacity": 199
}
```

Uploading a multi-page PDF instead of an image works the same way, except `document_id` comes back as an *array* — one document per page — since each page is processed independently.

## Step 3 — Start processing

`POST /api/v1/batches/{batch_name}/process` kicks off extraction. You can point it at a saved [Template](/developers/reference/templates-fields) via `template_id`, or — as here — declare the fields you want inline with `fields` for a one-off run:

**cURL**

```bash
curl -X POST https://imagetotable.ai/api/v1/batches/260716-4K9P/process \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
        "fields": [
          {"name": "invoice_number"},
          {"name": "invoice_date"},
          {"name": "vendor_name"},
          {"name": "total_amount"}
        ]
      }'
```

**Python**

```python
import os
import requests

response = requests.post(
    "https://imagetotable.ai/api/v1/batches/260716-4K9P/process",
    headers={"Authorization": f"Bearer {os.environ['API_KEY']}"},
    json={
        "fields": [
            {"name": "invoice_number"},
            {"name": "invoice_date"},
            {"name": "vendor_name"},
            {"name": "total_amount"},
        ]
    },
)
print(response.json())
```

**Javascript**

```javascript
const response = await fetch("https://imagetotable.ai/api/v1/batches/260716-4K9P/process", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    fields: [
      { name: "invoice_number" },
      { name: "invoice_date" },
      { name: "vendor_name" },
      { name: "total_amount" },
    ],
  }),
});
console.log(await response.json());
```

This deducts credits immediately (one document deducted per call) and enqueues the document(s) for background processing:

```json
{
  "batch_name": "260716-4K9P",
  "queued": 1,
  "quality": "fast",
  "webhook_registered": false
}
```

Leave `fields` and `template_id` out entirely and the API infers reasonable column names on its own instead. `quality` is optional too — omit it and processing falls back to your account's own speed/quality setting (see [Account Settings and API Behavior](/developers/guides/account-settings)). `webhook_registered: false` here just means this particular call didn't include `webhook_url` — it doesn't mean no webhook is registered for this batch; see the [Batches reference](/developers/reference/batches#start-processing-a-batch) if you're registering one separately via `PUT .../webhook`.

## Step 4 — Poll and get your results

Processing is asynchronous — poll `GET /api/v1/batches/{batch_name}` until `all_done` is `true` (a few seconds for a single document), or register a [webhook](/developers/guides/webhooks) instead of polling. The full lifecycle is covered in the [Async Task Model](/developers/guides/async-model) guide.

Once it's done, fetch the reshaped results:

**cURL**

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

**Python**

```python
import os
import requests

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

**Javascript**

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

Response JSON is never language-tabbed — the shape doesn't change based on which client sent the request:

```json
{
  "batch_name": "260716-4K9P",
  "status": "succeeded",
  "created_at": "2026-07-16T09:12:03Z",
  "started_at": "2026-07-16T09:12:05Z",
  "completed_at": "2026-07-16T09:12:14Z",
  "documents": [
    {
      "document_id": "3f9c9e2a-1b7d-4e2b-9a3e-2f5b6c7d8e9f",
      "filename": "invoice.jpg",
      "status": "succeeded",
      "created_at": "2026-07-16T09:12:03Z",
      "started_at": "2026-07-16T09:12:05Z",
      "completed_at": "2026-07-16T09:12:14Z",
      "line_items": [
        {
          "invoice_number": "INV-1042",
          "invoice_date": "2026-06-30",
          "vendor_name": "Acme Supply Co.",
          "total_amount": "1,284.50"
        }
      ]
    }
  ]
}
```

`completed_at` is set once a document (or, at the batch level, every document in the batch) reaches a terminal state — `succeeded`, `failed`, or `canceled`. It stays `null` while still `queued` or `processing`, and at the batch level it also stays `null` until *all* documents in the batch have finished, even if some of them finished earlier.

## Next steps

From here: read [Async Task Model](/developers/guides/async-model) for the full status lifecycle, [Webhooks](/developers/guides/webhooks) to get notified instead of polling, and [API Reference](/developers/reference/) for every endpoint's full parameter list.

---

Source: https://imagetotable.ai/developers/quickstart
