Riferimento API
Questo documento corrisponde al comportamento dell'app Express in api/app.js e ai gestori di route sotto api/routes/.
Limiti e comportamento
| Voce | Valore |
|---|---|
| Dimensione corpo JSON | Fino a 2 MB (express.json({ limit: '2mb' })) |
| Target per richiesta | 1–36 codici lingua |
| Elementi batch | 1–100 elementi per richiesta batch |
| Modelli | standard (default) o advanced (solo livelli a pagamento; vedi sotto) |
Quota token mensile (livello gratuito): Prima di chiamare il modello, l'API stima i token come circa ceil(content_length / 4) × (number_of_targets + 1) e, solo per il livello free, rifiuta la richiesta con 429 / token_limit_reached se la stima supera la quota mensile residua (FREE_TIER_MONTHLY_TOKENS, default 100000). I livelli a pagamento non sono bloccati da questo controllo preliminare in enforceTokenCap; l'uso viene comunque registrato.
Limiti di velocità: Quando Upstash Redis è configurato (UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN, e l'URL non contiene il segnaposto your-instance), si applicano limiti per minuto per livello: free 5, starter 30, growth 60, scale 120, enterprise illimitato. Al superamento del limite, la risposta è 429 con error: "rate_limit_reached". Se Redis non è configurato, il rate limiting viene saltato (vedi rateLimit.js).
Risposte con rate limiting riuscito possono includere X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
GET /health
Nessuna autenticazione.
Risposta 200
{
"status": "ok",
"timestamp": "2025-03-23T12:00:00.000Z"
}
GET /languages
Nessuna autenticazione.
Restituisce la lista canonica delle lingue supportate (codice, nome visualizzato, flag RTL). Ci sono 36 voci; i codici sono gli unici valori accettati in targets negli endpoint di traduzione.
Risposta 200
{
"languages": [
{ "code": "en", "name": "English", "rtl": false },
{ "code": "ar", "name": "Arabic", "rtl": true }
]
}
Fonte: api/utils/languages.js.
POST /translate
Richiede Authorization: Bearer <api_key>.
Traduce una singola stringa content in ogni lingua elencata in targets. Il modello restituisce un singolo oggetto JSON le cui chiavi sono esattamente i codici lingua richiesti e i cui valori sono stringhe tradotte (vedi formatPrompts.js).
Corpo della richiesta
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
content | stringa | Sì | Stringa non vuota da tradurre. |
targets | stringa[] | Sì | Array non vuoto di codici lingua validi (max 36). |
format | stringa | No | Uno tra plain, markdown, json, html. Se omesso, il formato è rilevato automaticamente da content. |
source | stringa | No | Suggerimento lingua sorgente per il modello; opzionale. |
model | stringa | No | standard (default) o advanced. advanced richiede un livello a pagamento (403 su free). |
Risposta 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 appaiono solo quando format è stato omesso e la rilevazione automatica è stata eseguita.
Esempio (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"]
}'
Esempio (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
Richiede Authorization: Bearer <api_key>.
Processa ogni elemento sequenzialmente (una chiamata modello per elemento). Se un elemento fallisce, l'API restituisce 500 e non restituisce risultati parziali per quella richiesta.
Corpo della richiesta
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
items | array | Sì | Ogni elemento: id (stringa), content (stringa), format opzionale. |
targets | stringa[] | Sì | Stesse regole di /translate. |
source | stringa | No | Suggerimento lingua sorgente opzionale. |
model | stringa | No | standard o advanced (stesse regole di /translate). |
Risposta 200
{
"results": [
{ "id": "welcome", "translations": { "fr": "...", "de": "..." } },
{ "id": "goodbye", "translations": { "fr": "...", "de": "..." } }
],
"usage": {
"total_tokens": 900,
"input_tokens": 400,
"output_tokens": 500,
"model": "standard"
}
}
Esempio
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
Richiede Authorization: Bearer <api_key>.
Mette in coda un lavoro di traduzione e restituisce immediatamente un job_id. La traduzione viene eseguita in background — nessun rischio di timeout HTTP indipendentemente dalla dimensione del contenuto. Effettua polling su GET /jobs/:id per il risultato.
Usa questo endpoint invece di POST /translate quando traduci documenti grandi (Markdown lungo, molte lingue target) dove la durata della richiesta potrebbe superare il timeout del client HTTP o del proxy.
Corpo della richiesta
| Campo | Tipo | Obbligatorio | Descrizione |
|---|---|---|---|
content | stringa | Sì | Stringa non vuota da tradurre. |
targets | stringa[] | Sì | Array non vuoto di codici lingua validi (max 36). |
format | stringa | No | Uno tra plain, markdown, json, html. Rilevato automaticamente se omesso. |
source | stringa | No | Suggerimento lingua sorgente; opzionale. |
model | stringa | No | standard (default) o advanced. |
Risposta 202
{
"job_id": "a1b2c3d4-...",
"status": "pending",
"created_at": "2025-03-23T12:00:00.000Z"
}
GET /jobs/:id
Richiede Authorization: Bearer <api_key>.
Effettua polling sullo stato di un lavoro inviato tramite POST /jobs. Effettua polling ogni 5–10 secondi. I lavori sono di proprietà dell'utente che li ha inviati — altri utenti ricevono 404.
Risposta (pending / processing)
{
"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 (in attesa di un worker) o processing (il worker l'ha preso). queue_position (basato su 1) indica quante lavorazioni in attesa o in elaborazione sono state create prima di questa — usalo per l'interfaccia di progresso. Ometto quando la query di conteggio fallisce.
Risposta (completed)
{
"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"
}
}
Risposta (failed)
{
"job_id": "a1b2c3d4-...",
"status": "failed",
"error": "Model returned invalid JSON"
}
Esempio (JavaScript)
const API = 'https://api.usepolylingo.com/v1'
const headers = {
'Authorization': `Bearer ${process.env.POLYLINGO_API_KEY}`,
'Content-Type': 'application/json',
}
// 1. Invia
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. Poll
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) }
// Opzionale: mostra progresso (queue_position è basato su 1, omesso se non in coda)
if (job.queue_position != null) console.log(`Queue position: ${job.queue_position}`)
}
GET /usage
Richiede Authorization: Bearer <api_key> (ricerca chiave standard — non il percorso interno solo bypass).
Restituisce l'uso dei token per il mese solare corrente per l'utente autenticato.
Risposta 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 sono null per enterprise (quota illimitata nel reporting).
Formati contenuto
Valori format supportati: plain, markdown, json, html.
| Formato | Preservato | Tradotto |
|---|---|---|
plain | Interruzioni di linea / paragrafi | Tutto il testo visibile |
markdown | Sintassi, link (URL invariato), codice fenced (letterale) | Prosa e testo link |
json | Chiavi, struttura, tipi non stringa | Solo valori stringa |
html | Tag e attributi | Nodi di testo e attributi appropriati (vedi prompt) |
RTL e direzione nella tua app
Per output plain e markdown, l'API restituisce solo testo tradotto — non aggiunge dir="rtl" o elementi wrapper. Imposta la direzione del testo nella tua UI (CSS direction, attributo dir di un elemento genitore, o layout i18n del tuo framework) quando visualizzi arabo, ebraico o persiano.
Per il formato html, il markup tradotto può includere dir="rtl" dove appropriato per target RTL; vedi formatPrompts.js e i test HTML in scripts/test-translation.js.
Risposte di errore
Gli errori sono JSON quando possibile:
{
"error": "invalid_request",
"message": "Dettaglio leggibile dall'uomo"
}
| HTTP | error | Quando |
|---|---|---|
| 400 | invalid_request | Campi body mancanti/non validi (es. content vuoto, targets errato) |
| 400 | invalid_format | format non nel set supportato |
| 400 | invalid_language | Codice sconosciuto in targets |
| 401 | invalid_api_key | Authorization mancante/malformato, chiave sconosciuta, chiave revocata |
| 403 | advanced_not_available | model: "advanced" su livello gratuito |
| 429 | token_limit_reached | Limite mensile livello gratuito superato (pre-controllo) |
| 429 | rate_limit_reached | Limite RPM per minuto (quando Redis abilitato) |
| 500 | translation_error | Errore modello/rete; sicuro riprovare |
| 404 | not_found | GET /jobs/:id — lavoro inesistente o appartenente ad altro utente |
| 500 | server_error | POST /jobs — fallito in coda; sicuro riprovare |
GET /usage può restituire 500 con messaggio generico se la query Supabase fallisce.
Modelli sottostanti (informativo)
L'API espone solo standard e advanced. Gli ID modello OpenAI reali sono configurati in api/utils/modelRouter.js e non sono restituiti nelle risposte API.