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, comprador)
- Atributos del producto (marca, categoría, publicación) y tu catálogo interno del Maestro de productos
- Costos de la orden (comisiones, envío, logística)
- Costos adicionales (almacenamiento fulfillment, publicidad)
- Métricas derivadas: rentabilidad, costos y porcentajes. Wivo calcula la rentabilidad en tres alcances según qué costos descuenta — los tres llegan por la API, y están explicados en Los tres alcances 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
Precio. El acceso a la API de datos es un add-on, aparte de tu plan: tiene 30 días de prueba gratis y, después, cuesta un 30% adicional sobre el valor de tu plan base. Se factura por separado del plan. El Maestro de productos por API viene incluido en el plan Pro y no paga este add-on.
¿Vas a enviar las órdenes de tu tienda propia? La API de órdenes (beta) viene incluida en el plan Pro y no necesita esta habilitación: tu API Key está en la vista Integración con API.
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,products,orders,channels,internal-cost. Agrega columnas a cada fila de la respuesta —medidas, atributos, o ambos según la categoría— (ver sección 9). Si se omite, se devuelve exactamente el conjunto basetimezone: opcional, único valor aceptadoutc. DevuelveFecha de compracomo instante en UTC en vez de la hora local del canal. Si se omite, nada cambia; cualquier otro valor retorna HTTP 400 Bad Request (ver sección 11)
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 y de si envíastimezone(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, tal cual la escribe y en su propio idioma: Wivo no la traduce ni la normaliza. Un mismo tipo de cobro puede llegar comoCobro por Productos patrocinadosdesde una cuenta de un marketplace en español y comoTarifa pelo serviço de armazenamento Fulldesde una de Brasil. No conviene clasificar por este texto: para eso está la medida de costo que acompaña a la filaCosto 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
Wivo Analytics tiene una vista de Rentabilidad que muestra estos mismos datos. Si usas la plataforma y quieres que tus cifras coincidan con las de esa vista, o si simplemente quieres el recorte con el que Wivo calcula rentabilidad, es este: órdenes y publicaciones, en estado Regular, dejando fuera las filas sin información de pago.
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"
Ese recorte define qué filas entran. Qué alcance de rentabilidad quieres leer define qué columnas pides, y se agrega con categories. Los tres alcances —Marketplace, Comercial y Total— se diferencian por los costos que descuentan, y están explicados en Los tres alcances de rentabilidad:
| Alcance | Columnas que lo componen | Qué agregar a la petición |
|---|---|---|
| Marketplace | Rentabilidad Marketplace, % Rentabilidad Marketplace, Costo de Marketplace | nada: vienen en el conjunto base |
| Comercial | Rentabilidad Comercial, % Rentabilidad Comercial, Costo Comercial, % Costo Comercial | &categories=internal-cost,costs |
| Total | Rentabilidad Total, % Rentabilidad Total, Costo Total, % Costo Total | &categories=internal-cost,costs |
Comercial y Total se piden con las mismas categorías, así que una sola petición trae las columnas de los dos alcances a la vez. No hace falta pedirlos por separado: eliges cuál leer según la pregunta que estés respondiendo.
Si comparas contra la plataforma y los totales no cuadran, revisa además la ventana de fechas y su zona horaria (ver sección 11): es la causa más frecuente.
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.
Este ejemplo es la respuesta por defecto, la que recibes si no envías categories. Con ese parámetro cada ítem llega con columnas adicionales —atributos del producto y de la orden, el desglose de costos, los alcances de rentabilidad— y el conjunto base no cambia (ver sección 9).
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. Columnas adicionales (categories)
Por defecto, cada ítem de data trae un conjunto base: los atributos de la orden y del producto que ves en el ejemplo de la sección 8, más 7 medidas.
Ventas, Unidades, Costo de Marketplace, Rentabilidad Marketplace, % Rentabilidad Marketplace, Costo Envío, Costo de Comisiones.
El parámetro categories agrega columnas a cada ítem de data: el desglose detrás de esos totales, atributos adicionales del producto y de la orden, y los alcances de rentabilidad. Pides una o más categorías separadas por coma.
Una categoría no significa “todo lo de X”. Significa “lo de X que no viene ya en el conjunto base”.
categories=channelsagregaTipo de Canal, mientrasCanalyCuentasiguen llegando siempre, la pidas o no. Con las medidas ocurre lo mismo:Ventaspertenece aincomey llega igual sin pedir nada.
El conjunto base llega siempre. Pidas las categorías que pidas, esas columnas aparecen con las mismas claves y los mismos valores:
categoriesúnicamente agrega, nunca quita ni renombra.
Una misma orden puede ocupar varias filas, y dos de ellas pueden coincidir en todas las columnas que recibes y diferir solo en los montos. No uses las columnas de la respuesta como clave única para deduplicar: perderías registros.
Los porcentajes son de la fila, no de la ventana.
% Costo Totalo% Rentabilidad Totalson la tasa de esa fila sola. Para la tasa de un conjunto —un día, una marca, un canal— suma los montos y haz la división tú: promediar los porcentajes de las filas da otro número.
Las columnas se identifican por su nombre exacto (el mismo que aparece como clave en el JSON, siempre en español). Si pides varias categorías y comparten alguna columna, aparece una sola vez (no se duplica). Los totales
VentasyCosto de Marketplaceya vienen en el conjunto 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.
El desglose de comisiones describe el cobro, no lo reemplaza.
Cobro de ComisionesyReembolso de Comisionesson los totales que informa el marketplace;Comisión Variable,Comisión FijayComisión por Cuotas—con sus tres reembolsos— te dicen de qué se componen. Para conciliar montos, usa los totales.
Comisiones: Cobro de Comisiones, Comisión Variable, Comisión Fija, Comisión por Cuotas, Reembolso de Comisiones, Reembolso de Comisión Variable, Reembolso de Comisión Fija, Reembolso de Comisión por Cuotas, 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
Además del desglose por sección, costs trae los totales y las tasas de la vista de Rentabilidad:
Totales de la venta: Venta Cancelada, Cobro Total, Cobros Comercial
Costo por alcance: Costo de Marketplace (Marketplace), Costo Comercial (Comercial), Costo Total (Total)
Tasas: % Costo de Marketplace, % Costo Comercial, % Costo Total, % Cobro de Comisiones, % Costo de Comisiones, % Costo de Publicidad, % Costo Envío, % Costo Producto
Qué mide cada alcance está explicado en Los tres alcances de rentabilidad.
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 Comisión Variable | |
Reembolso de Comisión Fija | |
Reembolso de Comisión por Cuotas | |
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 |
products — Atributos del producto
| Columna | |
|---|---|
Marca | |
Categoría | |
Id publicación | Identificador de la publicación en el marketplace |
Producto, SKU Producto, SKU Marketplace, Variante y SKU Variante ya vienen en el conjunto base.
orders — Atributos de la orden
| Columna | |
|---|---|
Comprador | |
Tipo de Despacho del Marketplace | Modalidad de despacho tal como la nombra el marketplace |
Fecha de Pago |
Tipo de DespachoyTipo de Despacho del Marketplaceson dos columnas distintas, no dos nombres del mismo dato. La primera viene en el conjunto base y distingue fulfillment de no fulfillment. La segunda llega concategories=ordersy trae la modalidad que declara el marketplace.
Orden, Nro. suborden, Nro. Paquete, N° de Liquidación, Fecha de compra y los estados ya vienen en el conjunto base.
channels — Atributos del canal
| Columna | |
|---|---|
Tipo de Canal | Distingue, por ejemplo, un marketplace de una tienda propia |
Canal y Cuenta ya vienen en el conjunto base.
internal-cost — Catálogo interno y rentabilidad
Trae tu catálogo interno —el que cargas en el Maestro de productos— y el costo de producto con las rentabilidades que dependen de él.
| Columna | |
|---|---|
Marca Interna | |
Categoría Interna | |
Producto Interno | |
SKU Producto Interno | |
Costo de Producto | |
Rentabilidad Comercial | |
% Rentabilidad Comercial | |
Rentabilidad Total | |
% Rentabilidad Total |
Los costos de cada alcance —Costo Comercial, Costo Total y sus tasas— llegan con categories=costs.
Los tres alcances de rentabilidad
Wivo calcula tres rentabilidades sobre la misma venta, y las tres son correctas a la vez: lo que cambia es qué costos descuenta cada una. Las tres se piden desde la API.
| Alcance | Qué descuenta | Para qué sirve |
|---|---|---|
| Marketplace | Solo los cobros del canal, sin el costo de tu producto | Conciliar contra lo que el canal te deposita |
| Comercial | Los cobros del canal atribuibles a la venta, más el costo del producto | Decidir precio y surtido de un producto, sin mezclar gastos que no dependen de esa venta |
| Total | Todo: los ocho tipos de costo del canal más el costo del producto | La mirada del negocio: qué queda al final |
Comercial es Total sin los cuatro cobros que no son consecuencia de haber vendido esa unidad: publicidad, almacenamiento fulfillment, gestión de cuenta y financiamiento. Muchos vendedores los pagan de otro presupuesto y necesitan la rentabilidad del producto sin ellos; en el alcance Total siguen sumando. Los cuatro llegan igual en la respuesta con categories=costs, cada uno en su sección.
Qué significa cada alcance para tu negocio y cuándo conviene cada uno está en Tipos de rentabilidad en Wivo.
Cómo se compone la rentabilidad
Con categories=income,costs,reimbursement,internal-cost recibes las cuatro columnas que la forman, y puedes reproducir el cálculo fila por fila:
Rentabilidad Total = Ingreso Total - Cobro Total + Reembolso Total - Costo de Producto
Rentabilidad Comercial = Ingreso Total - Cobros Comercial + Reembolso Total - Costo de Producto
La única diferencia entre las dos es el cobro que restan: Cobros Comercial es Cobro Total sin esos cuatro cobros. La resta entre las dos rentabilidades te dice cuánto pesan en la fila.
Cuando el costo de producto todavía no está cargado
El costo de producto lo cargas tú en Wivo: el marketplace no lo informa. Cuando una fila todavía no lo tiene cargado en el Maestro de productos, Costo de Producto llega en 0, no vacío. Ese 0 entra igual en el cálculo, así que Rentabilidad Comercial y Rentabilidad Total quedan más altas de lo que serán una vez que cargues el costo.
Tenlo presente antes de consolidar rentabilidad sobre un conjunto de filas. La práctica recomendada es cruzar el SKU Producto Interno de cada fila contra tu propio maestro de costos y decidir qué hacer con las que no encuentres —excluirlas, imputarles un costo o revisarlas— en vez de sumar todo de corrido. Rentabilidad Marketplace no descuenta el costo de producto, así que no se ve afectada.
Ejemplos
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 conjunto 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"
}
Con categories=products,orders,channels,internal-cost la misma fila llega con los atributos del catálogo y los otros dos alcances de rentabilidad:
{
"Orden": "1000000000000001",
"Marca": "Marca Demostración",
"Categoría": "Categoría Demostración",
"Id publicación": "9000000000001",
"Comprador": "Comprador Demostración",
"Tipo de Despacho del Marketplace": "Envío a domicilio",
"Tipo de Canal": "Marketplace",
"Marca Interna": "Marca Interna Demostración",
"Categoría Interna": "Categoría Interna Demostración",
"Producto Interno": "Producto Interno Demostración",
"SKU Producto Interno": "SKU-INT-001",
"Ventas": "7500",
"Costo de Producto": "3000",
"Rentabilidad Comercial": "3300.00",
"% Rentabilidad Comercial": "44.0",
"Rentabilidad Total": "3000.00",
"% Rentabilidad Total": "40.0"
}
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 |
Con categories=orders se suma una tercera columna de fecha, Fecha de Pago, en el mismo formato que Fecha de compra: sin designador de zona. Trátala con el mismo criterio.
Fecha de compra también puede pedirse en UTC, con un parámetro: ver Pedir la fecha de compra en UTC.
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, pide este mismo campo en UTC con timezone=utc (ver abajo). Fecha de actualización es la referencia cuando lo que te interesa es cuándo se actualizó el registro, no cuándo se compró.
Pedir la fecha de compra en UTC
Con timezone=utc en la petición, Fecha de compra llega como instante en UTC: el mismo formato que Fecha de actualización, con designador Z y milisegundos. Así puedes parsearla sin conocer la zona del canal.
curl "https://api.wivoanalytics.com/data?date=purchase_date&from=2025-01-01&to=2025-01-31&timezone=utc" -H "Ocp-Apim-Subscription-Key: TU_API_KEY_AQUI"
Qué formato tiene cada campo de fecha en cada caso:
| Campo | Sin timezone | Con timezone=utc |
|---|---|---|
Fecha de compra | Hora local del canal, sin designador de zona | Instante en UTC, con Z |
Fecha de actualización | UTC, con Z | UTC, con Z |
Fecha de Pago (con categories=orders) | Hora local del canal, sin designador de zona | Hora local del canal, sin designador de zona |
Un ejemplo con el desplazamiento a la vista: una compra registrada a las 08:12:59 en un canal que opera en UTC−4 llega como 2026-08-14T08:12:59.000 sin el parámetro, y como 2026-08-14T12:12:59.000Z con él.
timezone también define la zona de la ventana. Con timezone=utc, from y to se interpretan en UTC, así que un mismo rango de fechas no selecciona exactamente las mismas compras que sin el parámetro: las de las primeras y últimas horas del rango entran o salen según el desplazamiento del canal. Si comparas las dos respuestas, espera totales distintos.
Fecha de Pago llega siempre en la hora local del canal, con el parámetro y sin él. meta.fields declara su zona en cada respuesta.
El único valor aceptado es utc, en minúsculas. Cualquier otro —local, UTC en mayúsculas o el nombre de una zona— retorna HTTP 400 Bad Request, indicando el valor recibido y cuáles son los válidos.
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 |
date=purchase_date + timezone=utc | UTC |
Con date=updated_at, el valor de Fecha de actualización que recibes se puede reenviar tal cual como from en el siguiente request. Si filtras por updated_at, timezone no altera la ventana: esa ventana se interpreta en UTC en todos los casos.
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: Fecha de compra, Fecha de actualización y, cuando pides categories=orders, Fecha de Pago. La declaración refleja lo que pediste, así que no necesitas deducir la zona del formato ni recordar con qué parámetros armaste la petición. Si tu integración verifica el formato antes de parsear, léelo de ahí en vez de asumirlo.
Así se ve una respuesta pedida con timezone=utc y categories=orders:
"window": { "field": "purchase_date", "timezone": "UTC" },
"fields": {
"Fecha de compra": { "type": "datetime", "timezone": "UTC" },
"Fecha de actualización": { "type": "datetime", "timezone": "UTC" },
"Fecha de Pago": { "type": "datetime", "timezone": "local" }
}