Actualizado 6 junio 2026 · 14 min de lectura
Automatizar facturación con API REST: guía completa Python
Generar facturas manualmente es sostenible cuando tienes 5 clientes. Cuando tienes 50, 500 o 5,000 ventas al mes, necesitas automatización. En esta guía te muestro cómo construir un sistema completo de facturación automática con Python: desde generar una factura individual hasta procesar miles en batch, programar ejecución con cron e integrar con Django o Flask.
Todo con una API REST. Sin instalar bibliotecas de renderizado. Sin mantener servidores de PDF.
Arquitectura del sistema de facturación
Un sistema de facturación automatizado tiene estas piezas:
- Fuente de datos: tu base de datos (PostgreSQL, MySQL, SQLite) o tu ERP/CRM con las ventas.
- Lógica de negocio: calcular subtotales, impuestos, descuentos, folios consecutivos.
- Generación del PDF: convertir los datos en un documento visual profesional.
- Distribución: enviar por email, guardar en disco, subir a la nube.
- Programación: ejecutar automáticamente (diario, semanal, al cierre de mes).
En esta guía, la pieza 3 la resuelve Reportia. Tú te enfocas en las piezas 1, 2, 4 y 5 — la lógica de tu negocio.
Paso 1: Función base para generar una factura
Empezamos con una función reutilizable que acepta los datos de una factura y devuelve el PDF:
import requests
from datetime import date
API_URL = "https://reportia.4l3.org/v1/render"
API_KEY = "TU_API_KEY"
def generar_factura(cliente, items, folio, moneda="MXN"):
"""Genera una factura PDF y devuelve los bytes del archivo."""
subtotal = sum(item["cantidad"] * item["precio"] for item in items)
iva = round(subtotal * 0.16, 2)
total = round(subtotal + iva, 2)
payload = {
"template": "factura-servicios",
"format": "pdf",
"data": {
"empresa": {
"nombre": "Tu Empresa SA de CV",
"rfc": "TEM210101XYZ",
"direccion": "Av. Principal 456, Monterrey, NL",
"telefono": "+52 81 1234 5678",
"email": "[email protected]"
},
"cliente": cliente,
"folio": folio,
"fecha": date.today().isoformat(),
"items": items,
"subtotal": subtotal,
"iva": iva,
"total": total,
"moneda": moneda
}
}
response = requests.post(
API_URL,
json=payload,
headers={
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
},
timeout=30
)
response.raise_for_status()
return response.content
# Uso:
pdf_bytes = generar_factura(
cliente={"nombre": "Acme Corp", "rfc": "ACM200101ABC",
"email": "[email protected]"},
items=[
{"concepto": "Consultoría Técnica", "cantidad": 40, "precio": 750},
{"concepto": "Licencia de software (mensual)", "cantidad": 1, "precio": 4500}
],
folio="F-2026-0089"
)
with open("F-2026-0089.pdf", "wb") as f:
f.write(pdf_bytes)
print("Factura generada correctamente")
Esta función es la base. Calcula IVA automáticamente, usa la fecha del día y maneja errores con raise_for_status().
Paso 2: Procesamiento batch desde base de datos
El caso más común: tienes una tabla ventas en tu base de datos y necesitas generar las facturas de todas las ventas del mes. Aquí un ejemplo con PostgreSQL:
import psycopg2
import os
def facturar_mes(anio, mes):
"""Genera facturas para todas las ventas de un mes."""
conn = psycopg2.connect(
host="localhost", dbname="mi_erp",
user="app", password="secreto"
)
cursor = conn.cursor()
cursor.execute("""
SELECT v.id, c.nombre, c.rfc, c.email,
v.concepto, v.cantidad, v.precio_unitario
FROM ventas v
JOIN clientes c ON v.cliente_id = c.id
WHERE EXTRACT(YEAR FROM v.fecha) = %s
AND EXTRACT(MONTH FROM v.fecha) = %s
AND v.factura_generada = FALSE
ORDER BY v.id
""", (anio, mes))
ventas = cursor.fetchall()
if not ventas:
print("No hay ventas pendientes de facturar")
return
# Agrupar por cliente
clientes = {}
for venta in ventas:
cid = venta[1] # nombre del cliente como key
if cid not in clientes:
clientes[cid] = {
"cliente": {"nombre": venta[1], "rfc": venta[2],
"email": venta[3]},
"items": [],
"venta_ids": []
}
clientes[cid]["items"].append({
"concepto": venta[4],
"cantidad": int(venta[5]),
"precio": float(venta[6])
})
clientes[cid]["venta_ids"].append(venta[0])
# Obtener el último folio
cursor.execute("SELECT MAX(folio_num) FROM facturas")
ultimo_folio = cursor.fetchone()[0] or 0
carpeta = f"./facturas/{anio}-{mes:02d}/"
os.makedirs(carpeta, exist_ok=True)
generadas = 0
for nombre_cliente, datos in clientes.items():
ultimo_folio += 1
folio = f"F-{anio}-{ultimo_folio:04d}"
try:
pdf = generar_factura(
cliente=datos["cliente"],
items=datos["items"],
folio=folio
)
ruta = os.path.join(carpeta, f"{folio}.pdf")
with open(ruta, "wb") as f:
f.write(pdf)
# Marcar ventas como facturadas
for vid in datos["venta_ids"]:
cursor.execute(
"UPDATE ventas SET factura_generada = TRUE WHERE id = %s",
(vid,)
)
# Registrar la factura
cursor.execute(
"""INSERT INTO facturas (folio, folio_num, cliente, total, ruta_pdf, fecha)
VALUES (%s, %s, %s, %s, %s, CURRENT_DATE)""",
(folio, ultimo_folio, nombre_cliente,
sum(i["cantidad"] * i["precio"] for i in datos["items"]),
ruta)
)
generadas += 1
print(f"OK: {folio} - {nombre_cliente}")
except Exception as e:
print(f"ERROR: {nombre_cliente} - {e}")
conn.commit()
conn.close()
print(f"\n{generadas} facturas generadas en {carpeta}")
# Ejecutar
facturar_mes(2026, 6)
Este script agrupa ventas por cliente, asigna folios consecutivos, genera los PDFs, marca las ventas como facturadas y registra cada factura en la base de datos. Todo en una sola ejecución.
De 4 horas manuales a 0 minutos
Automatiza tu facturación completa. 50 documentos gratis al mes.
Ver planes →Paso 3: Programar ejecución automática con cron
No quieres ejecutar el script manualmente. Programa un cron job:
# Facturar diariamente a las 23:00 (las ventas del día)
0 23 * * * cd /app && python facturacion.py --modo diario >> /var/log/facturas.log 2>&1
# Facturación mensual: día 1 de cada mes a las 6:00 AM (mes anterior)
0 6 1 * * cd /app && python facturacion.py --modo mensual >> /var/log/facturas.log 2>&1
Para manejar los dos modos, agrega argumentos a tu script:
import argparse
from datetime import date, timedelta
parser = argparse.ArgumentParser()
parser.add_argument("--modo", choices=["diario", "mensual"], default="diario")
args = parser.parse_args()
hoy = date.today()
if args.modo == "diario":
# Facturar ventas de hoy
facturar_dia(hoy)
elif args.modo == "mensual":
# Facturar todo el mes anterior
mes_anterior = hoy.replace(day=1) - timedelta(days=1)
facturar_mes(mes_anterior.year, mes_anterior.month)
Paso 4: Integración con Django
Si tu aplicación usa Django, puedes exponer un endpoint para generar facturas bajo demanda:
# views.py
from django.http import HttpResponse, JsonResponse
from django.views.decorators.http import require_POST
from django.contrib.auth.decorators import login_required
import requests
REPORTIA_URL = "https://reportia.4l3.org/v1/render"
REPORTIA_KEY = "TU_API_KEY"
@login_required
@require_POST
def generar_factura_view(request, venta_id):
"""Genera y descarga la factura PDF de una venta."""
from .models import Venta
venta = Venta.objects.select_related("cliente").get(
id=venta_id, empresa=request.user.empresa
)
payload = {
"template": "factura-servicios",
"format": "pdf",
"data": {
"empresa": {
"nombre": venta.empresa.nombre,
"rfc": venta.empresa.rfc,
},
"cliente": {
"nombre": venta.cliente.nombre,
"rfc": venta.cliente.rfc,
},
"folio": venta.folio,
"fecha": venta.fecha.isoformat(),
"items": [
{"concepto": item.concepto,
"cantidad": item.cantidad,
"precio": float(item.precio)}
for item in venta.items.all()
],
"subtotal": float(venta.subtotal),
"iva": float(venta.iva),
"total": float(venta.total),
"moneda": "MXN"
}
}
resp = requests.post(
REPORTIA_URL,
json=payload,
headers={"Authorization": f"Bearer {REPORTIA_KEY}",
"Content-Type": "application/json"},
timeout=30
)
if resp.status_code != 200:
return JsonResponse({"error": "Error generando factura"}, status=500)
# Guardar referencia
venta.factura_generada = True
venta.save()
response = HttpResponse(resp.content, content_type="application/pdf")
response["Content-Disposition"] = f'attachment; filename="{venta.folio}.pdf"'
return response
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path("venta/<int:venta_id>/factura/", views.generar_factura_view,
name="generar_factura"),
]
Paso 5: Integración con Flask
Si prefieres Flask, la integración es igual de directa:
from flask import Flask, request, send_file, jsonify
from io import BytesIO
import requests as http_requests
app = Flask(__name__)
REPORTIA_URL = "https://reportia.4l3.org/v1/render"
REPORTIA_KEY = "TU_API_KEY"
@app.route("/api/facturas", methods=["POST"])
def crear_factura():
"""Recibe datos de factura y devuelve el PDF."""
datos = request.get_json()
payload = {
"template": datos.get("template", "factura-servicios"),
"format": "pdf",
"data": datos
}
resp = http_requests.post(
REPORTIA_URL,
json=payload,
headers={"Authorization": f"Bearer {REPORTIA_KEY}",
"Content-Type": "application/json"},
timeout=30
)
if resp.status_code != 200:
return jsonify({"error": resp.text}), 500
return send_file(
BytesIO(resp.content),
mimetype="application/pdf",
as_attachment=True,
download_name=f"{datos.get('folio', 'factura')}.pdf"
)
Webhook: notificación cuando la factura está lista
Para procesos asíncronos (facturas pesadas, lotes grandes), puedes usar webhooks. Reportia envía un POST a tu URL cuando el documento está listo:
import requests
payload = {
"template": "factura-servicios",
"format": "pdf",
"data": {
"cliente": {"nombre": "Cliente Grande SA"},
"folio": "F-2026-0200",
"items": [{"concepto": "Proyecto anual", "cantidad": 1, "precio": 500000}],
"total": 580000,
"moneda": "MXN"
},
"webhook": {
"url": "https://tu-app.com/api/webhook/factura-lista",
"headers": {"X-Webhook-Secret": "mi-secreto-seguro"}
}
}
response = requests.post(
"https://reportia.4l3.org/v1/render",
json=payload,
headers={"Authorization": "Bearer TU_API_KEY",
"Content-Type": "application/json"}
)
# Respuesta inmediata con job_id
data = response.json()
print(f"Job ID: {data['job_id']}")
print("El PDF se enviará a tu webhook cuando esté listo")
Tu endpoint de webhook recibirá:
# Tu endpoint recibe:
# POST /api/webhook/factura-lista
# Headers: X-Webhook-Secret: mi-secreto-seguro
# Body:
{
"job_id": "abc-123",
"status": "completed",
"folio": "F-2026-0200",
"download_url": "https://reportia.4l3.org/v1/download/abc-123",
"expires_at": "2026-06-07T00:00:00Z"
}
Envío automático por email
Completa el pipeline enviando cada factura por email al cliente:
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email import encoders
def enviar_factura(email_destino, folio, pdf_bytes):
"""Envía la factura PDF como adjunto por email."""
msg = MIMEMultipart()
msg["From"] = "[email protected]"
msg["To"] = email_destino
msg["Subject"] = f"Tu factura {folio}"
cuerpo = f"""Hola,
Adjunto encontrarás tu factura {folio}.
Si tienes alguna duda, responde a este correo.
Saludos,
Tu Empresa SA de CV"""
msg.attach(MIMEText(cuerpo, "plain"))
adjunto = MIMEBase("application", "pdf")
adjunto.set_payload(pdf_bytes)
encoders.encode_base64(adjunto)
adjunto.add_header("Content-Disposition", f"attachment; filename={folio}.pdf")
msg.attach(adjunto)
with smtplib.SMTP("smtp.tuempresa.com", 587) as server:
server.starttls()
server.login("[email protected]", "password")
server.send_message(msg)
print(f"Factura {folio} enviada a {email_destino}")
# Uso combinado: generar + enviar
pdf = generar_factura(
cliente={"nombre": "Acme Corp", "rfc": "ACM200101ABC",
"email": "[email protected]"},
items=[{"concepto": "Servicio mensual", "cantidad": 1, "precio": 12000}],
folio="F-2026-0100"
)
enviar_factura("[email protected]", "F-2026-0100", pdf)
Para más detalles sobre el envío por email, consulta nuestra guía de envío automático de facturas por email.
También Excel y CSV
Además de PDF, genera las mismas facturas en Excel o CSV cambiando el campo "format". Ideal para contadores que necesitan importar datos a su sistema contable. Consulta las plantillas disponibles para cada formato.
Facturación automática en 30 minutos
50 documentos gratis al mes. Plan Pro desde $9 USD/mes.
Empezar gratis →Preguntas frecuentes
¿Reportia genera el XML fiscal (CFDI)?
No. Reportia genera el PDF de representación impresa. El timbrado CFDI lo haces con tu PAC (Finkok, SW, Facturapi, etc.). Reportia complementa tu PAC generando el PDF profesional que envías al cliente. Más sobre facturación PDF.
¿Cuántas facturas puedo generar por minuto?
En el plan gratuito, 10 documentos/minuto. En Pro, 60/minuto. En Business, 200/minuto con prioridad. Para lotes de miles de facturas, usa el endpoint batch con webhook.
¿Puedo personalizar el diseño de la factura?
Sí. Usa cualquiera de las plantillas prediseñadas o sube tu propio HTML/CSS. Puedes incluir tu logo, colores corporativos y cualquier campo adicional.
¿Funciona con monedas distintas a MXN?
Sí. Envía el campo "moneda" con cualquier código ISO 4217: USD, EUR, COP, ARS, etc. La plantilla formatea los números automáticamente.
¿Necesito instalar algo?
Solo la biblioteca requests de Python (pip install requests). La generación del PDF ocurre en la nube. Ver planes y precios.
Otros tutoriales: Generar facturas PDF con Python · Enviar facturas por email · Automatizar reportes PDF y Excel · Ver todos los tutoriales