Librería para Python
Paquete apifactu · versión 1.2.0 · Python 3.10 a 3.13
Cliente oficial para integrar la facturación desde tu propio backend. Trae resueltos los reintentos
(en 429 y 5xx, nunca en 4xx), la idempotencia y la verificación
de la firma de los webhooks, con tipos y documentación en el propio paquete.
1. Instalación
# Desde PyPI pip install apifactu # O con el ZIP que descargas en tu panel → Integraciones pip install ./apifactu-sdk-python-1.2.0.zip
2. Tu clave
- Entra en tu panel: https://app.apifactu.es → API keys → «Crear clave».
- Copia la clave en el momento de crearla: por seguridad no se vuelve a mostrar entera.
- Guárdala en una variable de entorno (por ejemplo
APIFACTU_API_KEY); no la escribas en el código ni la subas al repositorio.
eck_test_) y úsala igual que la real. El host es el mismo
(https://api.apifactu.es): el entorno lo decide la clave. Con eck_test_
las facturas se numeran y devuelven QR y huella, pero no se comunican a Hacienda. Cuando todo
funcione, cambia la clave por una eck_live_ y ya estás facturando de verdad.
3. Primera factura
import os from apifactu import Client client = Client(api_key=os.environ["APIFACTU_API_KEY"]) # eck_test_… para probar inv = client.invoices.create( referencia="PED-1001", # tu identificador del pedido base_imponible=100, tipo_iva=21, nif_receptor="12345678Z", nombre_receptor="Cliente SL", idempotency_key="PED-1001", # reintentar nunca duplica la factura ) print(inv.status, inv.numero_completo, inv.qr_url)
Guarda inv.id: es lo que necesitan invoices.cancel(id) y invoices.rectify(id, …). La confirmación de Hacienda llega después, por webhook.
4. Consultar
detalle = client.invoices.get(inv.id) # estado, QR, huella, CSV de la AEAT pagina = client.invoices.list(status="ACCEPTED", page=1, per_page=20) png = client.invoices.qr_png(inv.id) # bytes del QR, listo para imprimir
5. Webhooks firmados
Registra la URL de tu servidor y verifica cada entrega sobre el cuerpo crudo de la petición (si deserializas y vuelves a serializar el JSON, la firma deja de coincidir).
wh = client.webhooks.register("https://mitienda.es/webhook",
events=["invoice.accepted", "invoice.rejected"])
# wh.secret solo se muestra al crearlo: guárdalo
# En tu endpoint (ejemplo con Flask)
from apifactu import verify_webhook, parse_webhook_event
@app.post("/webhook")
def webhook():
if not verify_webhook(request.get_data(), request.headers, WEBHOOK_SECRET):
return "", 400
event = parse_webhook_event(request.get_data())
# event.event == "invoice.accepted" | "invoice.rejected" …
return "", 200
verify_webhook usa la firma versionada X-Apifactu-Signature (t=…,v1=…, con protección frente a reenvíos) si viene, y si no la firma plana X-Fiscal-Signature.
Errores
| Excepción | Cuándo | Qué hacer |
|---|---|---|
ApifactuAuthError | Clave inválida o revocada | Crea una nueva en tu panel → API keys |
ApifactuApiError | Validación (422) u otro 4xx | Corrige el payload; no reintentes |
ApifactuRateLimitError | 429 tras agotar los reintentos | Espera lo que indique Retry-After |
ApifactuServerError | 5xx tras agotar los reintentos | Reintenta más tarde con la misma idempotency_key |
Todas heredan de ApifactuError. El CHANGELOG y el README completos van dentro del paquete.