Couriers de Perú · un solo API
Rastreo y agencias de couriers peruanos, detrás de un solo contrato.
Cada courier tiene su propio formato de guía, su propio vocabulario de estados y su propia idea de qué significa «no encontrado». Nosotros normalizamos eso a un modelo único — y te decimos, carrier por carrier, cuánta evidencia real respalda cada integración.
$ curl -H "X-API-Key: ..." \
"https://api.tracking-peru.com/v1/tracking?carrier=marvisur&number=V001-0000001"
{
"carrier": "marvisur",
"tracking_number": "V001-0000001",
"status": "delivered",
"delivered": true,
"terminal": true,
"events": [ ... ]
}- Couriers relevados
- 5
- Agencias sincronizadas
- 818
- Estados canónicos
- 11
- Zona horaria
- -05:00
Con su backend documentado y su evidencia archivada.
De 5 catálogos, con ubigeo y geolocalización.
Un vocabulario, no cinco. Con el literal del courier siempre adjunto.
Explícita en cada fecha. Ningún courier la manda.
Qué funciona hoy, sin maquillaje
Cada courier aporta dos cosas: rastrear envíos y publicar sus agencias. Un courier en ámbar no está «casi listo»: está implementado y esperando algo concreto, y su tarjeta dice qué falta.
- Rastreo de envíos
- 3/560 %
- Catálogo de agencias
- 5/5100 %
operativo y verificado en vivo
sincronizado, con ubigeo INEI
Busca una agencia
El mismo GET /v1/agencies que usarías tú, contra los catálogos de los couriers que publican uno. Cada resultado dice de dónde salió su ubigeo — y cuándo no lo pudimos resolver.
datos de ejemploEl API todavía no está desplegado, así que estas 10 agencias son ficticias y están para probar el buscador. No son el catálogo.
10 agencias
olvaAGENCIA DE EJEMPLO 01
AV ALFREDO BENAVIDES NRO 1851
MIRAFLORES · LIMA · LIMA
ubigeo 150122 · carrier
olvaAGENCIA DE EJEMPLO 02
JR DE LA UNION NRO 300
LIMA · LIMA · LIMA
ubigeo 150101 · carrier
marvisurAGENCIA DE EJEMPLO 03
AV LARCO NRO 745
MIRAFLORES · LIMA · LIMA
ubigeo 150122 · matched
marvisurAGENCIA DE EJEMPLO 04
FRENTE AL MERCADO CENTRAL, GARCI CARBAJAL
AREQUIPA
sin ubigeo · ubicación sin resolver
urbanoAGENCIA DE EJEMPLO 05
AV JAVIER PRADO ESTE NRO 4200
LIMA · LIMA · LIMA
ubigeo 150101 · carrier
urbanoAGENCIA DE EJEMPLO 06
CALLE COMERCIO NRO 128
TRUJILLO · LA LIBERTAD
sin ubigeo · ubicación sin resolver
shalomAGENCIA DE EJEMPLO 07
AV NICOLAS AYLLON NRO 2900
LIMA · LIMA
sin ubigeo · ubicación sin resolver
shalomAGENCIA DE EJEMPLO 08
AV EJERCITO NRO 510
AREQUIPA · AREQUIPA
sin ubigeo · ubicación sin resolver
cruzdelsurAGENCIA DE EJEMPLO 09
AV PASEO DE LA REPUBLICA NRO 5824
MIRAFLORES · LIMA · LIMA
ubigeo 150122 · matched
cruzdelsurAGENCIA DE EJEMPLO 10
AV LOS INCAS NRO 1140
CUSCO · CUSCO
sin ubigeo · ubicación sin resolver
Ninguna agencia coincide. Prueba con el distrito solo, o quita el filtro de courier.
Pregúntale, o deja que te avise
Las dos formas devuelven el mismo Shipment: estado canónico, timelineordenado con zona horaria explícita, bultos y pago. El literal del courier siempre adjunto en status_raw, porque normalizar no debería significar perder el dato original.
Quien aprende a leer una respuesta ya sabe leer el webhook: el objeto que llega a tu endpoint es exactamente el mismo.
Cuando preguntas tú
Toca un endpoint para ver qué contesta.
{
"carriers": [
{
"id": "marvisur",
"name": "Expreso Marvisur",
"enabled": true,
"verified": {
"level": "live",
"last_probe": "2026-08-03T22:10:23Z",
"notes": "hit y miss reproducidos byte a byte contra el upstream"
},
"id_format": "V###-####### (serie V + 3 dígitos, distinto de V000)",
"examples": ["V999-9999999"],
"detection": "strong",
"requires_code": false,
"requires_end_user_auth": false,
"anonymous_timeline": true,
"provides": {
"parties": true, "event_location": true,
"packages": true, "payment_status": true
}
}
]
}Es el endpoint de descubrimiento: tu código puede leer el estado en vez de confiar en esta página.
{
"carrier": "marvisur",
"tracking_number": "V001-0000001",
"status": "delivered",
"status_raw": "ENTREGADO",
"delivered": true,
"terminal": true,
"events": [
{
"seq": 0,
"status": "registered",
"status_raw": "RECEPCION",
"description": "SU ENVÍO FUÉ RECEPCIONADO EN NUESTRA SEDE",
"occurred_at": "2023-07-01T07:54:06-05:00",
"time_precision": "second",
"location": { "name": "GARCI CARBAJAL", "structured": false }
}
],
"detail": "full",
"fetched_at": "2026-08-03T22:10:31Z"
}El literal del courier viaja siempre en status_raw: normalizar no debería significar perder el dato original.
{
"carrier": "marvisur",
"tracking_number": "V001-0000001",
"status": "delivered",
"status_raw": "ENTREGADO",
"delivered": true,
"terminal": true,
"events": [
{
"seq": 0,
"status": "registered",
"status_raw": "RECEPCION",
"description": "SU ENVÍO FUÉ RECEPCIONADO EN NUESTRA SEDE",
"occurred_at": "2023-07-01T07:54:06-05:00",
"time_precision": "second",
"location": { "name": "GARCI CARBAJAL", "structured": false }
}
],
"detail": "full",
"fetched_at": "2026-08-03T22:10:31Z"
}Idéntica respuesta. Existe porque un carrier explícito en la ruta se cachea y se loguea mejor que un query param.
{
"items": [
{ "custom_id": "a", "carrier": "marvisur", "number": "V001-0000001" },
{ "custom_id": "b", "carrier": "marvisur", "number": "V999-9999999" }
]
}{
"results": [
{ "custom_id": "a", "carrier": "marvisur",
"number": "V001-0000001",
"ok": true, "shipment": { "…": "el Shipment completo" } },
{ "custom_id": "b", "carrier": "marvisur",
"number": "V999-9999999",
"ok": false,
"error": {
"code": "not_found",
"message": "no se encontró el envío"
} }
],
"summary": { "total": 2, "ok": 1, "failed": 1 }
}Responde 200 aunque haya ítems fallidos: el error va por ítem. Aquí no hay detección automática.
{
"agencies": [
{
"carrier": "olva",
"id": "948",
"name": "AGENTE OLVA MIRAFLORES - BENAVIDES C18",
"location": {
"address": "AV ALFREDO BENAVIDES NRO 1851",
"department": "LIMA", "province": "LIMA",
"district": "MIRAFLORES",
"ubigeo": "150122", "structured": true
},
"ubigeo_level": "district",
"ubigeo_source": "carrier",
"geo": { "lat": -12.1265813, "lng": -77.0132440 },
"hours": {
"weekly": [
{ "weekday": 1,
"spans": [ { "open": "08:00", "close": "20:00" } ] }
],
"structured": true,
"time_zone": "America/Lima"
},
"kind": "agent",
"services": { "dropoff": true, "pickup": true },
"synced_at": "2026-08-03T22:10:19Z"
}
],
"pagination": {
"page": 1, "per_page": 20, "total": 417, "total_pages": 21
},
"meta": { "sources": [ "…frescura por carrier" ] }
}ubigeo_source es lo que hace auditable el join: "carrier" lo dio el courier, "matched" lo resolvimos por texto y puede fallar.
{
"ubigeo": "150101",
"level": "district",
"carriers": [
{ "carrier": "olva", "count": 5, "kinds": { "agent": 5 } }
],
"not_covered": [],
"unknown": [
{
"carrier": "marvisur",
"reason": "el catálogo de este carrier no resuelve a nivel
district; la consulta pide ese nivel"
}
]
}Solo un courier que publica distrito puede recibir un "no cubre". El que da su ubicación como texto libre cae en "unknown", con el motivo escrito.
{
"carrier": "olva",
"id": "579",
"name": "TIENDA CHACHAPOYAS",
"location": {
"address": "JR. AMAZONAS 1120",
"department": "AMAZONAS", "province": "CHACHAPOYAS",
"district": "CHACHAPOYAS",
"ubigeo": "010101", "structured": true
},
"ubigeo_level": "district",
"ubigeo_source": "carrier",
"kind": "office",
"services": { "dropoff": true, "pickup": true },
"synced_at": "2026-08-03T22:10:19Z"
}Devuelve la Agency pelada, sin envoltorio. La PK natural es el par (carrier, id).
{ "url": "https://tuservicio.com/hooks/tracking" }{
"webhook": {
"id": 11,
"url": "https://tuservicio.com/hooks/tracking",
"enabled": true,
"created_at": "2026-08-04T10:00:00Z"
},
"signing_secret": "9f2b…64 hex…c1",
"verified": true,
"usage": { "active_subscriptions": 0, "max_subscriptions": 50 }
}Se guarda deshabilitado y recibe un ping firmado: solo se habilita si devuelves el challenge como cuerpo.
{
"carrier": "marvisur",
"number": "V001-0000001",
"code": "opcional-2do-factor"
}{
"subscription": {
"id": 4821,
"carrier": "marvisur",
"tracking_number": "V001-0000001",
"status": "active",
"next_poll_at": "2026-08-04T10:16:00Z",
"created_at": "2026-08-04T10:00:00Z",
"expires_at": "2026-08-25T10:00:00Z"
},
"outcome": "created",
"usage": { "active_subscriptions": 8, "max_subscriptions": 50 }
}carrier es OBLIGATORIO: no hay detección automática. Un rastreo mal detectado se ve en la respuesta; una suscripción mal detectada no la mira nadie durante semanas.
Cuando prefieres que te avise
Registras un endpoint HTTPS, suscribes un envío y recibes un POST firmado cada vez que ese envío cambia de estado. Un poller de fondo hace el trabajo aburrido: acelera cerca de la entrega y se apaga solo cuando el envío termina.
- 1
POST /v1/webhooksguarda el signing secret - 2
POST /v1/tracking/subscriptionsun envío por request - 3
tu endpoint recibeverifica la firma y responde 2xx
Cuatro eventos:tracking.updated·tracking.delivered·tracking.expired·webhook.ping
Hay 6 decisiones del subsistema que cambian cómo escribes el receptor —reintentos, deduplicación, verify-before-enable, guarda anti-SSRF—. Están todas en la documentación.
POST /hooks/tracking HTTP/1.1
User-Agent: tracking-peru-webhooks/1
X-Webhook-Event: tracking.updated
X-Webhook-Event-Id: evt_4821_tracking.updated_v1-1010100000000
X-Webhook-Attempt: 1
X-Webhook-Signature: t=1785578400,v1=6f1a…
{
"id": "evt_4821_tracking.updated_v1-1010100000000",
"event": "tracking.updated",
"occurred_at": "2026-08-04T14:02:11Z",
"data": {
"subscription_id": 4821,
"carrier": "marvisur",
"tracking_number": "V001-0000001",
"status": "at_destination",
"previous_status": "in_transit",
"delivered": false,
"terminal": false,
"change": { "appeared": ["at_destination"] },
"shipment": { "…": "el Shipment completo, con su timeline" }
}
}Un envío, once estados, un solo vocabulario
Cada courier nombra los hitos a su manera y ninguno usa los mismos. Este es el recorrido al que los traducimos —el mismo para los cinco—, con el literal original siempre adjunto en status_raw.
registeredEl courier lo recibió y lo dio de alta.
at_originEn la agencia de origen, sin salir todavía.
in_transitViajando entre sedes.
at_destinationLlegó a la ciudad de destino.
out_for_deliveryoavailable_for_pickupSale a repartir, o queda esperando en agencia.
deliveredEntregado. No hay estado después de este.
De su palabra a la nuestra
Validados contra respuestas reales: 3 de 5 couriers (Expreso Marvisur, Urbano Express y Cruz del Sur Cargo). Olva Courier y Shalom siguen pendientes — no agregamos estados estimados mientras no lleguen sus respuestas.
- marvisur devolvió"RECEPCION"
registered
Y los que se salen del camino
- delayed
- Sigue en curso, solo que tarde.
- returning
- Vuelve al origen. No es terminal: todavía puede entregarse.
- returned
- Volvió y quedó ahí.
- exception
- Algo que el courier reporta y no encaja en ningún otro estado.
Una key, un plan, sin sorpresas
Los cuatro llegan a los mismos endpoints y a los mismos carriers. Lo que cambia es cuántos envíos puedes tener vigilados a la vez —ese es el medidor— y cómo te respondemos cuando algo se rompe.
betaEl precio que tomes ahora te queda fijo, también cuando salgamos de beta.
Free
S/0para siempre
sin tarjeta, no vence
Para integrar, probar en serio y quedarte. No vence.
- Webhooks activos
- 3
- Requests por minuto
- 30
- Consultas de rastreo
- hasta 1.000 al mes
- Soporte
- documentación y comunidad
Básico
S/25al mes
o S/ 250 al año — 2 meses gratis
Cuando el rastreo ya es parte de tu operación.
- Webhooks activos
- 50
- Requests por minuto
- 60
- Consultas de rastreo
- sin tope
- Soporte
- por correo
Pro
recomendadoS/79al mes
o S/ 790 al año — 2 meses gratis
Volumen alto y respuesta rápida cuando algo se rompe.
- Webhooks activos
- 200
- Requests por minuto
- 120
- Consultas de rastreo
- sin tope
- Soporte
- prioritario
A medida
A convenir
Volumen alto, requisitos propios o facturación distinta.
- Webhooks activos
- 200+
- Requests por minuto
- negociado
- Consultas de rastreo
- sin tope, con SLA
- Soporte
- dedicado, con SLA
Los tres incluyen
- Todos los carriers — ninguno queda reservado para el plan caro
- Todos los endpoints: rastreo, agencias y cobertura por ubigeo
- Webhooks firmados con HMAC, con reintentos y deduplicación
- Cada carrier con su nivel de evidencia publicado
- Los carriers que se vayan completando, sin costo extra
Hoy eso son 3 couriers con rastreo operativo y 5 catálogos de agencias (818 puntos sincronizados). Los que faltan entran a tu key sin que pagues de nuevo.
El límite por minuto es un guardarraíl
No es una cuota que compras: está para que un bucle mal escrito no tumbe el servicio para el resto. Nadie sube de plan por el RPM — se sube por cuántos envíos puede vigilar a la vez, que es lo que de verdad se mide.
cómo se paga hoy
El pago automático todavía no está montado. Hoy escribes, coordinamos el pago y te emitimos la key el mismo día — sin formulario y sin tarjeta guardada.
Los botones abren un correo con el plan ya puesto en el asunto. Cuando el link de pago esté, el mismo botón lleva al checkout y la key se emite sola.
Empieza gratis, y no vence
Déjanos tu correo y te emitimos la key en el momento. El plan Free no vence y no pide tarjeta: si tu volumen crece, subes de plan cuando quieras. Y si el courier que te importa está en ámbar, dilo — eso prioriza el orden en que se completan.
- webhooks activos
- 3
- consultas al mes
- 1.000
- req / minuto
- 30
- para siempre
- S/ 0
envíos vigilados a la vez
de rastreo bajo demanda
guardarraíl contra ráfagas
no vence ni pide tarjeta
- Ves la key en pantalla apenas la pides, y además te llega por correo con su fecha de vencimiento.
- Una key gratuita por dirección de correo. Si esa dirección ya tiene la suya, te lo decimos en el momento.
- El plan Free no vence: no te cobramos, no se renueva sola y no hay nada que cancelar.
- Mientras dure la beta, el precio que tomes queda fijo — y los cinco carriers rastreen en vivo para que termine.
¿Un plan pago o algo a medida?
hola@tracking-peru.com- Autenticación
- X-API-Key
- Formato
- JSON · REST
- Webhooks
- firmados · HMAC