L'API Recepto en Python

Trois scripts : valider, lire, générer. Prérequis : Python 3.9 ou plus récent, avec requests.

La validation se fait sans clé. La lecture et la génération lisent une clé d'API dans la variable d'environnement RECEPTO_API_KEY ; une clé gratuite s'obtient sur la page des tarifs.

Valider une facture

Le fichier part tel quel dans le champ file. La réponse donne le format, le profil, et chaque erreur avec l'identifiant et le texte de sa règle.

"""Valider une facture avec Recepto : gratuit, sans clé.

python validate.py facture.pdf
"""

import os
import sys

import requests

API = os.environ.get("RECEPTO_API", "https://recepto.fr/v1")

with open(sys.argv[1], "rb") as invoice:
    response = requests.post(f"{API}/validate", files={"file": invoice}, timeout=60)

if response.status_code != 200:
    # 413 ou 422 : fichier illisible ; 429 : réessayer après Retry-After secondes.
    sys.exit(f"Refusé ({response.status_code}) : {response.json()['detail']}")

report = response.json()
print(f"{report['format']} {report['profile']} : {'valide' if report['valid'] else 'invalide'}")
for error in report["errors"]:
    print(f"- {error['rule_id']} : {error['message']}")

Lire une facture reçue

/v1/parse renvoie la même structure JSON pour CII, UBL et Factur-X, avec le résumé de la validation dans source. Schéma : /v1/schemas/invoice-1.0.json.

"""Lire une facture reçue en JSON (clé d'API requise).

RECEPTO_API_KEY=rcp_... python parse.py facture.pdf
"""

import os
import sys

import requests

API = os.environ.get("RECEPTO_API", "https://recepto.fr/v1")
HEADERS = {"Authorization": f"Bearer {os.environ['RECEPTO_API_KEY']}"}

with open(sys.argv[1], "rb") as invoice:
    response = requests.post(f"{API}/parse", files={"file": invoice}, headers=HEADERS, timeout=60)

if response.status_code == 429:
    sys.exit(f"Limite atteinte : réessayer dans {response.headers['Retry-After']} s.")
response.raise_for_status()

result = response.json()
invoice = result["invoice"]
print(f"Facture {invoice['number']} de {invoice['seller']['name']}")
print(f"Net à payer : {invoice['totals']['due']} {invoice['currency']}")
print(f"Valide selon EN 16931 : {'oui' if result['source']['valid'] else 'non'}")
print(f"Lectures restantes ce mois-ci : {response.headers['X-Quota-Remaining']}")

Générer une facture Factur-X

L'entrée contient les parties et les lignes, sans aucun total. /v1/generate calcule totaux et TVA selon EN 16931, produit un PDF/A-3 Factur-X, et ne le renvoie que si son XML passe le XSD et le Schematron. Données de l'exemple :

{
  "number": "F2026-0042",
  "issue_date": "2026-10-15",
  "due_date": "2026-11-14",
  "delivery_date": "2026-10-10",
  "seller": {
    "name": "Atelier Numérique SAS",
    "legal_registration": {
      "value": "999999998",
      "scheme": "0002"
    },
    "vat_id": "FR11999999998",
    "address": {
      "line1": "12 rue des Lilas",
      "city": "Nantes",
      "postal_code": "44000"
    }
  },
  "buyer": {
    "name": "Boulangerie du Port SARL",
    "vat_id": "FR19787878784",
    "address": {
      "line1": "3 quai de la Fosse",
      "city": "Nantes",
      "postal_code": "44100"
    }
  },
  "payment": {
    "means_code": "30",
    "iban": "FR7630006000011234567890189"
  },
  "lines": [
    {
      "name": "Développement du site vitrine",
      "quantity": "12.5",
      "unit_code": "HUR",
      "unit_price": "65.00",
      "vat_rate": "20"
    }
  ]
}
"""Générer une facture Factur-X à partir de ses données (clé d'API requise).

RECEPTO_API_KEY=rcp_... python generate.py facture.json facture.pdf
"""

import json
import os
import sys

import requests

API = os.environ.get("RECEPTO_API", "https://recepto.fr/v1")
HEADERS = {"Authorization": f"Bearer {os.environ['RECEPTO_API_KEY']}"}

with open(sys.argv[1], encoding="utf-8") as source:
    data = json.load(source)

response = requests.post(f"{API}/generate", json=data, headers=HEADERS, timeout=60)

if response.status_code == 422 and "report" in response.json():
    # La facture obtenue enfreint une règle officielle : le rapport dit laquelle.
    for error in response.json()["report"]["errors"]:
        print(f"- {error['rule_id']} : {error['message']}")
    sys.exit(1)
response.raise_for_status()

with open(sys.argv[2], "wb") as output:
    output.write(response.content)
print(f"Facture Factur-X écrite dans {sys.argv[2]}")

Erreurs et limites

Référence complète de l'API · Erreurs de validation expliquées