Referência da API

Este documento corresponde ao comportamento do aplicativo Express em api/app.js e aos manipuladores de rota em api/routes/.

Limites e comportamento

ItemValor
Tamanho do corpo JSONAté 2 MB (express.json({ limit: '2mb' }))
Destinos por requisição1–36 códigos de idioma
Itens por lote1–100 itens por requisição em lote
Modelosstandard (padrão) ou advanced (apenas planos pagos; veja abaixo)

Limite mensal de tokens (plano gratuito): Antes de chamar o modelo, a API estima tokens aproximadamente como ceil(content_length / 4) × (number_of_targets + 1) e, apenas para o plano free, rejeita a requisição com 429 / token_limit_reached se a estimativa ultrapassar a cota mensal restante (FREE_TIER_MONTHLY_TOKENS, padrão 100000). Planos pagos não são bloqueados por essa verificação prévia em enforceTokenCap; o uso ainda é registrado.

Limites de taxa: Quando o Upstash Redis está configurado (UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN, e a URL não contém o placeholder your-instance), limites por minuto se aplicam por plano: gratuito 5, iniciante 30, crescimento 60, escala 120, empresarial ilimitado. Ao atingir o limite, a resposta é 429 com error: "rate_limit_reached". Se o Redis não estiver configurado, a limitação de taxa é ignorada (veja rateLimit.js).

Respostas bem-sucedidas com limitação de taxa podem incluir X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.


GET /health

Sem autenticação.

Resposta 200

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

GET /languages

Sem autenticação.

Retorna a lista canônica de idiomas suportados (código, nome exibido, flag RTL). Há 36 entradas; os códigos são os únicos valores aceitos em targets nos endpoints de tradução.

Resposta 200

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

Fonte: api/utils/languages.js.


POST /translate

Requer Authorization: Bearer <api_key>.

Traduza uma única string content para todos os idiomas listados em targets. O modelo retorna um único objeto JSON cujas chaves são exatamente os códigos de idioma solicitados e cujos valores são as strings traduzidas (veja formatPrompts.js).

Corpo da requisição

CampoTipoObrigatórioDescrição
contentstringSimString não vazia para traduzir.
targetsstring[]SimArray não vazio de códigos de idioma válidos (máx. 36).
formatstringNãoUm dos plain, markdown, json, html. Se omitido, o formato é detectado automaticamente a partir do content.
sourcestringNãoDica do idioma de origem para o modelo; opcional.
modelstringNãostandard (padrão) ou advanced. advanced requer plano pago (403 no gratuito).

Resposta 200

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

detected_format e detection_confidence aparecem apenas quando format foi omitido e a detecção automática foi executada.

Exemplo (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"]
  }'

Exemplo (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

Requer Authorization: Bearer <api_key>.

Processa cada item sequencialmente (uma chamada ao modelo por item). Se algum item falhar, a API retorna 500 e não retorna resultados parciais para essa requisição.

Corpo da requisição

CampoTipoObrigatórioDescrição
itemsarraySimCada elemento: id (string), content (string), format opcional.
targetsstring[]SimMesmas regras de /translate.
sourcestringNãoDica opcional do idioma de origem.
modelstringNãostandard ou advanced (mesmas regras de /translate).

Resposta 200

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

Exemplo

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

Requer Authorization: Bearer <api_key>.

Enfileira um trabalho de tradução e retorna imediatamente com um job_id. A tradução é executada em segundo plano — sem risco de timeout HTTP independentemente do tamanho do conteúdo. Consulte GET /jobs/:id para o resultado.

Use este endpoint em vez de POST /translate ao traduzir documentos grandes (Markdown longo, muitos idiomas alvo) onde a duração da requisição pode exceder o timeout do seu cliente HTTP ou proxy.

Corpo da requisição

CampoTipoObrigatórioDescrição
contentstringSimString não vazia para traduzir.
targetsstring[]SimArray não vazio de códigos de idioma válidos (máx. 36).
formatstringNãoUm dos plain, markdown, json, html. Detectado automaticamente se omitido.
sourcestringNãoDica do idioma de origem; opcional.
modelstringNãostandard (padrão) ou advanced.

Resposta 202

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

GET /jobs/:id

Requer Authorization: Bearer <api_key>.

Consulta o status de um trabalho enviado via POST /jobs. Consulte a cada 5–10 segundos. Os trabalhos pertencem ao usuário que os enviou — outros usuários recebem 404.

Resposta (pendente / processando)

{
  "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
}

status é pending (aguardando um trabalhador) ou processing (trabalhador assumiu). queue_position (base 1) indica quantos trabalhos pendentes ou em processamento foram criados estritamente antes deste — use para UI de progresso. Omitido se a consulta de contagem falhar.

Resposta (concluído)

{
  "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"
  }
}

Resposta (falha)

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

Exemplo (JavaScript)

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

// 1. Enviar
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. Consultar
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) }
  // Opcional: mostrar progresso (queue_position é base 1, omitido se não estiver na fila)
  if (job.queue_position != null) console.log(`Posição na fila: ${job.queue_position}`)
}

GET /usage

Requer Authorization: Bearer <api_key> (consulta padrão da chave — não o caminho interno de bypass).

Retorna o uso de tokens para o mês calendário atual para o usuário autenticado.

Resposta 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_included e tokens_remaining são null para enterprise (cota ilimitada no relatório).


Formatos de conteúdo

Valores suportados para format: plain, markdown, json, html.

FormatoPreservadoTraduzido
plainQuebras de linha / parágrafosTodo texto visível
markdownSintaxe, links (URL inalterada), código cercado (verbatim)Prosa e texto dos links
jsonChaves, estrutura, tipos não stringApenas valores string
htmlTags e atributosNós de texto e atributos apropriados (veja prompts)

RTL e direção na sua aplicação

Para saída plain e markdown, a API retorna apenas o texto traduzido — não adiciona dir="rtl" ou elementos wrapper. Defina a direção do texto na sua UI (CSS direction, atributo dir de um elemento pai ou layout i18n do seu framework) ao exibir árabe, hebraico ou persa.

Para formato html, a marcação traduzida pode incluir dir="rtl" onde apropriado para destinos RTL; veja formatPrompts.js e os testes HTML em scripts/test-translation.js.


Respostas de erro

Erros são JSON quando possível:

{
  "error": "invalid_request",
  "message": "Detalhe legível para humanos"
}
HTTPerrorQuando
400invalid_requestCampos do corpo ausentes/inválidos (ex: content vazio, targets inválidos)
400invalid_formatformat não está no conjunto suportado
400invalid_languageCódigo desconhecido em targets
401invalid_api_keyAuthorization ausente/malformado, chave desconhecida, chave revogada
403advanced_not_availablemodel: "advanced" no plano gratuito
429token_limit_reachedLimite mensal do plano gratuito excedido (pré-verificação)
429rate_limit_reachedLimite RPM por minuto (quando Redis habilitado)
500translation_errorFalha do modelo/rede; seguro para tentar novamente
404not_foundGET /jobs/:id — trabalho não existe ou pertence a outro usuário
500server_errorPOST /jobs — falha ao enfileirar; seguro para tentar novamente

GET /usage pode retornar 500 com mensagem genérica se a consulta Supabase falhar.


Modelos subjacentes (informativo)

A API expõe apenas standard e advanced. Os IDs reais dos modelos OpenAI são configurados em api/utils/modelRouter.js e não são retornados nas respostas da API.

Referência da API | PolyLingo | PolyLingo