Ratenbegrenzungen & Fehler
Aktuelle Tarifkontingente, Anfragelimits pro Minute, 429-Verhalten und Backend-Fehlercodes.
Kontingent nach Tarif
Monatliche Extraktionskontingente werden am ersten Tag jedes UTC-Monats zurueckgesetzt. Das Kontingent wird gezaehlt, nachdem eine erfolgreiche Extraktion finalisiert und gespeichert wurde.
| Tarif | Monatliche Extraktionen | Anfragelimit | Locale-Hinweise | Pro-Funktionen |
|---|---|---|---|---|
| Free | 100 | 10 req/min | nur auto | Nein |
| Hobby | 500 | 60 req/min | auto, en, de, fr, es, nl | Nein |
| Starter | 2,000 | 300 req/min | auto, en, de, fr, es, nl | Nein |
| Pro | 10,000 | 600 req/min | auto, en, de, fr, es, nl | Ja |
| Business | 50,000 | 1200 req/min | auto, en, de, fr, es, nl | Ja |
Pro-Funktionen sind ZUGFeRD-Verarbeitung, DATEV-Export, Duplikaterkennung, Anomalieerkennung, Kreditoren-Analysen und Feld-Bounding-Boxes.
Umgang mit 429
Die API kann aus zwei Gruenden 429 Too Many Requests zurueckgeben:
| Fehlercode | Bedeutung | Retry-Header |
|---|---|---|
rate_limit_exceeded | Das gleitende Minutenlimit fuer API-Schluessel oder Dashboard-Nutzer wurde ueberschritten | Retry-After: 60 |
quota_exceeded | Das monatliche Extraktionskontingent des Tarifs ist erschoepft | Retry-After enthaelt den ersten Tag des naechsten UTC-Monats |
Nutzen Sie exponentielles Backoff fuer rate_limit_exceeded. Stoppen Sie Extraktionstraffic oder wechseln Sie den Tarif bei quota_exceeded.
import random
import time
import httpx
def extract_with_retry(text: str, api_key: str, max_retries: int = 5) -> dict:
delay = 1.0
for _ in range(max_retries):
response = httpx.post(
"https://api.parsurai.com/v1/extract",
headers={"X-API-Key": api_key},
json={"text": text, "locale": "auto"},
)
if response.status_code != 429:
response.raise_for_status()
return response.json()
body = response.json()
if body.get("error", {}).get("code") == "quota_exceeded":
raise RuntimeError(body["error"]["message"])
retry_after = int(response.headers.get("Retry-After", "60"))
time.sleep(retry_after + random.uniform(0, delay))
delay = min(delay * 2, 60)
raise RuntimeError("Max retries exceeded")
async function extractWithRetry(text, apiKey, maxRetries = 5) {
let delay = 1000;
for (let attempt = 0; attempt < maxRetries; attempt += 1) {
const res = await fetch('https://api.parsurai.com/v1/extract', {
method: 'POST',
headers: { 'X-API-Key': apiKey, 'Content-Type': 'application/json' },
body: JSON.stringify({ text, locale: 'auto' }),
});
if (res.status !== 429) {
if (!res.ok) throw new Error(`API error: ${res.status}`);
return res.json();
}
const body = await res.json();
if (body.error?.code === 'quota_exceeded') {
throw new Error(body.error.message);
}
const retryAfter = Number(res.headers.get('Retry-After') ?? '60') * 1000;
await new Promise((resolve) => setTimeout(resolve, retryAfter + Math.random() * delay));
delay = Math.min(delay * 2, 60000);
}
throw new Error('Max retries exceeded');
}
Umgang mit 503 und 504
Zwei weitere Antworten beschreiben den Dienst statt Ihrer Anfrage. Beide koennen gefahrlos wiederholt werden:
| Fehlercode | Status | Bedeutung |
|---|---|---|
extraction_capacity_exhausted | 503 | Alle PDF-Extraktionsplaetze des Prozesses sind belegt. Es wurde keine Arbeit begonnen, ein erneuter Versuch kostet also nichts. |
pdf_extraction_timeout | 504 | Die Textextraktion hat ihr 20-Sekunden-Budget ueberschritten. |
Keine der beiden enthaelt einen Retry-After-Header. Nutzen Sie dasselbe exponentielle Backoff wie oben, beginnend bei etwa einer Sekunde. Wiederholen Sie ein 504 nur wenige Male: Ein Dokument, das wiederholt in den Timeout laeuft, ist fuer diesen Endpunkt zu teuer -- teilen Sie es auf.
Fehlerformat
Alle Anwendungsfehler verwenden dieselbe JSON-Huelle:
{
"error": {
"code": "quota_exceeded",
"message": "Monatliches Extraktionskontingent ueberschritten. Bitte upgraden Sie Ihren Plan, um fortzufahren.",
"upgrade_url": "https://invoice-extractor.com/pricing"
}
}
upgrade_url ist nur bei Kontingent- und Tarifgating-Fehlern vorhanden.
Vollstaendige Fehlercode-Referenz
| Code | HTTP Status | Description |
|---|---|---|
| invalid_api_key | 401 | Ungueltiger oder inaktiver API-Schluessel |
| inactive_subscription | 402 | Abonnement ist inaktiv |
| feature_not_in_plan | 402 | Funktion erfordert einen hoeheren Tarif |
| quota_exceeded | 429 | Monatliches Extraktionskontingent erschoepft |
| rate_limit_exceeded | 429 | Gleitendes Zeitfenster fuer Anfragen ueberschritten |
| validation_error | 422 | Anfragevalidierung fehlgeschlagen |
| extraction_failed | 502 | Fehler im Extraktionsdienst |
| upload_too_large | 413 | Hochgeladene Datei ueberschreitet das Limit dieses Endpunkts: 25 MB fuer Extraktion und Split, 10 MB fuer Klassifizierung und Zuschnitt, 5 MB fuer die oeffentliche Demo. Jeder zu grosse Upload liefert diesen einen Code. |
| artifact_too_large | 413 | AP-Workflow-Artefakt ueberschreitet das Limit von 25 MB |
| upload_not_authorized | 422 | Der Upload konnte nicht autorisiert werden. Angegebener Dateityp oder angegebene Groesse liegen ausserhalb dessen, was dieser Endpunkt akzeptiert; es wurden keine Daten gesendet. |
| pdf_too_many_pages | 422 | PDF ueberschreitet das Seitenlimit dieses Pfads: 30 fuer Standard- und Demo-Extraktion, 10 fuer Pro AP, bis zu 100 fuer Business AP. Kein Endpunkt darf die Obergrenze von 100 Seiten ueberschreiten. |
| unsupported_einvoice_profile | 422 | Wohlgeformtes E-Rechnungs-XML mit nicht unterstuetztem UBL/CII-Profil |
| pdf_unreadable | 422 | PDF ist beschaedigt, verschluesselt oder passwortgeschuetzt |
| image_too_large_to_process | 422 | Bild ueberschreitet das Dekodierlimit von 100 Megapixeln bei /v1/documents/crop |
| einvoice_attachment_too_large | 422 | Eingebetteter E-Rechnungs-Anhang ueberschreitet 10 MB |
| extraction_capacity_exhausted | 503 | Extraktionskapazitaet ist momentan ausgelastet; mit Backoff wiederholen |
| pdf_extraction_timeout | 504 | PDF-Textextraktion hat das 20-Sekunden-Budget ueberschritten; mit Backoff wiederholen |
| pdf_no_extractable_text | 422 | Gescanntes PDF ohne Textebene, das auch OCR nicht lesen konnte; Seite als Bild hochladen |
| ocr_unavailable | 503 | Texterkennung war fuer ein gescanntes PDF noetig und ist momentan nicht verfuegbar; mit Backoff wiederholen |
| legal_reacceptance_required | 428 | Aktualisierte AGB oder Datenschutzerklaerung muessen vom Kontoinhaber akzeptiert werden, bevor API-Aufrufe fortgesetzt werden |
| email_verification_required | 428 | Die E-Mail-Adresse muss vom Kontoinhaber bestaetigt werden, bevor API-Aufrufe fortgesetzt werden |
| internal_error | 500 | Unerwarteter Serverfehler |
Monitoring-Empfehlungen
- Fragen Sie
GET /v1/usageab, umcalls_this_month,quota,plan_nameundresets_onzu lesen. - Aktivieren Sie Kontingentwarnungen unter Dashboard > Einstellungen > Benachrichtigungen.
- Alarmieren Sie, wenn
calls_this_month / quotaIhren internen Grenzwert ueberschreitet.
Entscheidungen bei Tarif-Gating
Einige Anfragen sind syntaktisch gueltig, werden aber vom aktuellen Tarif blockiert. Diese Antworten verwenden 402 mit feature_not_in_plan und verbrauchen kein Extraktionskontingent.
| Funktion | Mindesttarif | Hinweise |
|---|---|---|
Explizite Locale-Hinweise wie de, fr, es oder nl | Hobby | Free sollte locale: "auto" senden |
| ZUGFeRD-Verarbeitung | Pro | POST /v1/extract/zugferd |
| Duplikat- und Anomaliepruefung | Pro | /v1/intelligence/* |
| Kreditoren-Analysen | Pro | GET /v1/analytics/vendors |
| DATEV-Export | Pro | POST /v1/exports/datev |
| Feld-Bounding-Boxes | Pro | Nur bei PDF-/Bild-Uploads, wenn OCR-Spans verfuegbar sind |
Usage-Antwortbeispiel
curl https://api.parsurai.com/v1/usage \
-H "X-API-Key: $PARSURAI_API_KEY"
{
"calls_this_month": 318,
"quota": 2000,
"plan_name": "starter",
"resets_on": "2026-07-01"
}