Documentation

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.

OffreExtractions mensuellesLimite de requetesIndices de localeFonctions Pro
Free10010 req/minauto uniquementNon
Hobby50060 req/minauto, en, de, fr, es, nlNon
Starter2,000300 req/minauto, en, de, fr, es, nlNon
Pro10,000600 req/minauto, en, de, fr, es, nlOui
Business50,0001200 req/minauto, en, de, fr, es, nlOui

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'erreurSignificationHeader de retry
rate_limit_exceededLa limite glissante par minute pour la cle API ou l'utilisateur dashboard est depasseeRetry-After: 60
quota_exceededLe quota mensuel d'extractions de l'offre est epuiseRetry-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'erreurStatutSignification
extraction_capacity_exhausted503Tous les emplacements d'extraction PDF du processus sont occupes. Aucun travail n'a demarre, un nouvel essai ne coute donc rien.
pdf_extraction_timeout504L'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

CodeHTTP StatusDescription
invalid_api_key401Cle API invalide ou inactive
inactive_subscription402Abonnement inactif
feature_not_in_plan402Fonction reservee a une offre superieure
quota_exceeded429Quota mensuel d'extractions depasse
rate_limit_exceeded429Limite de requetes par fenetre glissante depassee
validation_error422Validation de la requete echouee
extraction_failed502Erreur du service d'extraction
upload_too_large413Le 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_large413L'artefact du workflow AP depasse la limite de 25 Mo
upload_not_authorized422L'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_pages422Le 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_profile422XML e-facture bien forme dont le profil UBL/CII n'est pas pris en charge
pdf_unreadable422PDF corrompu, chiffre ou protege par mot de passe
image_too_large_to_process422L'image depasse la limite de decodage de 100 megapixels sur /v1/documents/crop
einvoice_attachment_too_large422La piece jointe e-facture integree depasse 10 MB
extraction_capacity_exhausted503Capacite d'extraction momentanement saturee; reessayez avec backoff
pdf_extraction_timeout504L'extraction du texte PDF a depasse le budget de 20 secondes; reessayez avec backoff
pdf_no_extractable_text422PDF scanne sans couche de texte, illisible aussi par l'OCR; importez la page comme image
ocr_unavailable503La reconnaissance de texte, requise pour un PDF scanne, est momentanement indisponible; reessayez avec backoff
legal_reacceptance_required428Les 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_required428L'adresse e-mail doit etre verifiee par le titulaire du compte avant la reprise des appels API
internal_error500Erreur serveur inattendue

Recommandations de suivi

  • Appelez GET /v1/usage pour lire calls_this_month, quota, plan_name et resets_on.
  • Activez les alertes de quota dans Dashboard > Parametres > Notifications.
  • Alertez quand calls_this_month / quota depasse 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.

FonctionOffre minimaleNotes
Indices de locale explicites comme de, fr, es ou nlHobbyFree doit envoyer locale: "auto"
Parsing ZUGFeRDProPOST /v1/extract/zugferd
Controles de doublon et d'anomaliePro/v1/intelligence/*
Analyses fournisseursProGET /v1/analytics/vendors
Export DATEVProPOST /v1/exports/datev
Bounding boxes de champsProSeulement 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"
}