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
401: clé absente, inconnue, révoquée ou expirée.413ou422: fichier trop gros ou illisible, avec un code danscode; pour la génération,invalid_invoiceet le rapport de validation dansreport.429: trop de requêtes ou quota du mois atteint ; attendre le nombre de secondes de l'en-têteRetry-After.- Chaque lecture et chaque génération réussies donnent le solde du mois dans
X-Quota-Remaining.
Référence complète de l'API · Erreurs de validation expliquées