API-Referenz
Dieses Dokument entspricht dem Verhalten der Express-App in api/app.js und den Routen-Handlern unter api/routes/.
Limits und Verhalten
| Element | Wert |
|---|---|
| JSON-Body-Größe | Bis zu 2 MB (express.json({ limit: '2mb' })) |
| Ziele pro Anfrage | 1–36 Sprachcodes |
| Batch-Elemente | 1–100 Elemente pro Batch-Anfrage |
| Modelle | standard (Standard) oder advanced (nur kostenpflichtige Stufen; siehe unten) |
Monatliches Token-Limit (kostenlose Stufe): Vor dem Aufruf des Modells schätzt die API Tokens ungefähr als ceil(content_length / 4) × (number_of_targets + 1) und lehnt für die free Stufe Anfragen mit 429 / token_limit_reached ab, wenn die Schätzung das verbleibende monatliche Kontingent (FREE_TIER_MONTHLY_TOKENS, Standard 100000) überschreiten würde. Kostenpflichtige Stufen werden von dieser Vorprüfung in enforceTokenCap nicht blockiert; die Nutzung wird weiterhin protokolliert.
Ratenbegrenzungen: Wenn Upstash Redis konfiguriert ist (UPSTASH_REDIS_REST_URL / UPSTASH_REDIS_REST_TOKEN, und die URL enthält nicht den Platzhalter your-instance), gelten pro Minute Limits je nach Stufe: free 5, starter 30, growth 60, scale 120, enterprise unbegrenzt. Bei Erreichen des Limits ist die Antwort 429 mit error: "rate_limit_reached". Wenn Redis nicht konfiguriert ist, wird die Ratenbegrenzung übersprungen (siehe rateLimit.js).
Erfolgreiche ratenbegrenzte Antworten können X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset enthalten.
GET /health
Keine Authentifizierung.
Antwort 200
{
"status": "ok",
"timestamp": "2025-03-23T12:00:00.000Z"
}
GET /languages
Keine Authentifizierung.
Gibt die kanonische Liste der unterstützten Sprachen zurück (Code, Anzeigename, RTL-Flag). Es gibt 36 Einträge; Codes sind die einzigen akzeptierten Werte in targets bei Übersetzungsendpunkten.
Antwort 200
{
"languages": [
{ "code": "en", "name": "English", "rtl": false },
{ "code": "ar", "name": "Arabic", "rtl": true }
]
}
Quelle: api/utils/languages.js.
POST /translate
Erfordert Authorization: Bearer <api_key>.
Übersetzt einen einzelnen content-String in jede Sprache, die in targets aufgeführt ist. Das Modell gibt ein einzelnes JSON-Objekt zurück, dessen Schlüssel genau die angeforderten Sprachcodes sind und dessen Werte übersetzte Strings sind (siehe formatPrompts.js).
Anfrage-Body
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
content | string | Ja | Nicht-leerer String zum Übersetzen. |
targets | string[] | Ja | Nicht-leeres Array gültiger Sprachcodes (max. 36). |
format | string | Nein | Eines von plain, markdown, json, html. Wenn ausgelassen, wird das Format automatisch erkannt aus content. |
source | string | Nein | Quellsprache-Hinweis für das Modell; optional. |
model | string | Nein | standard (Standard) oder advanced. advanced erfordert eine kostenpflichtige Stufe (403 bei kostenloser Stufe). |
Antwort 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 und detection_confidence erscheinen nur, wenn format ausgelassen wurde und die automatische Erkennung lief.
Beispiel (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"]
}'
Beispiel (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
Erfordert Authorization: Bearer <api_key>.
Verarbeitet jedes Element nacheinander (ein Modellaufruf pro Element). Wenn ein Element fehlschlägt, gibt die API 500 zurück und liefert keine Teilergebnisse für diese Anfrage.
Anfrage-Body
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
items | Array | Ja | Jedes Element: id (string), content (string), optional format. |
targets | string[] | Ja | Gleiche Regeln wie bei /translate. |
source | string | Nein | Optionaler Quellsprache-Hinweis. |
model | string | Nein | standard oder advanced (gleiche Regeln wie bei /translate). |
Antwort 200
{
"results": [
{ "id": "welcome", "translations": { "fr": "...", "de": "..." } },
{ "id": "goodbye", "translations": { "fr": "...", "de": "..." } }
],
"usage": {
"total_tokens": 900,
"input_tokens": 400,
"output_tokens": 500,
"model": "standard"
}
}
Beispiel
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
Erfordert Authorization: Bearer <api_key>.
Stellt einen Übersetzungsauftrag in die Warteschlange und gibt sofort eine job_id zurück. Die Übersetzung läuft im Hintergrund — kein HTTP-Timeout-Risiko, unabhängig von der Inhaltsgröße. Polling von GET /jobs/:id für das Ergebnis.
Verwenden Sie diesen Endpunkt anstelle von POST /translate, wenn Sie große Dokumente übersetzen (langer Markdown, viele Zielsprachen), bei denen die Anfragedauer Ihre HTTP-Client- oder Proxy-Timeouts überschreiten könnte.
Anfrage-Body
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
content | string | Ja | Nicht-leerer String zum Übersetzen. |
targets | string[] | Ja | Nicht-leeres Array gültiger Sprachcodes (max. 36). |
format | string | Nein | Eines von plain, markdown, json, html. Automatisch erkannt, wenn ausgelassen. |
source | string | Nein | Quellsprache-Hinweis; optional. |
model | string | Nein | standard (Standard) oder advanced. |
Antwort 202
{
"job_id": "a1b2c3d4-...",
"status": "pending",
"created_at": "2025-03-23T12:00:00.000Z"
}
GET /jobs/:id
Erfordert Authorization: Bearer <api_key>.
Fragt den Status eines über POST /jobs eingereichten Jobs ab. Poll alle 5–10 Sekunden. Jobs gehören dem einreichenden Benutzer — andere Benutzer erhalten 404.
Antwort (ausstehend / in Bearbeitung)
{
"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 ist pending (wartet auf Worker) oder processing (Worker hat ihn übernommen). queue_position (1-basiert) gibt an, wie viele ausstehende oder bearbeitete Jobs streng vor diesem erstellt wurden — verwenden Sie es für Fortschritts-UI. Wird weggelassen, wenn die Zählabfrage fehlschlägt.
Antwort (abgeschlossen)
{
"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"
}
}
Antwort (fehlgeschlagen)
{
"job_id": "a1b2c3d4-...",
"status": "failed",
"error": "Model returned invalid JSON"
}
Beispiel (JavaScript)
const API = 'https://api.usepolylingo.com/v1'
const headers = {
'Authorization': `Bearer ${process.env.POLYLINGO_API_KEY}`,
'Content-Type': 'application/json',
}
// 1. Absenden
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. Abfragen
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) }
// Optional: Fortschritt anzeigen (queue_position ist 1-basiert, wird weggelassen, wenn nicht in der Warteschlange)
if (job.queue_position != null) console.log(`Queue position: ${job.queue_position}`)
}
GET /usage
Erfordert Authorization: Bearer <api_key> (Standard-Schlüsselsuche — nicht der interne Bypass-Only-Pfad).
Gibt die Token-Nutzung für den aktuellen Kalendermonat für den authentifizierten Benutzer zurück.
Antwort 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 und tokens_remaining sind null für enterprise (unbegrenztes Kontingent in Berichten).
Inhaltsformate
Unterstützte format-Werte: plain, markdown, json, html.
| Format | Erhalten | Übersetzt |
|---|---|---|
plain | Zeilenumbrüche / Absätze | Gesamter sichtbarer Text |
markdown | Syntax, Links (URL unverändert), eingerahmter Code (wörtlich) | Prosa und Linktext |
json | Schlüssel, Struktur, Nicht-String-Typen | Nur String-Werte |
html | Tags und Attribute | Textknoten und passende Attribute (siehe Prompts) |
RTL und Richtung in Ihrer App
Für plain und markdown gibt die API nur den übersetzten Text zurück — sie fügt kein dir="rtl" oder Wrapper-Elemente hinzu. Stellen Sie die Textrichtung in Ihrer UI ein (CSS direction, dir-Attribut eines Elternelements oder das i18n-Layout Ihres Frameworks), wenn Sie Arabisch, Hebräisch oder Persisch anzeigen.
Für das html-Format kann die übersetzte Markup dir="rtl" enthalten, wo es für RTL-Ziele angemessen ist; siehe formatPrompts.js und die HTML-Tests in scripts/test-translation.js.
Fehlerantworten
Fehler sind JSON, wenn möglich:
{
"error": "invalid_request",
"message": "Für Menschen lesbare Details"
}
| HTTP | error | Wann |
|---|---|---|
| 400 | invalid_request | Fehlende/ungültige Body-Felder (z.B. leerer content, ungültige targets) |
| 400 | invalid_format | format nicht in der unterstützten Menge |
| 400 | invalid_language | Unbekannter Code in targets |
| 401 | invalid_api_key | Fehlendes/fehlerhaftes Authorization, unbekannter Schlüssel, widerrufener Schlüssel |
| 403 | advanced_not_available | model: "advanced" in der kostenlosen Stufe |
| 429 | token_limit_reached | Monatliches Limit der kostenlosen Stufe würde überschritten (Vorprüfung) |
| 429 | rate_limit_reached | RPM-Limit pro Minute (wenn Redis aktiviert) |
| 500 | translation_error | Modell-/Netzwerkfehler; sicher zum erneuten Versuch |
| 404 | not_found | GET /jobs/:id — Job existiert nicht oder gehört einem anderen Benutzer |
| 500 | server_error | POST /jobs — konnte nicht in die Warteschlange gestellt werden; sicher zum erneuten Versuch |
GET /usage kann 500 mit einer generischen Meldung zurückgeben, wenn die Supabase-Abfrage fehlschlägt.
Unterliegende Modelle (informativ)
Die API stellt nur standard und advanced bereit. Tatsächliche OpenAI-Modell-IDs sind in api/utils/modelRouter.js konfiguriert und werden nicht in API-Antworten zurückgegeben.