이미지 번역
상품 이미지 속 텍스트를 원본 시각 스타일(자형 굵기 · 색상 · 레이아웃 · 배지 배경)을 유지한 채 다른 언어로 치환합니다.
엔드포인트
POST /v1/image-translate/translate상품 이미지를 원시 바이트(raw binary) 로 전송하면, 원본 텍스트가 지워지고 선택한 대상 언어로 치환된 이미지를 반환합니다. 폰트 · 굵기 · 렌더 모드는 호출자가 쿼리 스트링으로 지정합니다.
- 입력 언어 — 중국어(간체 또는 번체), 일본어, 영어.
auto를 보내면 스크립트 필터를 건너뜁니다. - 출력 언어 — ISO-639 형식의 임의 코드. 시각 품질은 선택한
font가 해당 스크립트의 글리프를 가지고 있는지에 달려 있습니다. 한국어 · 중국어 간체 · 일본어 · 라틴 계열이 1순위 지원이며, 실시간 폰트 · 원본 언어 코드 목록은/supported-fonts에서 확인하세요.
요청 형식
엔드포인트는 HTTP body에 원시 이미지 바이트 만 받습니다. JSON + base64는 허용되지 않습니다.
| 헤더 | 필수 | 비고 |
|---|---|---|
Content-Type | 예 | image/jpeg, image/png, image/webp, 또는 application/octet-stream |
x-api-key | 예 | API 키 |
이외의 모든 파라미터는 쿼리 스트링 으로 전달합니다.
| 파라미터 | 타입 | 필수 | 기본값 | 설명 |
|---|---|---|---|---|
source_language | string | 예 | — | ch, zh, zh-CN, zh-TW, chinese_cht, ja, japan, en, auto 중 하나. auto 는 스크립트 필터를 끄고 번역이 있는 모든 영역을 렌더합니다. |
target_language | string (2–8자) | 예 | — | ISO-639 형식의 대상 언어 코드. 자유 입력. |
font | string | 아니오 | Pretendard | /supported-fonts 의 allFonts 중 하나. ASCII 외 문자가 포함된 경우 URL 인코딩하세요. 기본값은 Pretendard — 한글, CJK 구두점(×, ( ), 《 》, · 등), Latin Extended-A 까지 모두 커버하는 가변 굵기 한글 산세리프입니다. 렌더러가 내장 fallback chain 을 돌리기 때문에, 선택한 폰트가 갖지 못한 글리프(예: 한자)는 Pretendard → NotoSansSC 순서로 자동으로 채워집니다. |
font_weight | string | 아니오 | ExtraBold | auto, Thin, Light, Regular, Medium, SemiBold, Bold, ExtraBold, Black 중 하나. |
render | boolean | 아니오 | true | true 는 최종 번역 이미지(바이너리) 반환. false 는 텍스트가 지워진 이미지와 영역별 메타데이터를 JSON 으로 반환합니다. |
응답
render=true (기본) — 바이너리 이미지
render=true 일 때 응답 body는 렌더된 JPEG 바이트 이며 Content-Type: image/jpeg 입니다. 메타데이터는 응답 헤더로 전달됩니다.
| 헤더 | 설명 |
|---|---|
X-Apinoa-Request-Id | 요청 ID (이슈 신고 시 인용하세요) |
X-Apinoa-Regions-Count | 감지/치환된 텍스트 영역 수 |
텍스트가 감지되지 않은 경우 응답 body는 원본 이미지 바이트이고 X-Apinoa-Regions-Count: 0 입니다.
render=false — JSON + 영역 정보
render=false 일 때 응답은 JSON 입니다.
{
"success": true,
"requestId": "550e8400e29b41d4a716446655440000",
"data": {
"inpainted_base64": "<텍스트 제거된 이미지의 base64 JPEG>",
"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
}
}| 필드 | 타입 | 설명 |
|---|---|---|
inpainted_base64 | string | 원본 텍스트가 제거된 base64 JPEG |
regions[].polygon | number[][] | 원본 좌표계의 4점 폴리곤 |
regions[].bbox | object | 원본 좌표계의 축정렬 경계 박스 |
regions[].text | string | 인식된 원문 |
regions[].text_kr | string | 요청한 target_language 의 번역문. |
regions[].confidence | number | 인식 신뢰도, 0–1 |
regions[].text_color / bg_color | object | RGB 색상 추정 |
regions[].font_weight | string | 감지된 굵기: Thin, Regular, Bold |
regions[].is_badge | boolean | 가격표 · 세일 배지 같은 채워진 사각 라벨이면 true |
예제
기본 번역 (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폰트 지정 (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, # 서버 타임아웃은 5분 — 아래 "타임아웃" 섹션 참고
)
r.raise_for_status()
with open("translated.jpg", "wb") as f:
f.write(r.content)
print("regions:", r.headers.get("X-Apinoa-Regions-Count"))인페인트만 받아 클라이언트에서 렌더 (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"));제한
| 항목 | 값 |
|---|---|
| 최대 이미지 크기 | 8192 × 8192 px |
| 최대 요청 본문 | 20 MB |
| API 키별 QPS | 플랜의 초당 한도와 동일 |
| 이미지 포맷 | JPEG, PNG, WebP |
크기 초과 이미지는 413 Payload Too Large 를, QPS 초과는 retry-after 가 포함된 429 Too Many Requests 를 반환합니다.
오류
| 코드 | HTTP | 발생 조건 | 대응 |
|---|---|---|---|
BAD_REQUEST | 400 | 빈 본문 또는 잘못된 쿼리 파라미터 | 요청 형식을 점검 |
UNAUTHORIZED | 401 | API 키 누락 또는 무효 | x-api-key 헤더 확인 |
UNSUPPORTED_MEDIA_TYPE | 415 | Content-Type 이 바이너리 이미지 형식이 아님 | Content-Type: image/jpeg (또는 png/webp) 로 원시 바이트 전송 |
PAYLOAD_TOO_LARGE | 413 | 본문 > 20 MB | 클라이언트에서 다운스케일 |
INSUFFICIENT_BALANCE | 402 | 잔액이 호출 비용에 못 미침 | 대시보드 > 잔액에서 충전; 재시도로는 해결되지 않음 |
UPSTREAM_ERROR | 502 | 처리 일시 불가 | 재시도; 보통 60초 이내 회복 |
SERVICE_UNAVAILABLE | 503 | 일시적 용량 초과 | 백오프 후 재시도 |
타임아웃
부하가 높을 때는 요청 하나가 최대 5분(300초) 까지 걸릴 수 있습니다. 느린 응답이 클라이언트 측에서 중단되지 않도록 HTTP 클라이언트의 타임아웃을 최소 300초 이상으로 설정하세요.
| 지연 | 일반적 | 최악 |
|---|---|---|
| 일반 부하 | 1.5 – 3 s | < 10 s |
| 높은 부하 | 5 – 30 s | 5 분 |
대부분의 요청은 수 초 이내에 완료됩니다. 5분 한도는 지속적인 버스트 트래픽 환경에서만 의미가 있습니다. 5분 한도에 자주 근접한다면 동시성을 낮추거나 지원팀에 문의하세요.
가격
각 호출은 요금 페이지에 안내된 image-translate 단가로 잔액에서 차감됩니다.
지원
5xx 오류가 반복되거나 품질 회귀가 명확히 보이면 X-Apinoa-Request-Id 응답 헤더의 값을 첨부해 apinoa.com/contact 에서 문의하세요.