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
| Item | Valor |
|---|---|
| Tamanho do corpo JSON | Até 2 MB (express.json({ limit: '2mb' })) |
| Destinos por requisição | 1–36 códigos de idioma |
| Itens por lote | 1–100 itens por requisição em lote |
| Modelos | standard (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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
content | string | Sim | String não vazia para traduzir. |
targets | string[] | Sim | Array não vazio de códigos de idioma válidos (máx. 36). |
format | string | Não | Um dos plain, markdown, json, html. Se omitido, o formato é detectado automaticamente a partir do content. |
source | string | Não | Dica do idioma de origem para o modelo; opcional. |
model | string | Não | standard (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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
items | array | Sim | Cada elemento: id (string), content (string), format opcional. |
targets | string[] | Sim | Mesmas regras de /translate. |
source | string | Não | Dica opcional do idioma de origem. |
model | string | Não | standard 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
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
content | string | Sim | String não vazia para traduzir. |
targets | string[] | Sim | Array não vazio de códigos de idioma válidos (máx. 36). |
format | string | Não | Um dos plain, markdown, json, html. Detectado automaticamente se omitido. |
source | string | Não | Dica do idioma de origem; opcional. |
model | string | Não | standard (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.
| Formato | Preservado | Traduzido |
|---|---|---|
plain | Quebras de linha / parágrafos | Todo texto visível |
markdown | Sintaxe, links (URL inalterada), código cercado (verbatim) | Prosa e texto dos links |
json | Chaves, estrutura, tipos não string | Apenas valores string |
html | Tags e atributos | Nó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"
}
| HTTP | error | Quando |
|---|---|---|
| 400 | invalid_request | Campos do corpo ausentes/inválidos (ex: content vazio, targets inválidos) |
| 400 | invalid_format | format não está no conjunto suportado |
| 400 | invalid_language | Código desconhecido em targets |
| 401 | invalid_api_key | Authorization ausente/malformado, chave desconhecida, chave revogada |
| 403 | advanced_not_available | model: "advanced" no plano gratuito |
| 429 | token_limit_reached | Limite mensal do plano gratuito excedido (pré-verificação) |
| 429 | rate_limit_reached | Limite RPM por minuto (quando Redis habilitado) |
| 500 | translation_error | Falha do modelo/rede; seguro para tentar novamente |
| 404 | not_found | GET /jobs/:id — trabalho não existe ou pertence a outro usuário |
| 500 | server_error | POST /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.