Limites de debit & erreurs
Quotas actuels par offre, limites par minute, comportement 429 et codes d'erreur backend.
Quota par offre
Les quotas mensuels d'extraction sont remis a zero le premier jour de chaque mois UTC. Le quota est compte apres finalisation et enregistrement d'une extraction reussie.
| Offre | Extractions mensuelles | Limite de requetes | Indices de locale | Fonctions Pro |
|---|---|---|---|---|
| Free | 100 | 10 req/min | auto uniquement | Non |
| Hobby | 500 | 60 req/min | auto, en, de, fr, es, nl | Non |
| Starter | 2,000 | 300 req/min | auto, en, de, fr, es, nl | Non |
| Pro | 10,000 | 600 req/min | auto, en, de, fr, es, nl | Oui |
| Business | 50,000 | 1200 req/min | auto, en, de, fr, es, nl | Oui |
Les fonctions Pro sont le parsing ZUGFeRD, l'export DATEV, la detection de doublons, la detection d'anomalies, les analyses fournisseurs et les bounding boxes par champ.
Gerer les 429
L'API peut renvoyer 429 Too Many Requests pour deux raisons:
| Code d'erreur | Signification | Header de retry |
|---|---|---|
rate_limit_exceeded | La limite glissante par minute pour la cle API ou l'utilisateur dashboard est depassee | Retry-After: 60 |
quota_exceeded | Le quota mensuel d'extractions de l'offre est epuise | Retry-After contient le premier jour du mois UTC suivant |
Utilisez un backoff exponentiel pour rate_limit_exceeded, et arretez le trafic d'extraction ou changez d'offre pour 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');
}
Gerer les 503 et 504
Deux autres reponses decrivent le service et non votre requete. Les deux peuvent etre reessayees sans risque:
| Code d'erreur | Statut | Signification |
|---|---|---|
extraction_capacity_exhausted | 503 | Tous les emplacements d'extraction PDF du processus sont occupes. Aucun travail n'a demarre, un nouvel essai ne coute donc rien. |
pdf_extraction_timeout | 504 | L'extraction du texte a depasse son budget de 20 secondes. |
Aucune des deux ne porte d'en-tete Retry-After. Reutilisez le backoff exponentiel ci-dessus en partant d'environ une seconde. Ne reessayez un 504 que quelques fois: un document qui expire de maniere repetee est trop couteux pour cet endpoint, la solution est de le decouper.
Format d'erreur
Toutes les erreurs applicatives utilisent la meme enveloppe JSON:
{
"error": {
"code": "quota_exceeded",
"message": "Quota d'extractions mensuelles depasse. Veuillez mettre a niveau votre plan pour continuer.",
"upgrade_url": "https://invoice-extractor.com/pricing"
}
}
upgrade_url existe uniquement pour les erreurs de quota et de restriction d'offre.
Reference complete des codes d'erreur
| Code | HTTP Status | Description |
|---|---|---|
| invalid_api_key | 401 | Cle API invalide ou inactive |
| inactive_subscription | 402 | Abonnement inactif |
| feature_not_in_plan | 402 | Fonction reservee a une offre superieure |
| quota_exceeded | 429 | Quota mensuel d'extractions depasse |
| rate_limit_exceeded | 429 | Limite de requetes par fenetre glissante depassee |
| validation_error | 422 | Validation de la requete echouee |
| extraction_failed | 502 | Erreur du service d'extraction |
| upload_too_large | 413 | Le fichier envoye depasse la limite de ce point de terminaison: 25 Mo pour l'extraction et le decoupage, 10 Mo pour la classification et le recadrage, 5 Mo pour la demonstration publique. Tout envoi trop volumineux renvoie ce code. |
| artifact_too_large | 413 | L'artefact du workflow AP depasse la limite de 25 Mo |
| upload_not_authorized | 422 | L'envoi n'a pas pu etre autorise. Le type ou la taille de fichier declare depasse ce que ce point de terminaison accepte; aucune donnee n'a ete envoyee. |
| pdf_too_many_pages | 422 | Le PDF depasse la limite de pages de ce chemin: 30 pour l'extraction standard et demo, 10 pour Pro AP, jusqu'a 100 pour Business AP. Aucun endpoint ne peut depasser le plafond de 100 pages. |
| unsupported_einvoice_profile | 422 | XML e-facture bien forme dont le profil UBL/CII n'est pas pris en charge |
| pdf_unreadable | 422 | PDF corrompu, chiffre ou protege par mot de passe |
| image_too_large_to_process | 422 | L'image depasse la limite de decodage de 100 megapixels sur /v1/documents/crop |
| einvoice_attachment_too_large | 422 | La piece jointe e-facture integree depasse 10 MB |
| extraction_capacity_exhausted | 503 | Capacite d'extraction momentanement saturee; reessayez avec backoff |
| pdf_extraction_timeout | 504 | L'extraction du texte PDF a depasse le budget de 20 secondes; reessayez avec backoff |
| pdf_no_extractable_text | 422 | PDF scanne sans couche de texte, illisible aussi par l'OCR; importez la page comme image |
| ocr_unavailable | 503 | La reconnaissance de texte, requise pour un PDF scanne, est momentanement indisponible; reessayez avec backoff |
| legal_reacceptance_required | 428 | Les conditions generales ou la politique de confidentialite mises a jour doivent etre acceptees par le titulaire du compte avant la reprise des appels API |
| email_verification_required | 428 | L'adresse e-mail doit etre verifiee par le titulaire du compte avant la reprise des appels API |
| internal_error | 500 | Erreur serveur inattendue |
Recommandations de suivi
- Appelez
GET /v1/usagepour lirecalls_this_month,quota,plan_nameetresets_on. - Activez les alertes de quota dans Dashboard > Parametres > Notifications.
- Alertez quand
calls_this_month / quotadepasse votre seuil interne.
Decisions de restriction par offre
Certaines requetes sont syntaxiquement valides mais bloquees par l'offre courante. Elles renvoient 402 avec feature_not_in_plan et ne consomment pas le quota d'extraction.
| Fonction | Offre minimale | Notes |
|---|---|---|
Indices de locale explicites comme de, fr, es ou nl | Hobby | Free doit envoyer locale: "auto" |
| Parsing ZUGFeRD | Pro | POST /v1/extract/zugferd |
| Controles de doublon et d'anomalie | Pro | /v1/intelligence/* |
| Analyses fournisseurs | Pro | GET /v1/analytics/vendors |
| Export DATEV | Pro | POST /v1/exports/datev |
| Bounding boxes de champs | Pro | Seulement pour uploads PDF/image quand les spans OCR existent |
Exemple de reponse usage
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"
}