---
title: "OCR"
description: "Read the text in a product image: every region with its polygon, bounding box, text and confidence. No cleaning, no translation."
---

> Every apinoa docs page as markdown: https://apinoa.com/llms.txt (all in one file: https://apinoa.com/llms-full.txt; this API family in one file: https://apinoa.com/docs/image-translate/llms.txt)

```http
POST /v1/image-translate/ocr
```

Runs only the reading step of [Translate](/docs/api-reference/image-translate/translate): it returns the
text regions and changes nothing. Use it to pre-screen images or to drive your own workflow. Send the image as **raw bytes** in the body and `source_language` in the query string.

## Request

```bash tab="cURL"
curl -X POST "https://gateway.apinoa.com/v1/image-translate/ocr?source_language=zh-CN" \
  -H "x-api-key: $APINOA_API_KEY" \
  -H "Content-Type: image/jpeg" \
  --data-binary @product.jpg
```

```js tab="JavaScript"
import { readFile } from "node:fs/promises";

const res = await fetch("https://gateway.apinoa.com/v1/image-translate/ocr?source_language=zh-CN", {
  method: "POST",
  headers: { "x-api-key": process.env.APINOA_API_KEY, "Content-Type": "image/jpeg" },
  body: await readFile("product.jpg"),
});
const { success, data, error } = await res.json();
```

```python tab="Python"
import os

import requests

with open("product.jpg", "rb") as f:
    image = f.read()

res = requests.post(
    "https://gateway.apinoa.com/v1/image-translate/ocr",
    params={"source_language": "zh-CN"},
    headers={"x-api-key": os.environ["APINOA_API_KEY"], "Content-Type": "image/jpeg"},
    data=image,
    timeout=60,
)
body = res.json()
```

## Parameters

**Headers**

| Header | Required | Notes |
|---|---|---|
| `x-api-key` | yes | Your API key. |
| `Content-Type` | yes | `image/jpeg`, `image/png`, `image/webp` or `application/octet-stream`. |

**Query string**

| Parameter | Type | Required | Notes |
|---|---|---|---|
| `source_language` | string | yes | `ch`, `zh`, `zh-CN`, `zh-TW`, `chinese_cht`, `ja`, `japan`, `en`, or `auto`. |

## Response

A real response, shortened to its first region:

```json
{
  "success": true,
  "requestId": "43d899616f8e468aa22368bffaae1953",
  "data": {
    "regions": [
      {
        "index": 0,
        "polygon": [[83, 68], [352, 68], [352, 145], [83, 145]],
        "bbox": { "x": 83, "y": 68, "w": 269, "h": 77 },
        "text": "限时特价",
        "confidence": 0.999354,
        "text_color": { "r": 255, "g": 253, "b": 252 },
        "bg_color": { "r": 211, "g": 40, "b": 43 },
        "bg_std": 0.363554,
        "font_weight": "Regular",
        "is_badge": false
      }
    ],
    "regions_found": 3,
    "src_w": 800,
    "src_h": 600
  }
}
```

| Field | Type | Meaning |
|---|---|---|
| `regions[].index` | integer | The region's number, starting at 0. |
| `regions[].polygon` | number[][] | Four corner points, in the original image's coordinates. |
| `regions[].bbox` | object | The bounding box `{x, y, w, h}`, in the original image's coordinates. |
| `regions[].text` | string | The text that was read. |
| `regions[].confidence` | number | Recognition confidence, 0–1. |
| `regions[].text_color`, `regions[].bg_color` | object | Estimated text and background colours, as RGB. |
| `regions[].bg_std` | number | How uneven the background is behind the text; near 0 is a flat colour. |
| `regions[].font_weight` | string | The detected weight. |
| `regions[].is_badge` | boolean | `true` for text on a filled label, such as a price tag or sale badge. |
| `regions_found` | integer | How many regions were found. |
| `src_w`, `src_h` | integer | The original image's width and height, in pixels. |

## Errors

| Status | Code | Meaning |
|---|---|---|
| 400 | `BAD_REQUEST` | The body is empty, or a query parameter is invalid. |
| 401 | `UNAUTHORIZED` | The `x-api-key` header is missing, or the key is invalid. |
| 402 | `INSUFFICIENT_BALANCE` | The balance does not cover the call. Retrying does not clear it; top up. |
| 413 | `PAYLOAD_TOO_LARGE` | The body is over 20 MB. |
| 415 | `UNSUPPORTED_MEDIA_TYPE` | `Content-Type` is not an image type. |
| 422 | `INVALID_IMAGE` | The bytes are not a readable image. |
| 502 | `UPSTREAM_ERROR` | Reading is briefly unavailable. Retry. |

Every code is listed in [Errors](/docs/errors). Failed calls are not billed.

## Pricing

Each call is charged at the image-translate rate on the [pricing page](/pricing), the same as Translate.

## Notes

- **Limits.** Images up to 8192 × 8192 px and a 20 MB body, as JPEG, PNG or WebP.
