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ément | Valeur |
|---|---|
| Taille du corps JSON | Jusqu'à 2 Mo (express.json({ limit: '2mb' })) |
| Cibles par requête | 1–36 codes de langue |
| Éléments par lot | 1–100 éléments par requête batch |
| Modèles | standard (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
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
content | string | Oui | Chaîne non vide à traduire. |
targets | string[] | Oui | Tableau non vide de codes de langue valides (max 36). |
format | string | Non | L'un des plain, markdown, json, html. Si omis, le format est auto-détecté à partir de content. |
source | string | Non | Indice de langue source pour le modèle ; optionnel. |
model | string | Non | standard (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
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
items | array | Oui | Chaque élément : id (string), content (string), format optionnel. |
targets | string[] | Oui | Mêmes règles que /translate. |
source | string | Non | Indice de langue source optionnel. |
model | string | Non | standard 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
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
content | string | Oui | Chaîne non vide à traduire. |
targets | string[] | Oui | Tableau non vide de codes de langue valides (max 36). |
format | string | Non | L'un des plain, markdown, json, html. Auto-détecté si omis. |
source | string | Non | Indice de langue source ; optionnel. |
model | string | Non | standard (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.
| Format | Conservé | Traduit |
|---|---|---|
plain | Sauts de ligne / paragraphes | Tout le texte visible |
markdown | Syntaxe, liens (URL inchangée), code encadré (littéral) | Prose et texte des liens |
json | Clés, structure, types non chaîne | Valeurs chaînes uniquement |
html | Balises et attributs | Nœ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"
}
| HTTP | error | Quand |
|---|---|---|
| 400 | invalid_request | Champs du corps manquants/invalides (ex. content vide, targets incorrects) |
| 400 | invalid_format | format non dans l'ensemble supporté |
| 400 | invalid_language | Code inconnu dans targets |
| 401 | invalid_api_key | Authorization manquant/malformé, clé inconnue, clé révoquée |
| 403 | advanced_not_available | model: "advanced" sur niveau gratuit |
| 429 | token_limit_reached | Plafond mensuel du niveau gratuit dépassé (pré-vérification) |
| 429 | rate_limit_reached | Limite RPM par minute (quand Redis activé) |
| 500 | translation_error | Échec modèle/réseau ; sécurisé pour réessayer |
| 404 | not_found | GET /jobs/:id — travail inexistant ou appartenant à un autre utilisateur |
| 500 | server_error | POST /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.