Apinoa Docs

Image Translate

Replace text inside product images with a translation that preserves the original visual style — font weight, colour, layout, and badge backdrops.

View raw .mdx

Endpoint

POST /v1/image-translate/translate

Send a product image as raw bytes; receive the same image with the source text removed and replaced with a translation. Font, weight, and render mode are caller-controlled via query string.

  • Input languages — Chinese (Simplified or Traditional), Japanese, English. Pass auto to skip the script gate.
  • Output languages — any ISO-639-style code. Visual fidelity depends on whether the chosen font covers the script. Korean, Simplified Chinese, Japanese, and Latin targets are first-class. Call /supported-fonts for the live list of fonts and the source-language codes accepted.

Request Format

The endpoint accepts raw image bytes in the HTTP body. JSON-with-base64 is not accepted.

HeaderRequiredNotes
Content-TypeYesimage/jpeg, image/png, image/webp, or application/octet-stream
x-api-keyYesYour API key

All other parameters travel in the query string:

ParameterTypeRequiredDefaultDescription
source_languagestringYesOne of ch, zh, zh-CN, zh-TW, chinese_cht, ja, japan, en, or auto. auto skips the script gate — every region with a translation is rendered.
target_languagestring (2–8)YesISO-639-style target code. Free-form.
fontstringNoPretendardOne of the values returned by /supported-fonts under allFonts. URL-encode if it contains non-ASCII characters. Pretendard is the default — modern Korean variable sans with full Hangul, CJK punctuation, and Latin Extended-A coverage. The renderer also runs a built-in fallback chain so any glyph the chosen font is missing (e.g. CJK Han ideographs) is filled from Pretendard and NotoSansSC automatically.
font_weightstringNoExtraBoldOne of auto, Thin, Light, Regular, Medium, SemiBold, Bold, ExtraBold, Black. auto matches the original per-region weight where possible; any explicit weight overrides every region uniformly.
renderbooleanNotruetrue returns a final translated image (binary). false returns the inpainted image plus per-region metadata as JSON so you can draw the overlay yourself.

Response

render=true (default) — binary image

When render=true the response body is the rendered JPEG bytes with Content-Type: image/jpeg. Metadata flows through response headers:

HeaderDescription
X-Apinoa-Request-IdRequest id (echo this when filing a ticket)
X-Apinoa-Regions-CountNumber of text regions detected and replaced

If no text is detected, the response is the original image bytes with X-Apinoa-Regions-Count: 0.

render=false — JSON with regions

When render=false the response is JSON:

{
  "success": true,
  "requestId": "550e8400e29b41d4a716446655440000",
  "data": {
    "inpainted_base64": "<base64 JPEG with text removed>",
    "regions": [
      {
        "polygon": [[10, 20], [210, 20], [210, 60], [10, 60]],
        "bbox": { "x": 10, "y": 20, "w": 200, "h": 40 },
        "text": "免费送货",
        "text_kr": "무료 배송",
        "confidence": 0.98,
        "text_color": { "r": 255, "g": 255, "b": 255 },
        "bg_color": { "r": 200, "g": 50, "b": 50 },
        "font_weight": "Bold",
        "is_badge": true
      }
    ],
    "regions_count": 5
  }
}
FieldTypeDescription
inpainted_base64stringBase64-encoded JPEG with the source text removed.
regions[].polygonnumber[][]4-point polygon in original image coordinates.
regions[].bboxobjectAxis-aligned bounding box in original coordinates.
regions[].textstringDetected source text.
regions[].text_krstringThe translation, in the requested target_language.
regions[].confidencenumberRecognition confidence, 0–1.
regions[].text_color / bg_colorobjectRGB colour estimates.
regions[].font_weightstringDetected weight: Thin, Regular, Bold.
regions[].is_badgebooleantrue for filled-rectangle text labels (price tags, sale badges).

Examples

Basic translation (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

Pick a font (Python)

import requests

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

r = requests.post(
    "https://gateway.apinoa.com/v1/image-translate/translate",
    headers={
        "x-api-key": "YOUR_API_KEY",
        "Content-Type": "image/jpeg",
    },
    params={
        "source_language": "zh-CN",
        "target_language": "ko",
        "font": "Pretendard",
        "font_weight": "Bold",
    },
    data=img_bytes,
    timeout=300,  # the server timeout is 5 minutes — see "Timeouts" below
)
r.raise_for_status()
with open("translated.jpg", "wb") as f:
    f.write(r.content)
print("regions:", r.headers.get("X-Apinoa-Regions-Count"))

Inpaint-only, render client-side (Node.js / TypeScript)

import { readFileSync, writeFileSync } from "node:fs";

const image = readFileSync("product.jpg");

const url = new URL("https://gateway.apinoa.com/v1/image-translate/translate");
url.searchParams.set("source_language", "zh-CN");
url.searchParams.set("target_language", "ko");
url.searchParams.set("render", "false");

const res = await fetch(url, {
  method: "POST",
  headers: {
    "x-api-key": process.env.APINOA_API_KEY!,
    "Content-Type": "image/jpeg",
  },
  body: image,
});

const { data } = (await res.json()) as {
  data: { inpainted_base64: string; regions: any[] };
};
writeFileSync("inpainted.jpg", Buffer.from(data.inpainted_base64, "base64"));

Limits

LimitValue
Max image dimensions8192 × 8192 px
Max request body20 MB
Per-API-key QPSmatches your plan's per-second limit
Image formatJPEG, PNG, WebP

Oversize images return 413 Payload Too Large. Excessive QPS returns 429 Too Many Requests with retry-after set.

Errors

CodeHTTPWhenWhat to do
BAD_REQUEST400Empty body or invalid query paramsCheck the request shape
UNAUTHORIZED401Missing or invalid API keyCheck the x-api-key header
UNSUPPORTED_MEDIA_TYPE415Content-Type is not a binary image typeSet Content-Type: image/jpeg (or png/webp) and POST raw bytes
PAYLOAD_TOO_LARGE413Body > 20 MBDownscale client-side
INSUFFICIENT_BALANCE402Your balance does not cover the callTop up under Dashboard > Balance; retrying will not clear it
UPSTREAM_ERROR502Image processing temporarily unavailableRetry; usually recovers in < 60 s
SERVICE_UNAVAILABLE503Temporarily over capacityRetry with backoff

Timeouts

A request can take up to 5 minutes (300 s) under heavy load. Set your HTTP client's timeout to at least 300 seconds so a slow response is not aborted on your side.

LatencyTypicalWorst case
Normal load1.5 – 3 s< 10 s
Heavy load5 – 30 s5 min

Most requests finish in a few seconds; the 5-minute window only matters under sustained burst traffic. If you reliably approach it, lower your concurrency or contact support.

Pricing

Each call is charged against your balance at the image-translate rate on the pricing page.

Support

If you hit repeated 5xx errors or see obvious quality regressions, include the requestId from the X-Apinoa-Request-Id response header when you open a ticket at apinoa.com/contact.

On this page