API 참조

이 문서는 api/app.js 내 Express 앱의 동작과 api/routes/ 아래의 라우트 핸들러와 일치합니다.

제한 및 동작

항목
JSON 본문 크기최대 2 MB (express.json({ limit: '2mb' }))
요청당 대상1–36 개의 언어 코드
배치 항목배치 요청당 1–100 항목
모델standard (기본) 또는 advanced (유료 등급 전용; 아래 참조)

월별 토큰 허용량 (무료 등급): 모델 호출 전에 API는 토큰을 대략 ceil(content_length / 4) × (number_of_targets + 1)로 추정하며, free 등급에 한해 추정치가 남은 월별 할당량(FREE_TIER_MONTHLY_TOKENS, 기본값 100000)을 초과하면 429 / token_limit_reached로 요청을 거부합니다. 유료 등급은 enforceTokenCap에서 이 사전 검사를 차단하지 않으며, 사용량은 계속 기록됩니다.

요청 제한: Upstash Redis가 구성된 경우(UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN, URL에 your-instance 자리 표시자가 없을 때) 등급별 분당 제한이 적용됩니다: 무료 5, 스타터 30, 성장 60, 스케일 120, 엔터프라이즈 무제한. 제한 초과 시 응답은 429error: "rate_limit_reached"입니다. Redis가 구성되지 않은 경우 요청 제한은 건너뜁니다(rateLimit.js 참조).

성공적인 요청 제한 응답에는 X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset이 포함될 수 있습니다.


GET /health

인증 없음.

응답 200

{
  "status": "ok",
  "timestamp": "2025-03-23T12:00:00.000Z"
}

GET /languages

인증 없음.

지원되는 언어의 표준 목록(코드, 표시 이름, RTL 플래그)을 반환합니다. 총 36개 항목이 있으며, targets에서 허용되는 값은 코드뿐입니다.

응답 200

{
  "languages": [
    { "code": "en", "name": "English", "rtl": false },
    { "code": "ar", "name": "Arabic", "rtl": true }
  ]
}

출처: api/utils/languages.js.


POST /translate

Authorization: Bearer <api_key> 필요.

단일 content 문자열을 targets에 나열된 모든 언어로 번역합니다. 모델은 요청된 언어 코드와 정확히 일치하는 키를 가진 단일 JSON 객체를 반환하며, 값은 번역된 문자열입니다(formatPrompts.js 참조).

요청 본문

필드유형필수설명
contentstring번역할 비어 있지 않은 문자열
targetsstring[]유효한 언어 코드의 비어 있지 않은 배열 (최대 36)
formatstring아니오plain, markdown, json, html 중 하나. 생략 시 content에서 자동 감지
sourcestring아니오모델에 대한 원본 언어 힌트; 선택 사항
modelstring아니오standard (기본) 또는 advanced. advanced는 유료 등급 필요 (무료 등급은 403)

응답 200

{
  "translations": {
    "es": "...",
    "fr": "..."
  },
  "usage": {
    "input_tokens": 120,
    "output_tokens": 340,
    "total_tokens": 460,
    "model": "standard",
    "detected_format": "markdown",
    "detection_confidence": 0.95
  }
}

detected_formatdetection_confidenceformat이 생략되어 자동 감지가 실행된 경우에만 나타납니다.

예시 (cURL)

curl -sS -X POST "https://api.usepolylingo.com/v1/translate" \
  -H "Authorization: Bearer $POLYLINGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": "{\"title\":\"Hello\"}",
    "format": "json",
    "targets": ["fr", "de"]
  }'

예시 (Python 3)

pip install requests
import os, requests

url = "https://api.usepolylingo.com/v1/translate"
headers = {
    "Authorization": f"Bearer {os.environ['POLYLINGO_API_KEY']}",
    "Content-Type": "application/json",
}
r = requests.post(url, json={
    "content": "<p>Hello <strong>world</strong></p>",
    "format": "html",
    "targets": ["es"],
}, timeout=120)
r.raise_for_status()
print(r.json()["translations"]["es"])

POST /translate/batch

Authorization: Bearer <api_key> 필요.

각 항목을 순차적으로 처리합니다(항목당 모델 호출 1회). 항목 중 하나라도 실패하면 API는 500을 반환하며 해당 요청에 대한 부분 결과를 반환하지 않습니다.

요청 본문

필드유형필수설명
items배열각 요소: id (문자열), content (문자열), 선택적 format
targetsstring[]/translate와 동일한 규칙
sourcestring아니오선택적 원본 언어 힌트
modelstring아니오standard 또는 advanced (/translate와 동일한 규칙)

응답 200

{
  "results": [
    { "id": "welcome", "translations": { "fr": "...", "de": "..." } },
    { "id": "goodbye", "translations": { "fr": "...", "de": "..." } }
  ],
  "usage": {
    "total_tokens": 900,
    "input_tokens": 400,
    "output_tokens": 500,
    "model": "standard"
  }
}

예시

curl -sS -X POST "https://api.usepolylingo.com/v1/translate/batch" \
  -H "Authorization: Bearer $POLYLINGO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "items": [
      { "id": "a", "content": "Hello", "format": "plain" },
      { "id": "b", "content": "## Title", "format": "markdown" }
    ],
    "targets": ["es", "it"]
  }'

POST /jobs

Authorization: Bearer <api_key> 필요.

번역 작업을 큐에 넣고 즉시 job_id를 반환합니다. 번역은 백그라운드에서 실행되므로 콘텐츠 크기와 관계없이 HTTP 타임아웃 위험이 없습니다. 결과는 GET /jobs/:id로 폴링하세요.

긴 문서(긴 Markdown, 많은 대상 언어)를 번역할 때 요청 시간이 HTTP 클라이언트 또는 프록시 타임아웃을 초과할 수 있으므로 이 엔드포인트를 POST /translate 대신 사용하세요.

요청 본문

필드유형필수설명
contentstring번역할 비어 있지 않은 문자열
targetsstring[]유효한 언어 코드의 비어 있지 않은 배열 (최대 36)
formatstring아니오plain, markdown, json, html 중 하나. 생략 시 자동 감지
sourcestring아니오원본 언어 힌트; 선택 사항
modelstring아니오standard (기본) 또는 advanced

응답 202

{
  "job_id": "a1b2c3d4-...",
  "status": "pending",
  "created_at": "2025-03-23T12:00:00.000Z"
}

GET /jobs/:id

Authorization: Bearer <api_key> 필요.

POST /jobs로 제출된 작업의 상태를 폴링합니다. 5~10초마다 폴링하세요. 작업은 제출한 사용자 소유이며, 다른 사용자는 404를 받습니다.

응답 (대기 중 / 처리 중)

{
  "job_id": "a1b2c3d4-...",
  "status": "pending",
  "created_at": "2025-03-23T12:00:00.000Z",
  "updated_at": "2025-03-23T12:00:00.000Z",
  "completed_at": null,
  "queue_position": 3
}

statuspending (작업자 대기 중) 또는 processing (작업자가 작업을 가져감)입니다. queue_position (1부터 시작)은 이 작업보다 엄격히 먼저 생성된 대기 중 또는 처리 중 작업 수이며 진행 UI에 사용하세요. 쿼리 실패 시 생략됩니다.

응답 (완료됨)

{
  "job_id": "a1b2c3d4-...",
  "status": "completed",
  "created_at": "2025-03-23T12:00:00.000Z",
  "updated_at": "2025-03-23T12:00:02.000Z",
  "completed_at": "2025-03-23T12:00:02.000Z",
  "translations": {
    "es": "...",
    "fr": "..."
  },
  "usage": {
    "input_tokens": 120,
    "output_tokens": 340,
    "total_tokens": 460,
    "model": "standard"
  }
}

응답 (실패)

{
  "job_id": "a1b2c3d4-...",
  "status": "failed",
  "error": "Model returned invalid JSON"
}

예시 (JavaScript)

const API = 'https://api.usepolylingo.com/v1'
const headers = {
  'Authorization': `Bearer ${process.env.POLYLINGO_API_KEY}`,
  'Content-Type': 'application/json',
}

// 1. 제출
const submit = await fetch(`${API}/jobs`, {
  method: 'POST',
  headers,
  body: JSON.stringify({ content: longMarkdown, format: 'markdown', targets: ['de', 'fr'] }),
})
const { job_id } = await submit.json()

// 2. 폴링
while (true) {
  await new Promise(r => setTimeout(r, 10_000))
  const poll = await fetch(`${API}/jobs/${job_id}`, { headers })
  const job = await poll.json()
  if (job.status === 'completed') { console.log(job.translations); break }
  if (job.status === 'failed')    { throw new Error(job.error) }
  // 선택 사항: 진행 상황 표시 (queue_position은 1부터 시작하며, 큐에 없으면 생략)
  if (job.queue_position != null) console.log(`Queue position: ${job.queue_position}`)
}

GET /usage

Authorization: Bearer <api_key> 필요 (표준 키 조회 — 내부 우회 전용 경로 아님).

인증된 사용자의 현재 달력 월 토큰 사용량을 반환합니다.

응답 200

{
  "period_start": "2025-03-01T00:00:00.000Z",
  "period_end": "2025-03-31T23:59:59.000Z",
  "tokens_used": 12000,
  "tokens_included": 100000,
  "tokens_remaining": 88000,
  "overage_tokens": 0,
  "tier": "free"
}

tokens_includedtokens_remainingenterprise의 경우 null입니다 (보고서에 무제한 할당).


콘텐츠 형식

지원되는 format 값: plain, markdown, json, html.

형식보존됨번역됨
plain줄 바꿈 / 단락모든 가시 텍스트
markdown구문, 링크(URL 변경 없음), 펜스 코드(있는 그대로)산문 및 링크 텍스트
json키, 구조, 비문자열 유형문자열 값만
html태그 및 속성텍스트 노드 및 적절한 속성(프롬프트 참조)

앱에서의 RTL 및 방향

plainmarkdown 출력의 경우, API는 번역된 텍스트만 반환하며 dir="rtl" 또는 래퍼 요소를 추가하지 않습니다. 아랍어, 히브리어 또는 페르시아어를 표시할 때 UI에서 텍스트 방향(CSS direction, 상위 요소의 dir 속성 또는 프레임워크의 i18n 레이아웃)을 설정하세요.

html 형식의 경우, 번역된 마크업에 RTL 대상에 적합한 경우 dir="rtl"이 포함될 수 있습니다; formatPrompts.jsscripts/test-translation.js의 HTML 테스트 참조.


오류 응답

오류는 가능하면 JSON 형식입니다:

{
  "error": "invalid_request",
  "message": "사람이 읽을 수 있는 상세 내용"
}
HTTPerror상황
400invalid_request본문 필드 누락/잘못됨 (예: 빈 content, 잘못된 targets)
400invalid_format지원되지 않는 format
400invalid_languagetargets에 알 수 없는 코드 포함
401invalid_api_key누락/잘못된 Authorization, 알 수 없는 키, 취소된 키
403advanced_not_available무료 등급에서 model: "advanced" 요청
429token_limit_reached무료 등급 월별 한도 초과 (사전 검사)
429rate_limit_reached분당 RPM 제한 초과 (Redis 활성화 시)
500translation_error모델/네트워크 실패; 재시도 가능
404not_foundGET /jobs/:id — 작업이 없거나 다른 사용자 소유
500server_errorPOST /jobs — 큐에 넣기 실패; 재시도 가능

GET /usage는 Supabase 쿼리 실패 시 일반 메시지와 함께 500을 반환할 수 있습니다.


기본 모델 (참고용)

API는 standardadvanced만 노출합니다. 실제 OpenAI 모델 ID는 api/utils/modelRouter.js에서 구성되며 API 응답에는 포함되지 않습니다.

API 참조 | PolyLingo | PolyLingo