# Handwriting to Excel

Convert a handwritten table, log, or record into an editable XLSX workbook using handwriting-optimized recognition.

Canonical documentation: https://img2excel.net/docs/api/handwriting-to-excel

`POST /api/v1/handwriting-to-excel`

Convert a handwritten table, log, or record into an editable XLSX workbook using handwriting-optimized recognition. The synchronous endpoint waits for the conversion to finish. Failed conversions do not consume credits.

Use a sharp, evenly lit image with the whole table visible. Review extracted names, dates, and figures before using the workbook.

## Request

### Headers

| Header | Required | Description |
| --- | --- | --- |
| `Authorization` | Yes | `Bearer $IMG2EXCEL_API_KEY`. Use a server-side API key. |
| `Content-Type` | Yes | `multipart/form-data` with a boundary. Let your HTTP client set this when sending a file. |
| `Idempotency-Key` | No | A retry identifier of up to 200 characters. Recommended for every upload. |

### Body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `file` | Binary file | Yes | One PNG, JPG, or JPEG image, up to 4MB. |

Send the image as a file upload, not a JSON body or an image URL. This endpoint selects the recognition profile automatically.

## Request Examples

Run these examples on your server with `IMG2EXCEL_API_KEY` set. Python requires `pip install requests`; Node.js requires version 20 or later.


### cURL

```bash
curl --fail-with-body --max-time 330 -i https://img2excel.net/api/v1/handwriting-to-excel \
  -H "Authorization: Bearer $IMG2EXCEL_API_KEY" \
  -H "Idempotency-Key: handwriting-to-excel-001" \
  -F "file=@handwritten-log.jpg"
```


### Python

```python
import os
import requests

with open("handwritten-log.jpg", "rb") as image:
    response = requests.post(
        "https://img2excel.net/api/v1/handwriting-to-excel",
        headers={
            "Authorization": f"Bearer {os.environ['IMG2EXCEL_API_KEY']}",
            "Idempotency-Key": "handwritten-log-001",
        },
        files={"file": ("handwritten-log.jpg", image, "image/jpeg")},
        timeout=330,
    )

response.raise_for_status()
job = response.json()
print(job["result"]["download_url"])
```


### Node.js

```ts
import { readFile } from 'node:fs/promises';

const file = await readFile('handwritten-log.jpg');
const form = new FormData();
form.set('file', new File([file], 'handwritten-log.jpg', { type: 'image/jpeg' }));

const response = await fetch('https://img2excel.net/api/v1/handwriting-to-excel', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.IMG2EXCEL_API_KEY}`,
    'Idempotency-Key': 'handwritten-log-001',
  },
  body: form,
  signal: AbortSignal.timeout(330_000),
});

if (!response.ok) throw new Error(await response.text());
const job = await response.json();
console.log(job.result.download_url);
```




## Response

**200 OK** — conversion completed. Download the Excel file using `result.download_url`. The URL in this example is a placeholder.

```json
{
  "id": "job_0123456789abcdef0123456789abcdef",
  "type": "handwriting-to-excel",
  "status": "succeeded",
  "credits_reserved": 2,
  "credits_used": 2,
  "created_at": "2026-10-04T09:00:00.000Z",
  "completed_at": "2026-10-04T09:00:08.000Z",
  "result": {
    "filename": "table_result.xlsx",
    "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    "size_bytes": 17330,
    "download_url": "https://signed-storage-url.example/...",
    "download_url_expires_in": 900
  }
}
```

**422 Unprocessable Entity** — conversion failed. The response includes the job ID, `status: "failed"`, and `error.code` / `error.message`. Failed conversions do not consume credits.

When an idempotency key matches a previous conversion, `Idempotent-Replayed: true` is returned. A completed job returns `200`; an expired result returns `410`. If the original job is still queued or processing, the retry returns `202` and a `Location` header. Query that URL rather than creating another conversion.

See [Jobs and Errors](https://img2excel.net/docs/api/jobs) for all response fields. Synchronous and asynchronous calls share the same job records, account limits, and idempotency keys for each conversion type.

The synchronous endpoint waits for conversion to complete. Set your HTTP client timeout to 330 seconds. If the connection times out, retry with the same `Idempotency-Key`; do not use a new key unless you intend to start another conversion.

## Asynchronous Conversion (Optional)

Use `POST /api/v1/handwriting-to-excel/jobs` if you prefer to submit without waiting. Use the same upload fields and authentication. A new job returns `202` with its ID and a `Location` header. Poll `GET /api/v1/jobs/{job_id}` every 2–3 seconds until `succeeded`, `failed`, or `expired`. On success, download `result.download_url`. See [Jobs and Errors](https://img2excel.net/docs/api/jobs) for a polling script.

---

OpenAPI 3.1: https://img2excel.net/api/openapi.json
Complete API documentation: https://img2excel.net/docs/api/llms-full.txt
