API de Wivo Analytics
La API de Wivo te permite acceder programáticamente a datos de ventas, ítems de órdenes, costos asociados (comisiones, envíos, promociones, publicidad), márgenes y métricas de rentabilidad disponibles en los reportes de la plataforma.
¿Por dónde empezar? Si vas a integrar por primera vez, lee las secciones 1 a 6 en orden: te llevan desde habilitar la API hasta dejar tus datos sincronizados. Las secciones 7 a 11 son la referencia técnica que vas a consultar de a ratos (parámetros, campos, límites).
1. Casos de uso principales
- Integrar datos de Wivo en data warehouses, ERPs, sistemas contables o herramientas internas
- Automatizar reportes y dashboards personalizados sin exportaciones manuales de archivos
2. Datos disponibles
La API entrega información a nivel de ítem de orden, que incluye:
- Detalle de la orden (ID, fecha de compra, SKUs, tipo de envío, estado de orden y pago)
- Costos de la orden (comisiones, envío, logística)
- Costos adicionales (almacenamiento fulfillment, publicidad)
- Métricas derivadas (rentabilidad, porcentaje de rentabilidad)
3. Habilitar la API
- Accede a tu cuenta de Wivo con credenciales de administrador
- Ve a “Integración API” en el menú superior derecho
- Haz clic en “Solicitar habilitación de API”
- Agenda una reunión con un representante de Wivo para la activación
- Recibe tu API Key personal y confidencial
4. Autenticación
Incluye tu API Key en el header HTTP de cada petición:
Ocp-Apim-Subscription-Key: TU_API_KEY
Las claves inválidas o ausentes retornan HTTP 401 Unauthorized. Si tu cuenta no tiene habilitado el acceso a la API de datos, o la suscripción fue revocada, retorna HTTP 403 Forbidden.
5. Tu primera petición
Con tu API Key lista, haz esta petición para confirmar que todo funciona. Trae los ítems de órdenes de un rango de fechas:
curl "https://api.wivoanalytics.com/data?date=purchase_date&from=2025-01-01&to=2025-01-31&page=1" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI"
La respuesta es un JSON con dos partes:
data— el array de ítems de orden (cada uno con sus ventas, costos y rentabilidad). Ver el detalle de los campos en la sección 8.meta— información de paginación. Fíjate entotalPages: te dice cuántas páginas hay que recorrer para traer todo el rango.
Con esto ya puedes explorar los datos. Para una integración de verdad, sigue con la sección 6 (cómo mantener tus datos sincronizados) y usa las secciones 7 a 11 como referencia del detalle de cada parámetro y campo.
6. Cómo sincronizar tus datos: flujo histórico vs. flujo regular
Para una integración real vas a tener dos flujos: una carga histórica (una sola vez, para traer todo el pasado) y un flujo regular (recurrente, para mantenerte sincronizado).
Modelo mental — por qué borrar e insertar (delete + insert): Una orden no es inmutable. Después de la compra puede cambiar de estado, ajustar costos, recibir reembolsos o cancelarse. Por eso, cada vez que una orden aparece en la respuesta, borra todos sus ítems en tu base y vuelve a insertarlos completos. Así tu copia siempre refleja el último estado de Wivo, sin duplicados ni datos viejos. El campo para agrupar los ítems de una misma orden es
Orden.
6.1. Carga histórica (backfill inicial)
Objetivo: traer toda la historia que incluye tu plan, una sola vez.
Usa date=purchase_date y pagina hasta agotar el rango. El campo meta.totalPages de la respuesta (ver sección 8) te dice cuántas páginas hay.
El punto de partida es el inicio de la ventana que incluye tu plan (ver sección 7.2). Desde ahí, un solo rango paginado cubre toda tu historia: sin to, la ventana llega hasta hoy.
desde_cuando = inicio_de_la_ventana_de_tu_plan() # ver sección 7.2
page = 1
total_pages = 1 # se actualiza con la primera respuesta
while page <= total_pages:
resp = GET("/data", params={
"date": "purchase_date",
"from": desde_cuando,
"page": page,
"limit": 1000,
})
guardar_con_delete_insert(resp["data"]) # borrar ítems de cada Orden y reinsertar
total_pages = resp["meta"]["totalPages"]
page += 1
# Guarda el mayor "Fecha de actualización" (updated_at) que hayas visto:
# es el punto de partida del flujo regular.
Pide páginas grandes y respeta el rate limit. Con
limit=1000traes 10 veces más filas por request que con el mínimo, y el techo son 5 req/min (ver sección 10): unas 5.000 filas por minuto. Agrega una pausa entre páginas y reintentos con backoff exponencial ante unHTTP 429.
6.2. Flujo regular (incremental, por hora o por día)
Objetivo: mantener tu copia sincronizada trayendo solo lo que cambió.
Usa date=updated_at desde la última fecha que procesaste. Esto trae órdenes nuevas y actualizaciones (cambios de estado, ajustes de costo, cancelaciones, reembolsos):
last_synced = "2025-01-15T09:55:00" # ← último updated_at procesado, MENOS la ventana de seguridad
page = 1
total_pages = 1
while page <= total_pages:
resp = GET("/data", params={
"date": "updated_at",
"from": last_synced,
"page": page,
"limit": 1000,
})
guardar_con_delete_insert(resp["data"])
total_pages = resp["meta"]["totalPages"]
page += 1
La ventana se aplica a la fecha de actualización: la respuesta trae las subórdenes actualizadas en el rango, aunque se hayan comprado antes. Una orden comprada hace tres meses que hoy cambia de estado aparece en la ventana de hoy.
El alcance cubre las compras de los últimos 120 días y viene declarado en cada respuesta (ver sección 11).
Ventana de seguridad (recomendado): resta 5–10 min a tu último updated_at antes de pedir de nuevo. Si lo último procesado fue 2025-01-15T10:00:00, pide desde 2025-01-15T09:55:00. Esto evita perder cambios por desfases de reloj o latencia. Como reinsertas por orden completa, volver a traer una orden no genera duplicados.
Elige ventanas cortas. Una ventana de varios días puede devolver varias páginas aun con
limit=1000. Con el rate limit de 5 req/min (ver sección 10), sincronizar por hora es más rápido y liviano que por día.
Límite de 7 días con
updated_at: un request no puede cubrir más de 7 días (ver sección 7.2). Si tu proceso estuvo caído más de una semana, avanza en ventanas de ≤7 días hasta alcanzar el presente.
7. Referencia: endpoint y parámetros
7.1. Endpoint
GET /data— Devuelve órdenes e ítems de costos con todo su detalle.
7.2. Parámetros de GET /data
date(purchase_dateoupdated_at): Debes escoger uno de estos parámetros (obligatorio)from: obligatorio, ISO 8601. Acepta2025-01-31o2025-01-31T09:55:00to: opcional, ISO 8601. Acepta2025-02-01o2025-02-01T18:30:00item_type: opcional. Filtra por tipo de fila:order,publicationomarketplace. Acepta varios valores separados por coma. Los aliasordersypublicationssiguen siendo válidospayment_status: opcional. Filtra por estado de pago:paid,unpaidonoinformation. Acepta varios valores separados por comaitem_status: opcional. Filtra por estado de la orden:paid,canceledoreturned. Acepta varios valores separados por comapage: opcionallimit: opcional, tamaño de página. Default1000, máximo1000categories: opcional, lista separada por comas:income,costs,reimbursement. Agrega a la respuesta las medidas de esas categorías y su total (ver sección 9). Si se omite, se devuelve solo el set base
Hora en
fromyto: si envías hora, se respeta. Si envías solo la fecha,fromse completa a las00:00:00ytoa las23:59:59de ese día. La zona en que se interpretan depende del campo que filtres (ver sección 11).
Ventana de fechas que incluye tu plan. Tu plan define desde cuándo puedes consultar:
Plan Puedes consultar desde Free El día 1 del mes anterior al actual Pro El 1 de enero de dos años atrás Con
purchase_datepuedes pedir esa ventana completa en una sola petición y recorrerla paginando conlimit(el ritmo lo marca el rate limit, ver sección 10).Con
date=updated_atuna petición no puede cubrir más de 7 días, cualquiera sea tu plan.Un
toposterior al día de hoy se recorta a hoy, porque más allá no hay datos todavía. El valor efectivo viaja enmeta.to.Un
fromanterior a la ventana de tu plan retorna HTTP 400 Bad Request, con tu plan y la fecha desde la que puedes consultar en el mensaje. La petición falla completa: no se devuelve un rango recortado, para que puedas distinguir un tramo que no entró de uno que vino vacío.Un parámetro inválido —fecha, rango excedido, o un valor no reconocido en
item_type,payment_statusoitem_status— retorna HTTP 400 Bad Request indicando qué valor se rechazó y cuáles son los válidos.
Un nombre de parámetro que no esté en esta lista también retorna 400, con los nombres válidos en el mensaje. Es a propósito: si un parámetro mal escrito se ignorara en silencio, recibirías todas las filas creyendo que filtraste.
Ejemplos:
Con purchase_date:
curl "https://api.wivoanalytics.com/data?date=purchase_date&from=2025-01-01&to=2025-01-31&page=1" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI"
Con updated_at:
curl "https://api.wivoanalytics.com/data?date=updated_at&from=2025-01-01T20:55:00" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI"
7.3. Valores de los filtros y lo que devuelve cada columna
Los filtros se envían con la clave en inglés. Las columnas de la respuesta traen la etiqueta en español.
| Parámetro | Valor a enviar | Columna | Valor que verás |
|---|---|---|---|
item_type | order | Tipo de Dato | Orden |
item_type | publication | Tipo de Dato | Publicación |
item_type | marketplace | Tipo de Dato | Marketplace |
payment_status | paid | Estado de Pago | Pagado |
payment_status | unpaid | Estado de Pago | Informado |
payment_status | noinformation | Estado de Pago | Sin Información |
item_status | paid | Estado de Orden | Regular |
item_status | canceled | Estado de Orden | Cancelada |
item_status | returned | Estado de Orden | Devuelta |
unpaidse muestra comoInformado. Antes esa columna decíaNo Pagado. Lo que cambió es la etiqueta que viaja en la respuesta, no el contrato: el valor que envías en el filtro sigue siendopayment_status=unpaid. Si tu proceso concilia comparando el texto de la columnaEstado de Pago, ajústalo aInformado; si filtra por la clave, no tienes que cambiar nada.
Ojo con
item_status=paid: devuelve las órdenes en estadoRegular, o sea las que no fueron canceladas ni devueltas. No tiene relación con el estado de pago; para eso estápayment_status.
Los filtros son independientes entre sí y se combinan con AND. Los valores dentro de un mismo filtro se combinan con OR: payment_status=paid,unpaid trae las filas que estén en cualquiera de los dos estados.
Qué trae una fila marketplace
Son movimientos que el marketplace registra a nivel de cuenta y no de una venta puntual: tarifas de almacenamiento, cobros de publicidad, cargos de facturación, y también sus reembolsos y compensaciones.
Lo que caracteriza a estas filas:
VentasyUnidadesvienen en0SKU Productoviene enSin SKUProductotrae la descripción que el marketplace le da al movimiento, en el idioma de ese marketplace. Por ejemploCobro por Productos patrocinados,Cargos de facturaciónoTarifa pelo serviço de armazenamento FullCosto de Marketplacetrae el monto, junto con la medida de la sección de costo que correspondaEstado de Ordenviene enRegular
Importan al calcular costos: traen costo y no traen ventas. Si las excluyes con item_type=order o item_type=order,publication, ese costo no aparece en la respuesta. Cuánto pesa depende de la cuenta y del marketplace.
Pedir el mismo recorte que el panel de Rentabilidad
El panel carga considerando órdenes y publicaciones, en estado Regular, y excluyendo las filas sin información de pago. Ese recorte se pide así:
curl "https://api.wivoanalytics.com/data?date=purchase_date&from=2025-01-01&item_type=order,publication&payment_status=paid,unpaid&item_status=paid" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI"
Si necesitas que los totales coincidan con la pantalla, revisa además la ventana de fechas y su zona horaria (ver sección 11).
8. Estructura de respuesta de GET /data
Ejemplo simplificado:
{
"data": [
{
"Canal": "Marketplace A",
"Cuenta": "Cuenta Demo 1",
"Orden": "1000000000000001",
"Nro. suborden": "1000000000000001",
"Nro. Paquete": "1000000000000001",
"N° de Liquidación": "1000000000000001",
"SKU Producto": "SKU-DEMO-001",
"SKU Marketplace": "9000000000001",
"Producto": "Producto Demostración 1 - Talla: L",
"Variante": "Talla: L",
"SKU Variante": "SKU-DEMO-001",
"Fecha de actualización": "2024-01-11T06:26:29.000Z",
"Estado de Orden": "Regular",
"Estado de Pago": "Pagado",
"Estado de Despacho": "Entregado",
"Tipo de Despacho": "No Fulfillment",
"Fecha de compra": "2024-01-09T23:56:56.000",
"Tipo de Dato": "Orden",
"Ventas": "7500",
"Unidades": "1",
"Costo de Marketplace": "1500.00",
"Rentabilidad Marketplace": "6000.00",
"% Rentabilidad Marketplace": "80.00",
"Costo Envío": "1000",
"Costo de Comisiones": "500"
},
{
"Canal": "Marketplace A",
"Cuenta": "Cuenta Demo 1",
"Orden": "1000000000000002",
"Nro. suborden": "1000000000000003",
"Nro. Paquete": "1000000000000002",
"N° de Liquidación": "1000000000000002",
"SKU Producto": "SKU-DEMO-002",
"SKU Marketplace": "9000000000002",
"Producto": "Producto Demostración 2 - Talla: L Tamaño: 10.2",
"Variante": "Color: Azul, Tamaño: 10.2",
"SKU Variante": "SKU-DEMO-002",
"Fecha de actualización": "2024-01-11T06:29:13.000Z",
"Estado de Orden": "Regular",
"Estado de Pago": "Pagado",
"Estado de Despacho": "Entregado",
"Tipo de Despacho": "Fulfillment",
"Fecha de compra": "2024-01-09T23:56:43.000",
"Tipo de Dato": "Orden",
"Ventas": "12000",
"Unidades": "1",
"Costo de Marketplace": "4000.00",
"Rentabilidad Marketplace": "8000.00",
"% Rentabilidad Marketplace": "66.67",
"Costo Envío": "2000",
"Costo de Comisiones": "2000"
}
// ... aquí podrías seguir agregando más items con los mismos campos
],
"meta": {
"page": 1,
"pageSize": 100,
"count": 100,
"from": "2024-01-09T00:00:00",
"to": "2024-01-09T23:59:59",
"date": "purchase_date",
"totalRows": 3282,
"totalPages": 33
},
"correlation_id": "00000000-0000-4000-8000-000000000001"
}
El objeto meta te da todo lo necesario para paginar: page (página actual), totalPages (total de páginas del rango) y totalRows (total de ítems). Recorre desde page=1 hasta totalPages para traer el rango completo.
También trae window y fields, que declaran la zona horaria de la ventana y de cada campo de fecha (ver sección 11):
"meta": {
"page": 1,
"pageSize": 100,
"count": 100,
"from": "2024-01-09T00:00:00",
"to": "2024-01-09T23:59:59",
"date": "purchase_date",
"totalRows": 3282,
"totalPages": 33,
"window": { "field": "purchase_date", "timezone": "local" },
"fields": {
"Fecha de compra": { "type": "datetime", "timezone": "local" },
"Fecha de actualización": { "type": "datetime", "timezone": "UTC" }
}
}
9. Categorías de medidas (categories)
Por defecto, cada ítem de data trae un set base de 7 medidas. Son las que ves en el ejemplo de la sección 8:
Ventas, Unidades, Costo de Marketplace, Rentabilidad Marketplace, % Rentabilidad Marketplace, Costo Envío, Costo de Comisiones.
El parámetro categories agrega columnas adicionales a cada ítem de data, con el desglose detrás de esos totales. Pedís una o más categorías separadas por coma (income, costs, reimbursement). Cada categoría agrega sus medidas más un campo de total.
Las columnas se identifican por su nombre exacto (el mismo que aparece como clave en el JSON). Si pides varias categorías y comparten alguna medida, la columna aparece una sola vez (no se duplica). Los totales
VentasyCosto de Marketplaceya vienen en el set base; las categorías agregan su desglose.
income — Ingresos de la orden
| Columna | |
|---|---|
Ventas | |
Despacho pagado por comprador | |
Ingreso por Envío Flex | |
Ingreso de Compensación por Daños | |
Ingreso por promoción | |
Ingresos sin categorizar | |
Ingreso Total | total de la categoría |
costs — Costos del marketplace
Refleja el análisis de costos de la plataforma, agrupado por sección. El total de cada sección está en negrita.
Ojo con el gran total.
Costo de Marketplacees el costo total del canal y las ocho secciones de abajo son su desglose. Si necesitas el total exacto, usaCosto de Marketplacedirectamente en vez de sumar las secciones.
Comisiones: Cobro de Comisiones, Reembolso de Comisiones, Costo de Comisiones
Envío: Despacho pagado por comprador, Cobro Envío Flex, Ingreso por Envío Flex, Reembolso de envío, Cobro de Envío, Costo Envío
Publicidad: Cobro por publicidad, Reembolso por publicidad, Costos por publicidad
Almacenamiento Fulfillment: Cobro por Costo Logístico, Reembolso de Costo Logístico, Cobro por Servicio Almacenamiento, Cobro por Almacenamiento Prolongado, Cobro por Descarte de Stock, Reembolso por servicio almacenamiento, Reembolso por descarte de stock, Reembolso por almacenamiento prolongado, Costos de Almacenamiento Fulfillment
Otros costos: Cobro de Ajuste sobre la Comisión, Reembolso de Ajuste sobre la Comisión, Cobro de Penalizacion por Cancelación, Devolución de Penalización por Cancelación, Cobro Logística Inversa, Reembolso Logística Inversa, Cobro de Penalización por Daños, Ingreso de Compensación por Daños, Reembolso de Ajuste sobre Venta, Ingreso por promoción, Cobros sin categorizar, Ingresos sin categorizar, Reembolsos sin categorizar, Cobro de Impuestos, Reembolso de Impuestos, Otros costos
Descuentos y Promociones: Cobro por Cupones de Descuento, Reembolso por Cupones de Descuento, Costos de Descuentos y Promociones
Gestión de Cuenta: Cobro por Asesoría Comercial (KAM), Cobro por Uso de Mercado Pago, Cobro por Mantenimiento eShop, Cobro por Reputación, Cobro por Publicar, Reembolso por Asesoría Comercial (KAM), Reembolso por Uso de Mercado Pago, Reembolso por Mantenimiento eShop, Costos de Gestión de Cuenta
Financiamiento: Cobro por Financiación y Cuotas Mercado Pago, Cobro por Adelanto de Dinero, Reembolso por Financiación y Cuotas Mercado Pago, Costos de Financiamiento
Gran total: Costo de Marketplace
reimbursement — Reembolsos de cargos
| Columna | |
|---|---|
Reembolso de Ajuste sobre la Comisión | |
Devolución de Penalizacion por Cancelación | |
Reembolso Logística Inversa | |
Reembolso de Ajuste sobre Venta | |
Reembolso de Comisiones | |
Reembolso de envío | |
Reembolso de Costo Logístico | |
Reembolso por publicidad | |
Reembolsos sin categorizar | |
Reembolso por servicio almacenamiento | |
Reembolso por descarte de stock | |
Reembolso por almacenamiento prolongado | |
Reembolso de Impuestos | |
Reembolso Total | total de la categoría |
Ejemplo
curl "https://api.wivoanalytics.com/data?date=purchase_date&from=2025-01-01&to=2025-01-31&categories=income,costs" \
-H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI"
Cada ítem de data llega con el set base más las columnas de las categorías pedidas (en negrita las que aparecen solo por categories=income,costs):
{
"Orden": "1000000000000001",
"Ventas": "7500",
"Unidades": "1",
"Costo de Marketplace": "1500.00",
"Rentabilidad Marketplace": "6000.00",
"Ingreso Total": "7500.00",
"Cobro de Comisiones": "500",
"Costo de Comisiones": "500",
"Cobro de Envío": "1000",
"Costo Envío": "1000"
}
Una categoría no válida retorna HTTP 400 Bad Request.
10. Límites de uso (Rate limits)
- 5 peticiones por minuto por API Key
- 7.200 peticiones por día por API Key
- Superar cualquiera de los dos retorna HTTP 429 Too Many Requests
Los dos límites se aplican, no son una recomendación. Con limit=1000 (ver sección 7.2), 5 peticiones por minuto equivalen a unas 5.000 filas por minuto, así que el techo real de tu sincronización lo marca el tamaño de página que pidas: cuanto más grande, menos peticiones necesitas para el mismo dato.
Como buenas prácticas, implementa reintentos con backoff exponencial y agrupa la lógica para aprovechar al máximo cada petición.
11. Fechas y zonas horarias
La respuesta trae dos campos de fecha, y cada uno viene en una zona distinta. Es importante para cualquier cálculo o cruce con tus propias fuentes.
| Campo | Zona | Formato |
|---|---|---|
Fecha de actualización | UTC | 2026-08-14T16:00:42.286Z — con designador Z y milisegundos |
Fecha de compra | Hora local del canal de venta | 2026-08-14T08:12:59.000 — sin designador de zona |
Fecha de actualización
Viene siempre en UTC, con la Z y con milisegundos. Es el mismo valor sin importar por qué campo filtres, así que puedes parsearla directamente como instante.
Marca cuándo Wivo actualizó por última vez ese registro, y es el campo que se usa como cursor del flujo incremental de la sección 6.2.
Fecha de compra
Viene en la hora local del canal de venta, sin designador de zona. No la interpretes como UTC: la mayoría de los parsers asumen UTC cuando falta la Z, y eso desplaza la fecha varias horas.
Como la zona depende del canal, dos órdenes compradas en el mismo instante en canales de países distintos traen relojes distintos en este campo. Si necesitas comparar instantes o cruzar con fuentes en UTC, usa Fecha de actualización.
Zona de from y to
Se interpretan en la misma zona que el campo que estás filtrando, y meta.window te lo indica en cada respuesta:
| Filtro | Zona de from y to |
|---|---|
date=updated_at | UTC |
date=purchase_date | Hora local del canal, igual que el campo |
Con date=updated_at, el valor de Fecha de actualización que recibes se puede reenviar tal cual como from en el siguiente request.
Alcance de la búsqueda por fecha de actualización
Con date=updated_at, la respuesta incluye las subórdenes cuya fecha de compra esté dentro de los 120 días previos al inicio del rango consultado.
Cada respuesta declara ese límite en meta.window.purchaseDateFrom:
"window": {
"field": "updated_at",
"timezone": "UTC",
"purchaseDateFrom": "2026-04-16T00:00:00"
}
Para consultar compras anteriores a ese límite, usa date=purchase_date con el rango que necesites.
Declaración en la respuesta
meta.fields declara la zona de cada campo de fecha en cada respuesta, con las mismas claves que ves en data. Si tu integración necesita verificar el formato antes de parsear, léelo de ahí en vez de asumirlo.