Référence API

Ce document correspond au comportement de l'application Express dans api/app.js et des gestionnaires de routes sous api/routes/.

Limites et comportement

ÉlémentValeur
Taille du corps JSONJusqu'à 2 Mo (express.json({ limit: '2mb' }))
Cibles par requête1–36 codes de langue
Éléments par lot1–100 éléments par requête batch
Modèlesstandard (par défaut) ou advanced (uniquement niveaux payants ; voir ci-dessous)

Quota mensuel de jetons (niveau gratuit) : Avant d'appeler le modèle, l'API estime les jetons approximativement comme ceil(content_length / 4) × (number_of_targets + 1) et, uniquement pour le niveau free, rejette la requête avec 429 / token_limit_reached si l'estimation dépasse la subvention mensuelle restante (FREE_TIER_MONTHLY_TOKENS, par défaut 100000). Les niveaux payants ne sont pas bloqués par cette pré-vérification dans enforceTokenCap ; l'utilisation est toujours enregistrée.

Limites de débit : Lorsque Upstash Redis est configuré (UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN, et que l'URL ne contient pas le placeholder your-instance), des limites par minute s'appliquent selon le niveau : free 5, starter 30, growth 60, scale 120, enterprise illimité. En cas de dépassement, la réponse est 429 avec error: "rate_limit_reached". Si Redis n'est pas configuré, la limitation de débit est ignorée (voir rateLimit.js).

Les réponses réussies avec limitation de débit peuvent inclure X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.


GET /health

Pas d'authentification.

Réponse 200

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

GET /languages

Pas d'authentification.

Retourne la liste canonique des langues supportées (code, nom affiché, indicateur RTL). Il y a 36 entrées ; les codes sont les seules valeurs acceptées dans targets sur les points de terminaison de traduction.

Réponse 200

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

Source : api/utils/languages.js.


POST /translate

Nécessite Authorization: Bearer <api_key>.

Traduit une seule chaîne content dans chaque langue listée dans targets. Le modèle retourne un objet JSON unique dont les clés sont exactement les codes de langue demandés et dont les valeurs sont des chaînes traduites (voir formatPrompts.js).

Corps de la requête

ChampTypeObligatoireDescription
contentstringOuiChaîne non vide à traduire.
targetsstring[]OuiTableau non vide de codes de langue valides (max 36).
formatstringNonL'un des plain, markdown, json, html. Si omis, le format est auto-détecté à partir de content.
sourcestringNonIndice de langue source pour le modèle ; optionnel.
modelstringNonstandard (par défaut) ou advanced. advanced nécessite un niveau payant (403 sur free).

Réponse 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 et detection_confidence apparaissent uniquement lorsque format a été omis et que la détection automatique a été effectuée.

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

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

Nécessite Authorization: Bearer <api_key>.

Traite chaque élément séquentiellement (un appel modèle par élément). Si un élément échoue, l'API retourne 500 et ne renvoie pas de résultats partiels pour cette requête.

Corps de la requête

ChampTypeObligatoireDescription
itemsarrayOuiChaque élément : id (string), content (string), format optionnel.
targetsstring[]OuiMêmes règles que /translate.
sourcestringNonIndice de langue source optionnel.
modelstringNonstandard ou advanced (mêmes règles que /translate).

Réponse 200

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

Exemple

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

Nécessite Authorization: Bearer <api_key>.

Met en file d'attente un travail de traduction et retourne immédiatement un job_id. La traduction s'exécute en arrière-plan — aucun risque de timeout HTTP quelle que soit la taille du contenu. Interrogez GET /jobs/:id pour le résultat.

Utilisez ce point de terminaison au lieu de POST /translate lors de la traduction de documents volumineux (Markdown long, nombreuses langues cibles) où la durée de la requête pourrait dépasser le timeout de votre client HTTP ou proxy.

Corps de la requête

ChampTypeObligatoireDescription
contentstringOuiChaîne non vide à traduire.
targetsstring[]OuiTableau non vide de codes de langue valides (max 36).
formatstringNonL'un des plain, markdown, json, html. Auto-détecté si omis.
sourcestringNonIndice de langue source ; optionnel.
modelstringNonstandard (par défaut) ou advanced.

Réponse 202

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

GET /jobs/:id

Nécessite Authorization: Bearer <api_key>.

Interroge le statut d'un travail soumis via POST /jobs. Interrogez toutes les 5–10 secondes. Les travaux appartiennent à l'utilisateur qui les a soumis — les autres utilisateurs reçoivent 404.

Réponse (en attente / en traitement)

{
  "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 est pending (en attente d'un worker) ou processing (le worker l'a pris en charge). queue_position (base 1) indique combien de travaux en attente ou en traitement ont été créés strictement avant celui-ci — utilisez-le pour l'interface utilisateur de progression. Omit si la requête de comptage échoue.

Réponse (terminé)

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

Réponse (échoué)

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

Exemple (JavaScript)

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

// 1. Soumettre
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. Interroger
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) }
  // Optionnel : afficher la progression (queue_position est basé sur 1, omis si non en file)
  if (job.queue_position != null) console.log(`Queue position: ${job.queue_position}`)
}

GET /usage

Nécessite Authorization: Bearer <api_key> (recherche de clé standard — pas le chemin interne réservé au contournement).

Retourne l'utilisation des jetons pour le mois calendaire en cours pour l'utilisateur authentifié.

Réponse 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 et tokens_remaining sont null pour enterprise (allocation illimitée dans les rapports).


Formats de contenu

Valeurs format supportées : plain, markdown, json, html.

FormatConservéTraduit
plainSauts de ligne / paragraphesTout le texte visible
markdownSyntaxe, liens (URL inchangée), code encadré (littéral)Prose et texte des liens
jsonClés, structure, types non chaîneValeurs chaînes uniquement
htmlBalises et attributsNœuds texte et attributs appropriés (voir prompts)

RTL et direction dans votre application

Pour la sortie plain et markdown, l'API retourne uniquement le texte traduit — elle n'ajoute pas dir="rtl" ni d'éléments enveloppants. Définissez la direction du texte dans votre UI (CSS direction, attribut dir d'un élément parent ou mise en page i18n de votre framework) lors de l'affichage de l'arabe, de l'hébreu ou du persan.

Pour le format html, le balisage traduit peut inclure dir="rtl" lorsque c'est approprié pour les cibles RTL ; voir formatPrompts.js et les tests HTML dans scripts/test-translation.js.


Réponses d'erreur

Les erreurs sont au format JSON quand c'est possible :

{
  "error": "invalid_request",
  "message": "Détail lisible par un humain"
}
HTTPerrorQuand
400invalid_requestChamps du corps manquants/invalides (ex. content vide, targets incorrects)
400invalid_formatformat non dans l'ensemble supporté
400invalid_languageCode inconnu dans targets
401invalid_api_keyAuthorization manquant/malformé, clé inconnue, clé révoquée
403advanced_not_availablemodel: "advanced" sur niveau gratuit
429token_limit_reachedPlafond mensuel du niveau gratuit dépassé (pré-vérification)
429rate_limit_reachedLimite RPM par minute (quand Redis activé)
500translation_errorÉchec modèle/réseau ; sécurisé pour réessayer
404not_foundGET /jobs/:id — travail inexistant ou appartenant à un autre utilisateur
500server_errorPOST /jobs — échec de mise en file ; sécuritaire pour réessayer

GET /usage peut retourner 500 avec un message générique si la requête Supabase échoue.


Modèles sous-jacents (informatif)

L'API expose uniquement standard et advanced. Les identifiants réels des modèles OpenAI sont configurés dans api/utils/modelRouter.js et ne sont pas retournés dans les réponses API.

Référence API | PolyLingo | PolyLingo