Publicado 6 junio 2026 · 14 min de lectura
Reporte de ventas Excel automático con API (Python + JSON)
Tu equipo de ventas cierra el mes. Gerencia quiere el reporte en Excel. Alguien abre una hoja de cálculo, copia datos del dashboard, aplica formato, agrega fórmulas, revisa que los totales cuadren... y lo repite cada semana. O cada día.
Ese proceso manual consume horas. Y lo peor: es propenso a errores. Una celda mal referenciada, un filtro que se olvidó, un dato que se copió del mes equivocado.
La solución es directa: automatiza la generación del reporte de ventas. Consulta tu base de datos, estructura los números como JSON, envíalos a una API REST y recibe un archivo .xlsx profesional con encabezados, fórmulas, formato de moneda y columnas listas para gráficas.
En este tutorial vas a construir exactamente eso: un script que genera tu reporte de ventas Excel automático, sin tocar una sola celda manualmente.
Por qué automatizar el reporte de ventas con una API
Antes de escribir código, entiende por qué una API es mejor que scripts de generación local para reportes de ventas:
| Criterio | Reporte manual / script local | API REST (Reportia) |
|---|---|---|
| Tiempo por reporte | 30-60 min (manual) / 5 min (script) | 2 segundos (automático) |
| Errores humanos | Frecuentes (copiar/pegar) | Cero (datos directos de la DB) |
| Formato consistente | Varía según quién lo haga | Siempre idéntico (template) |
| Fórmulas Excel | Manuales o hardcodeadas | Declarativas en JSON |
| Escalabilidad | 1 reporte a la vez | 100+ reportes/minuto |
| Envío automático | Requiere intervención | Cron + email = automático |
El punto clave: separas los datos del formato. Tu script se encarga de los datos. La API se encarga del Excel. Si mañana gerencia quiere una columna nueva, cambias el JSON, no reescribes código de renderizado.
Paso 1: Consulta tus datos de ventas
El primer paso es extraer los datos de tu sistema. Puede ser una base de datos SQL, un CRM, una API de e-commerce (Shopify, WooCommerce) o tu propio dashboard. Lo importante es obtener los datos como una lista de registros.
Ejemplo con PostgreSQL:
import psycopg2
from psycopg2.extras import RealDictCursor
from datetime import datetime, timedelta
# Conexión a tu base de datos
conn = psycopg2.connect("postgresql://user:pass@localhost/mi_tienda")
cursor = conn.cursor(cursor_factory=RealDictCursor)
# Ventas de la última semana
hace_7_dias = (datetime.now() - timedelta(days=7)).strftime("%Y-%m-%d")
cursor.execute("""
SELECT
o.order_id,
o.fecha,
c.nombre AS cliente,
p.nombre AS producto,
p.categoria,
oi.cantidad,
oi.precio_unitario,
oi.cantidad * oi.precio_unitario AS subtotal,
o.descuento,
(oi.cantidad * oi.precio_unitario) - o.descuento AS total,
o.metodo_pago,
o.status
FROM orders o
JOIN order_items oi ON o.order_id = oi.order_id
JOIN productos p ON oi.producto_id = p.id
JOIN clientes c ON o.cliente_id = c.id
WHERE o.fecha >= %s AND o.status = 'completada'
ORDER BY o.fecha DESC
""", [hace_7_dias])
ventas = cursor.fetchall()
print(f"{len(ventas)} ventas en los últimos 7 días")
Si usas MySQL, cambia psycopg2 por pymysql. Si usas SQLite, por sqlite3. La estructura es la misma: query → lista de diccionarios.
Para e-commerce con API REST (Shopify, WooCommerce):
import requests
# Ejemplo: obtener pedidos de Shopify
orders = requests.get(
"https://tu-tienda.myshopify.com/admin/api/2024-01/orders.json",
headers={"X-Shopify-Access-Token": "TU_TOKEN"},
params={"status": "any", "created_at_min": hace_7_dias}
).json()["orders"]
ventas = [
{
"order_id": o["id"],
"fecha": o["created_at"][:10],
"cliente": o["customer"]["first_name"] + " " + o["customer"]["last_name"],
"total": float(o["total_price"]),
"metodo_pago": o["payment_gateway_names"][0],
"items": len(o["line_items"])
}
for o in orders
]
Paso 2: Estructura el JSON con formato de reporte
Ahora viene lo importante: organizar los datos para que el Excel salga con el formato que gerencia espera. No solo filas y columnas: encabezados con estilo, fila de totales, formato de moneda y columnas preparadas para gráficas.
from collections import defaultdict
# Calcular resúmenes
total_ventas = sum(v["total"] for v in ventas)
ticket_promedio = total_ventas / len(ventas) if ventas else 0
# Ventas por categoría (para segunda hoja)
por_categoria = defaultdict(lambda: {"cantidad": 0, "total": 0})
for v in ventas:
cat = v.get("categoria", "Sin categoría")
por_categoria[cat]["cantidad"] += v["cantidad"]
por_categoria[cat]["total"] += v["total"]
# Ventas por día (para gráfica)
por_dia = defaultdict(lambda: {"pedidos": 0, "total": 0})
for v in ventas:
dia = v["fecha"][:10] if isinstance(v["fecha"], str) else v["fecha"].strftime("%Y-%m-%d")
por_dia[dia]["pedidos"] += 1
por_dia[dia]["total"] += v["total"]
payload = {
"template": "reporte-ventas-semanal",
"format": "xlsx",
"data": {
"titulo": f"Reporte de Ventas Semanal",
"subtitulo": f"Período: {hace_7_dias} al {datetime.now().strftime('%Y-%m-%d')}",
"empresa": "Mi Empresa SA de CV",
"fecha_generacion": datetime.now().strftime("%Y-%m-%d %H:%M"),
"sheets": [
{
"nombre": "Detalle Ventas",
"columnas": [
{"key": "order_id", "label": "# Pedido", "width": 12},
{"key": "fecha", "label": "Fecha", "format": "date"},
{"key": "cliente", "label": "Cliente", "width": 25},
{"key": "producto", "label": "Producto", "width": 20},
{"key": "cantidad", "label": "Cant.", "width": 8, "format": "number"},
{"key": "precio_unitario", "label": "P. Unit.", "format": "currency"},
{"key": "subtotal", "label": "Subtotal", "format": "currency"},
{"key": "descuento", "label": "Desc.", "format": "currency"},
{"key": "total", "label": "Total", "format": "currency"},
{"key": "metodo_pago", "label": "Método Pago", "width": 15}
],
"filas": ventas,
"totales": {
"cantidad": sum(v["cantidad"] for v in ventas),
"subtotal": sum(v["subtotal"] for v in ventas),
"descuento": sum(v["descuento"] for v in ventas),
"total": total_ventas
},
"estilos": {
"encabezado": {"bg": "#059669", "color": "#FFFFFF", "bold": True},
"totales": {"bold": True, "border_top": "double"}
}
},
{
"nombre": "Por Categoría",
"columnas": [
{"key": "categoria", "label": "Categoría", "width": 20},
{"key": "cantidad", "label": "Unidades", "format": "number"},
{"key": "total", "label": "Total", "format": "currency"},
{"key": "porcentaje", "label": "% del Total", "format": "percent"}
],
"filas": [
{
"categoria": cat,
"cantidad": datos["cantidad"],
"total": datos["total"],
"porcentaje": datos["total"] / total_ventas if total_ventas else 0
}
for cat, datos in sorted(por_categoria.items(), key=lambda x: x[1]["total"], reverse=True)
]
},
{
"nombre": "Tendencia Diaria",
"columnas": [
{"key": "dia", "label": "Día", "format": "date"},
{"key": "pedidos", "label": "Pedidos", "format": "number"},
{"key": "total", "label": "Venta Total", "format": "currency"},
{"key": "ticket_promedio", "label": "Ticket Prom.", "format": "currency"}
],
"filas": [
{
"dia": dia,
"pedidos": datos["pedidos"],
"total": datos["total"],
"ticket_promedio": datos["total"] / datos["pedidos"]
}
for dia, datos in sorted(por_dia.items())
],
"chart_ready": True
}
],
"resumen": {
"total_ventas": total_ventas,
"num_pedidos": len(ventas),
"ticket_promedio": ticket_promedio,
"categorias": len(por_categoria)
}
}
}
Observa la estructura: cada hoja define sus columnas con formato (currency, percent, date), sus filas de datos y opcionalmente una fila de totales con estilo. La hoja “Tendencia Diaria” tiene chart_ready: True para que las columnas estén formateadas de manera que Excel pueda crear gráficas automáticamente al seleccionar el rango.
Paso 3: Envía a la API y recibe el Excel
Con el payload listo, una sola llamada genera el archivo:
import requests
response = requests.post(
"https://reportia.4l3.org/v1/render",
json=payload,
headers={"Authorization": "Bearer TU_API_KEY"},
timeout=30
)
if response.status_code == 200:
nombre_archivo = f"ventas_semanal_{datetime.now().strftime('%Y%m%d')}.xlsx"
with open(nombre_archivo, "wb") as f:
f.write(response.content)
print(f"Reporte generado: {nombre_archivo} ({len(response.content)} bytes)")
else:
print(f"Error: {response.status_code} - {response.text}")
El resultado es un archivo .xlsx con tres hojas: detalle de ventas con encabezados verdes y fila de totales, desglose por categoría con porcentajes, y tendencia diaria lista para gráficas. Todo en menos de 2 segundos.
Paso 4: Automatizar la generación y envío
Un reporte automático de verdad no requiere que alguien ejecute el script. Se ejecuta solo y llega al inbox de quien lo necesita.
Con cron (Linux/Mac)
# Reporte semanal: cada lunes a las 7:00 AM
0 7 * * 1 /usr/bin/python3 /opt/reportes/ventas_semanal.py
# Reporte mensual: primer día de cada mes a las 8:00 AM
0 8 1 * * /usr/bin/python3 /opt/reportes/ventas_mensual.py
# Reporte diario: cada día a las 23:00
0 23 * * * /usr/bin/python3 /opt/reportes/ventas_diario.py
Envío automático por email
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
def enviar_reporte(archivo_xlsx, destinatarios):
msg = MIMEMultipart()
msg["Subject"] = f"Reporte Ventas Semanal - {datetime.now().strftime('%d/%m/%Y')}"
msg["From"] = "[email protected]"
msg["To"] = ", ".join(destinatarios)
# Cuerpo del email
cuerpo = f"""
Reporte de ventas semanal generado automáticamente.
Resumen:
- Total ventas: ${total_ventas:,.2f}
- Pedidos: {len(ventas)}
- Ticket promedio: ${ticket_promedio:,.2f}
El archivo Excel adjunto contiene el detalle completo.
"""
msg.attach(MIMEText(cuerpo, "plain"))
# Adjuntar Excel
with open(archivo_xlsx, "rb") as f:
adjunto = MIMEBase("application", "octet-stream")
adjunto.set_payload(f.read())
encoders.encode_base64(adjunto)
adjunto.add_header("Content-Disposition", "attachment", filename=archivo_xlsx)
msg.attach(adjunto)
with smtplib.SMTP("smtp.gmail.com", 587) as server:
server.starttls()
server.login("[email protected]", "app_password")
server.send_message(msg)
print(f"Reporte enviado a {len(destinatarios)} destinatarios")
# Enviar a gerencia y equipo de ventas
enviar_reporte(
nombre_archivo,
["[email protected]", "[email protected]"]
)
Resultado: cada lunes a las 7 AM, gerencia recibe en su inbox el reporte de ventas de la semana anterior. Sin que nadie toque una tecla.
Ejemplo con JavaScript (Node.js)
Si tu stack es JavaScript, el proceso es idéntico:
const response = await fetch("https://reportia.4l3.org/v1/render", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer TU_API_KEY"
},
body: JSON.stringify({
template: "reporte-ventas-semanal",
format: "xlsx",
data: {
titulo: "Reporte Ventas Semanal",
sheets: [
{
nombre: "Ventas",
columnas: [
{ key: "fecha", label: "Fecha", format: "date" },
{ key: "producto", label: "Producto", width: 20 },
{ key: "cantidad", label: "Cant.", format: "number" },
{ key: "total", label: "Total", format: "currency" }
],
filas: salesData,
estilos: {
encabezado: { bg: "#059669", color: "#FFFFFF", bold: true }
}
}
]
}
})
});
const buffer = await response.arrayBuffer();
fs.writeFileSync(`ventas_${Date.now()}.xlsx`, Buffer.from(buffer));
Ejemplo con cURL
Para pruebas rápidas o integración con cualquier lenguaje:
curl -X POST https://reportia.4l3.org/v1/render \
-H "Content-Type: application/json" \
-H "Authorization: Bearer TU_API_KEY" \
-d '{
"template": "reporte-ventas-semanal",
"format": "xlsx",
"data": {
"titulo": "Ventas Semana 23",
"sheets": [{
"nombre": "Detalle",
"columnas": [
{"key": "producto", "label": "Producto"},
{"key": "cantidad", "label": "Cant."},
{"key": "total", "label": "Total", "format": "currency"}
],
"filas": [
{"producto": "Laptop Pro 15", "cantidad": 12, "total": 14400},
{"producto": "Mouse Ergonómico", "cantidad": 45, "total": 2250},
{"producto": "Monitor 27 4K", "cantidad": 8, "total": 4800},
{"producto": "Teclado Mecánico", "cantidad": 22, "total": 2860}
],
"totales": {"cantidad": 87, "total": 24310}
}]
}
}' -o ventas_semana23.xlsx
Casos de uso reales
E-commerce: reporte diario de ventas por canal
Tiendas online que venden en múltiples canales (web, marketplace, redes sociales) necesitan un reporte consolidado. Con la API, un cron diario consulta cada canal, unifica los datos y genera un Excel con una hoja por canal más una hoja resumen.
SaaS: reporte mensual de MRR y churn
Empresas SaaS necesitan reportar ingresos recurrentes, nuevas suscripciones, cancelaciones y expansión de cuentas. El script consulta la tabla de suscripciones, calcula MRR, churn rate y LTV, y genera un Excel que el CFO puede abrir directamente en su computadora.
Retail: reporte semanal por sucursal
Cadenas de tiendas físicas necesitan comparar ventas entre sucursales. El reporte automático genera una hoja por sucursal con detalle de productos, más una hoja de ranking comparativo con las métricas clave: ventas totales, ticket promedio, productos estrella.
Startups: reporte semanal para inversionistas
El founder genera un Excel semanal con KPIs financieros (revenue, burn rate, runway) y métricas de producto (usuarios activos, conversión, retención). Lo envía automáticamente al board cada viernes. Formato consistente, datos actualizados, cero esfuerzo manual.
Formato de salida: qué incluye el Excel generado
El archivo .xlsx que devuelve la API incluye:
- Encabezados con estilo: fondo de color, texto en blanco, negritas. Configurables por JSON.
- Formato de celdas: moneda ($1,234.56), porcentaje (15.3%), fecha (06/06/2026), número con separador de miles.
- Ancho de columnas automático o manual según lo que definas.
- Fila de totales con fórmulas SUM reales (no valores hardcodeados).
- Múltiples hojas con nombres personalizados.
- Columnas chart-ready: formato numérico correcto para que Excel cree gráficas con un clic.
El resultado es un Excel que parece hecho a mano por alguien con buen ojo para el formato. Pero se genera en 2 segundos.
Planes y precios
| Plan | Documentos/mes | Precio | Ideal para |
|---|---|---|---|
| Free | 50 | $0 | Reporte semanal de un equipo |
| Pro | Ilimitados | $9/mes | Reportes diarios, múltiples equipos |
| Scale | Ilimitados + prioridad | $29/mes | Volumen alto, SLA garantizado |
| Lifetime | Ilimitados para siempre | $49 (pago único) | Pagar una vez, usar para siempre |
50 reportes gratuitos al mes = un reporte semanal durante todo el año con margen de sobra.
Automatiza tu reporte de ventas en Excel hoy
50 documentos/mes en el plan gratuito. Sin tarjeta de crédito. Primer reporte en menos de 5 minutos.
Empezar gratis →Artículos relacionados
- Generar reportes Excel desde base de datos con Python (API + JSON)
- Automatizar reportes PDF y Excel con API REST
- Enviar factura PDF por email automáticamente
Preguntas frecuentes
¿Puedo generar un reporte de ventas Excel automático sin instalar librerías?
Sí. Con una API REST como Reportia, solo necesitas hacer un POST con tus datos en JSON. No necesitas instalar ninguna librería de generación de Excel. El único requisito es poder hacer peticiones HTTP: requests en Python, fetch en JavaScript, curl en terminal.
¿El Excel generado soporta fórmulas y formato para gráficas?
Sí. La API genera archivos .xlsx con fórmulas SUM, AVERAGE y otras que definas en el JSON. Además, aplica formato de celdas (moneda, porcentaje, fecha) que Excel reconoce para crear gráficas automáticamente al seleccionar el rango de datos.
¿Cuántos reportes puedo generar gratis al mes?
El plan gratuito de Reportia incluye 50 documentos por mes sin tarjeta de crédito. Esto es suficiente para un reporte semanal de ventas durante todo el año. Si necesitas mayor volumen (reportes diarios, múltiples equipos), el plan Pro a $9/mes ofrece documentos ilimitados.
¿Puedo enviar el reporte de ventas por email automáticamente?
Sí. Después de recibir el archivo .xlsx de la API, adjúntalo a un email con smtplib en Python o cualquier servicio de email transaccional. Combina esto con un cron job y tendrás reportes semanales enviados automáticamente a gerencia sin intervención manual.