# Batch fill

Map a spreadsheet's columns to form fields once, then fill one PDF per row. Each row goes through deterministic standard fill. Each filled row bills one standard fill.

## Suggest a column mapping

```
POST /forms/{form_id}/suggest-mapping
```

Authentication: Bearer API key required.

Upload a spreadsheet and let Emboss suggest which column maps to which form field. Bills one context fill on success.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| form_id | path | string | yes | An existing form whose status is ready. |
| file | body | file (multipart) | yes | The spreadsheet (CSV or XLSX) whose header row names the columns to map. |

```curl
curl -X POST https://api.getemboss.ai/forms/6c47f7f5-f921-4698-910f-95dd7d81310b/suggest-mapping \
  -H "Authorization: Bearer sk_..." \
  -F "file=@./records.csv"
```

```js
const body = new FormData();
body.append("file", fileBlob, "records.csv");

const res = await fetch("https://api.getemboss.ai/forms/6c47f7f5-f921-4698-910f-95dd7d81310b/suggest-mapping", {
  method: "POST",
  headers: { Authorization: "Bearer sk_..." },
  body,
});
const { mapping, unmapped_columns } = await res.json();
```

```python
import requests

res = requests.post(
    "https://api.getemboss.ai/forms/6c47f7f5-f921-4698-910f-95dd7d81310b/suggest-mapping",
    headers={"Authorization": "Bearer sk_..."},
    files={"file": open("records.csv", "rb")},
)
data = res.json()  # {"mapping": {...}, "unmapped_columns": [...]}
```

### Response

```json
{
  "mapping": {
    "Phone": { "field_id": 0, "confidence": "high" },
    "Email": { "field_id": 1, "confidence": "high" }
  },
  "unmapped_columns": ["Notes"]
}
```

### Status codes

| Code | Meaning |
| --- | --- |
| 200 | Mapping suggestion returned. |
| 400 | Bad or unparseable spreadsheet. |
| 401 | Missing or invalid API key. |
| 402 | No payment method on file and either the monthly free allowance is used up or the request is over the free tier's 5-page cap. The body's detail carries code (over_free_tier or over_page_cap), message and billing_url, the page where a payment method can be added. |
| 404 | No such form, or owned by another key. |
| 409 | Form not ready. Detection has not finished. |
| 413 | Spreadsheet too large, or the request exceeds the size cap. |
| 429 | Rate limit exceeded. |
| 502 | Mapping failed upstream. |

## Start a batch fill

```
POST /forms/{form_id}/fill-batch
```

Authentication: Bearer API key required.

Upload the spreadsheet and a resolved mapping to start a batch fill. Returns 202 with a batch id. Each filled row bills one standard fill.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| form_id | path | string | yes | The form to fill, with status ready. |
| file | body | file (multipart) | yes | The spreadsheet of records. One row produces one output PDF. Max 1000 rows. |
| mapping | body | string (JSON) | yes | Resolved column-to-field mapping. Each key is a spreadsheet column name and each value contains a field_id from the form's contract. Example: {"Phone":{"field_id":0},"Email":{"field_id":1}}. |
| retention | body | string | no | Optional. ephemeral or account_default; default account_default. See the Ephemeral processing guide. |

```curl
curl -X POST https://api.getemboss.ai/forms/6c47f7f5-f921-4698-910f-95dd7d81310b/fill-batch \
  -H "Authorization: Bearer sk_..." \
  -F "file=@./records.csv" \
  -F 'mapping={"Phone":{"field_id":0},"Email":{"field_id":1}}'
```

```js
const mapping = JSON.stringify({ Phone: { field_id: 0 }, Email: { field_id: 1 } });

const body = new FormData();
body.append("file", fileBlob, "records.csv");
body.append("mapping", mapping);

const res = await fetch("https://api.getemboss.ai/forms/6c47f7f5-f921-4698-910f-95dd7d81310b/fill-batch", {
  method: "POST",
  headers: { Authorization: "Bearer sk_..." },
  body,
});
const { batch_id } = await res.json();
```

```python
import requests, json

mapping = json.dumps({"Phone": {"field_id": 0}, "Email": {"field_id": 1}})

res = requests.post(
    "https://api.getemboss.ai/forms/6c47f7f5-f921-4698-910f-95dd7d81310b/fill-batch",
    headers={"Authorization": "Bearer sk_..."},
    files={"file": open("records.csv", "rb")},
    data={"mapping": mapping},
)
data = res.json()  # {"batch_id": "<uuid>"}
```

### Response

```json
{
  "batch_id": "a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d"
}
```

### Status codes

| Code | Meaning |
| --- | --- |
| 202 | Batch started. |
| 400 | Bad spreadsheet, invalid mapping JSON, mapping references an unknown column or field, or too many rows (max 1000). |
| 401 | Missing or invalid API key. |
| 402 | No payment method on file, and the monthly free allowance is used up, the batch has more rows than the free fills left (the body then carries rows and remaining), or the form is over the free tier's 5-page cap. The body's detail carries code (over_free_tier or over_page_cap), message and billing_url, the page where a payment method can be added. |
| 404 | No such form, or owned by another key. |
| 409 | Form not ready or not yet fillable. |
| 410 | The form's documents have been deleted under its retention policy. |
| 413 | Spreadsheet too large, or the request exceeds the size cap. |
| 429 | Rate limit exceeded. |

## Get batch status

```
GET /forms/fill-batch/{batch_id}
```

Authentication: Bearer API key required.

Return the batch manifest. status is one of processing, complete, or failed. Poll until it is complete or failed. Each result entry links to its row PDF.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| batch_id | path | string | yes | The batch id returned by POST fill-batch. |

```curl
curl https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d \
  -H "Authorization: Bearer sk_..."
```

```js
const res = await fetch("https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", {
  headers: { Authorization: "Bearer sk_..." },
});
const batch = await res.json();
```

```python
import requests

res = requests.get(
    "https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    headers={"Authorization": "Bearer sk_..."},
)
batch = res.json()
```

### Response

```json
{
  "status": "complete",
  "total": 2,
  "filled": 2,
  "failed": 0,
  "results": [
    { "row": 1, "status": "filled", "pdf_url": "/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/rows/1/pdf" },
    { "row": 2, "status": "filled", "pdf_url": "/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/rows/2/pdf" }
  ],
  "error": null
}
```

### Status codes

| Code | Meaning |
| --- | --- |
| 200 | Batch status returned. |
| 401 | Missing or invalid API key. |
| 404 | No such batch, or owned by another key. |

## Download a row PDF

```
GET /forms/fill-batch/{batch_id}/rows/{n}/pdf
```

Authentication: Bearer API key required.

Download one filled row's PDF. n is the 1-based row index from the batch manifest.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| batch_id | path | string | yes | The batch id returned by POST fill-batch. |
| n | path | integer | yes | The 1-based row number from the batch results manifest. |

```curl
curl https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/rows/1/pdf \
  -H "Authorization: Bearer sk_..." \
  -o row-1.pdf
```

```js
const res = await fetch(
  "https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/rows/1/pdf",
  { headers: { Authorization: "Bearer sk_..." } },
);
const pdf = await res.arrayBuffer(); // the raw PDF bytes
```

```python
import requests

res = requests.get(
    "https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/rows/1/pdf",
    headers={"Authorization": "Bearer sk_..."},
)
open("row-1.pdf", "wb").write(res.content)
```

### Response

```json
Binary PDF (Content-Type: application/pdf).
```

### Status codes

| Code | Meaning |
| --- | --- |
| 200 | PDF returned. |
| 401 | Missing or invalid API key. |
| 404 | No such batch, or that row was not filled. |
| 410 | The form's documents have been deleted under its retention policy. |

## Download all rows as ZIP

```
GET /forms/fill-batch/{batch_id}/zip
```

Authentication: Bearer API key required.

Download a ZIP of every filled row PDF. Only filled rows are included. The archive is named batch-{id}.zip.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| batch_id | path | string | yes | The batch id returned by POST fill-batch. |

```curl
curl https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/zip \
  -H "Authorization: Bearer sk_..." \
  -o batch.zip
```

```js
const res = await fetch(
  "https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/zip",
  { headers: { Authorization: "Bearer sk_..." } },
);
const zip = await res.arrayBuffer(); // the raw ZIP bytes
```

```python
import requests

res = requests.get(
    "https://api.getemboss.ai/forms/fill-batch/a1b2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/zip",
    headers={"Authorization": "Bearer sk_..."},
)
open("batch.zip", "wb").write(res.content)
```

### Response

```json
Binary ZIP (Content-Type: application/zip) containing one row-N.pdf per filled row.
```

### Status codes

| Code | Meaning |
| --- | --- |
| 200 | ZIP returned. |
| 401 | Missing or invalid API key. |
| 404 | No such batch, or owned by another key. |
| 410 | The form's documents have been deleted under its retention policy. |

## See also

- [Batch fill](https://getemboss.ai/docs/batch-fill)
- [Batch fill PDFs from a spreadsheet](https://getemboss.ai/use-cases/batch-fill-from-spreadsheet)
