---
title: "Translate"
description: "Replace the text inside a product image with a translation, keeping the font weight, colours, layout and badge backdrops."
---

> 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/translate
```

Send the image as **raw bytes** in the body (not JSON, not base64), and every other parameter in the query
string. You get back the translated JPEG, or with `render=false` the cleaned image and every text region as
JSON.

## Request

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

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

const params = new URLSearchParams({ source_language: "zh-CN", target_language: "ko" });
const res = await fetch(`https://gateway.apinoa.com/v1/image-translate/translate?${params}`, {
  method: "POST",
  headers: { "x-api-key": process.env.APINOA_API_KEY, "Content-Type": "image/jpeg" },
  body: await readFile("product.jpg"),
});
await writeFile("translated.jpg", Buffer.from(await res.arrayBuffer()));
```

```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/translate",
    params={"source_language": "zh-CN", "target_language": "ko"},
    headers={"x-api-key": os.environ["APINOA_API_KEY"], "Content-Type": "image/jpeg"},
    data=image,
    timeout=300,
)
res.raise_for_status()
with open("translated.jpg", "wb") as f:
    f.write(res.content)
```

## 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 | The text's language: `ch`, `zh`, `zh-CN`, `zh-TW`, `chinese_cht`, `ja`, `japan`, `en`, or `auto`. `auto` skips the language check, so every region with a translation is rendered. |
| `target_language` | string (2–8) | yes | The language to translate into, as an ISO 639-style code. Korean, Simplified Chinese, Japanese and Latin-script targets render best; [Supported Fonts](/docs/api-reference/image-translate/supported-fonts) lists the fonts for each. |
| `font` | string | | A value from `allFonts` in [Supported Fonts](/docs/api-reference/image-translate/supported-fonts), or `user/<id>` for an uploaded [custom font](/docs/api-reference/image-translate/custom-fonts). Default `Pretendard`. Glyphs the font lacks fall back to `Pretendard` and `NotoSansSC`. |
| `font_weight` | string | | `auto`, `Thin`, `Light`, `Regular`, `Medium`, `SemiBold`, `Bold`, `ExtraBold` or `Black`. Default `ExtraBold`. `auto` matches each region's original weight; any other value applies to every region. |
| `render` | boolean | | `true` (default) returns the finished image; `false` returns the cleaned image and the regions as JSON, to draw yourself. |

## Response

### `render=true` (Default): The Image

The body is the translated **JPEG** (`Content-Type: image/jpeg`), with these headers:

| Header | Meaning |
|---|---|
| `X-Apinoa-Request-Id` | The request's id. Quote it if you contact support. |
| `X-Apinoa-Regions-Count` | How many text regions were detected and replaced. |

An image with no text comes back unchanged, with `X-Apinoa-Regions-Count: 0`.

### `render=false`: The Regions as JSON

A real response, shortened to its first region:

```json
{
  "success": true,
  "requestId": "240d4c0348574bd7bc12f8e5d8780a87",
  "data": {
    "inpainted_base64": "/9j/4AAQSkZJRgABAQAA…",
    "regions": [
      {
        "index": 0,
        "polygon": [[83, 68], [352, 68], [352, 145], [83, 145]],
        "bbox": { "x": 83, "y": 68, "w": 269, "h": 77 },
        "text": "限时特价",
        "text_kr": "기간 한정 특가",
        "confidence": 0.999354,
        "text_color": { "r": 255, "g": 253, "b": 252 },
        "bg_color": { "r": 211, "g": 40, "b": 43 },
        "bg_std": 0.418889,
        "font_weight": "Regular",
        "is_badge": false
      }
    ],
    "regions_count": 3
  }
}
```

| Field | Type | Meaning |
|---|---|---|
| `inpainted_base64` | string | The image with the source text removed, as a base64 JPEG. |
| `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[].text_kr` | string | Its translation, in `target_language` (the field name is historical). |
| `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_count` | integer | How many regions were found. |

## 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. Downscale before sending. |
| 413 | `IMAGE_TOO_LARGE` | The image is over 8192 × 8192 px. |
| 415 | `UNSUPPORTED_MEDIA_TYPE` | `Content-Type` is not an image type. Send raw bytes with `image/jpeg`, `image/png` or `image/webp`. |
| 422 | `INVALID_IMAGE` | The bytes are not a readable image. |
| 502 | `UPSTREAM_ERROR` | Processing is briefly unavailable. Retry; it usually recovers within a minute. |
| 503 | `SERVICE_UNAVAILABLE` | Temporarily over capacity. Retry with backoff. |

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).

## Notes

- **Limits.** Images up to 8192 × 8192 px and a 20 MB body, as JPEG, PNG or WebP.
- **Timeouts.** Most calls finish in 1.5–3 seconds; under heavy load one can take up to **5 minutes**. Set
  your HTTP client's timeout to at least 300 seconds so a slow answer is not cut off on your side.
- **Drawing it yourself.** With `render=false`, decode `inpainted_base64` and draw each region's
  `text_kr` inside its `polygon`, using `text_color`, `bg_color` and `font_weight`.
- **Your own font.** Upload one and pass `font=user/<id>`; see [Custom Fonts](/docs/api-reference/image-translate/custom-fonts).
