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

pipapifactu
# 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

  1. Entra en tu panel: https://app.apifactu.es → API keys → «Crear clave».
  2. Copia la clave en el momento de crearla: por seguridad no se vuelve a mostrar entera.
  3. Guárdala en una variable de entorno (por ejemplo APIFACTU_API_KEY); no la escribas en el código ni la subas al repositorio.
Probar sin efectos fiscales. Crea en tu panel una clave de pruebas (empieza por 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

POST/v1/invoices
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

GET/v1/invoices
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).

POST/v1/webhooks
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ónCuándoQué hacer
ApifactuAuthErrorClave inválida o revocadaCrea una nueva en tu panel → API keys
ApifactuApiErrorValidación (422) u otro 4xxCorrige el payload; no reintentes
ApifactuRateLimitError429 tras agotar los reintentosEspera lo que indique Retry-After
ApifactuServerError5xx tras agotar los reintentosReintenta más tarde con la misma idempotency_key

Todas heredan de ApifactuError. El CHANGELOG y el README completos van dentro del paquete.

Crear cuenta y obtener API key →