Introducción
¿Qué es la API de Zeolos?
La API de Zeolos es una solución REST para calcular las emisiones de CO₂ de expediciones de transporte de mercancías. Permite a empresas, operadores logísticos y desarrolladores integrar cálculos precisos de huella de carbono en sus sistemas.
Características Principales
Multi-modal
Soporta carretera, marítimo, aéreo y ferrocarril en una misma expedición
Certificado
Factores de emisión basados en GLEC Framework y EN 16258
Rápido
Cálculo en tiempo real con respuesta en segundos
Batch Processing
Procesa hasta 500 expediciones en paralelo
¿Para Quién es Esta API?
- Empresas de logística: Calculen la huella de carbono de sus operaciones
- Operadores de transporte: Ofrezcan datos de sostenibilidad a sus clientes
- Plataformas SaaS: Integren cálculos de emisiones en sus productos
- Consultorías: Generen reportes de sostenibilidad automatizados
- Desarrolladores: Construyan soluciones verdes para el sector transporte
Casos de Uso Comunes
| Caso de Uso | Descripción |
|---|---|
| Cálculo por envío | Calcular emisiones de cada expedición individual en tiempo real |
| Reportes mensuales | Generar informes de sostenibilidad agregados por periodo |
| Comparación de rutas | Evaluar diferentes opciones de transporte y elegir la más sostenible |
| Certificación ISO 14083 | Obtener datos para certificaciones de huella de carbono |
| Compensación de CO₂ | Calcular emisiones para programas de compensación de carbono |
Tipos de Emisiones
La API calcula tres tipos de emisiones según el estándar internacional:
| Tipo | Nombre | Descripción |
|---|---|---|
WTW |
Well-to-Wheel | Emisiones totales del ciclo completo (producción + combustión) |
WTT |
Well-to-Tank | Emisiones de producción y distribución del combustible |
TTW |
Tank-to-Wheel | Emisiones directas del vehículo durante el transporte |
scope: 1 en tu request.
Requisitos Previos
Para usar esta API necesitas:
- ✅ Una suscripción activa al servicio de Zeolos
- ✅ Tu API token (proporcionado al contratar el servicio)
- ✅ Conocimientos básicos de APIs REST
- ✅ Un cliente HTTP (curl, Postman, o librerías como requests/fetch)
Quick Start
Crea tu primera expedición en 5 minutos. Este ejemplo calcula las emisiones de un envío por carretera entre dos ciudades españolas.
Paso 1: Obtén tu API Token
Tu API token se proporciona al contratar el servicio. Guárdalo en un lugar seguro. Tiene el siguiente formato:
abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567890abcd
Paso 2: Haz tu Primera Request
Vamos a calcular las emisiones de un envío de 1,500 kg entre Madrid y Barcelona por carretera.
Usando cURL
curl -X POST "https://api.zeolos.es/api/expeditions/" \
-H "X-API-Key: TU_TOKEN_AQUI" \
-H "Content-Type: application/json" \
-d '{
"client_name": "Mi Empresa",
"expedition_id": "EXP-001",
"expedition_date": "2025-11-08",
"cargo": {
"weight": 1500,
"weight_unit": "kg",
"is_refrigerated": false
},
"route": [
{
"location": {
"address": "Madrid, España"
}
},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
},
{
"location": {
"address": "Barcelona, España"
}
}
]
}'
Usando Python
import requests
API_TOKEN = "TU_TOKEN_AQUI"
BASE_URL = "https://api.zeolos.es/api"
headers = {
"X-API-Key": API_TOKEN,
"Content-Type": "application/json"
}
payload = {
"client_name": "Mi Empresa",
"expedition_id": "EXP-001",
"expedition_date": "2025-11-08",
"cargo": {
"weight": 1500,
"weight_unit": "kg",
"is_refrigerated": False
},
"route": [
{"location": {"address": "Madrid, España"}},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
},
{"location": {"address": "Barcelona, España"}}
]
}
response = requests.post(f"{BASE_URL}/expeditions/", headers=headers, json=payload)
if response.status_code == 201:
data = response.json()
print(f"✅ Expedición creada exitosamente")
print(f"📏 Distancia: {data['results']['total_distance_km']} km")
print(f"🌍 CO₂ WTW: {data['results']['total_emission_wtw']} kg")
print(f"🔥 CO₂ TTW: {data['results']['total_emission_ttw']} kg")
else:
print(f"❌ Error {response.status_code}: {response.json()}")
Usando JavaScript (Node.js / Browser)
const API_TOKEN = "TU_TOKEN_AQUI";
const BASE_URL = "https://api.zeolos.es/api";
const payload = {
client_name: "Mi Empresa",
expedition_id: "EXP-001",
expedition_date: "2025-11-08",
cargo: {
weight: 1500,
weight_unit: "kg",
is_refrigerated: false
},
route: [
{ location: { address: "Madrid, España" } },
{
transport_mode: "road",
details: {
vehicle_type: "rigid_truck_12_20t",
fuel_type: "diesel",
load_level: "average_mixed"
}
},
{ location: { address: "Barcelona, España" } }
]
};
fetch(`${BASE_URL}/expeditions/`, {
method: 'POST',
headers: {
'X-API-Key': API_TOKEN,
'Content-Type': 'application/json'
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
if (data.status === 'COMPLETED') {
console.log('✅ Expedición creada');
console.log(`📏 Distancia: ${data.results.total_distance_km} km`);
console.log(`🌍 CO₂: ${data.results.total_emission_wtw} kg`);
} else {
console.error('❌ Error:', data);
}
})
.catch(error => console.error('❌ Error:', error));
Paso 3: Interpreta la Respuesta
Si todo va bien, recibirás un 201 Created con esta estructura:
{
"client_name": "Mi Empresa",
"expedition_id": "EXP-001",
"expedition_date": "2025-11-08",
"status": "COMPLETED",
"error_message": "",
"scope": 3,
"cargo": {
"weight": 1500.0,
"weight_unit": "kg",
"is_refrigerated": false,
"teu_equivalent_units": "average"
},
"route": [
{
"location": {
"query": "Madrid, España"
}
},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
},
"results": {
"distance_km": 621.45,
"emission_wtw": 1186.49,
"emission_wtt": 273.33,
"emission_ttw": 913.16
}
},
{
"location": {
"query": "Barcelona, España"
}
}
],
"results": {
"total_distance_km": 621.45,
"total_emission_wtw": 1186.49,
"total_emission_wtt": 273.33,
"total_emission_ttw": 913.16
},
"created_at": "2025-11-08T10:30:00Z",
"updated_at": "2025-11-08T10:30:05Z"
}
Entendiendo los Resultados
| Campo | Valor | Significado |
|---|---|---|
status |
COMPLETED | ✅ Cálculo exitoso |
total_distance_km |
621.45 | Distancia real de la ruta calculada |
total_emission_wtw |
1186.49 | Emisiones totales (kg CO₂ equivalente) |
total_emission_ttw |
913.16 | Emisiones directas del vehículo |
total_emission_wtt |
273.33 | Emisiones de producción del combustible |
Has calculado tu primera expedición. Este envío de 1,500 kg entre Madrid y Barcelona genera 1,186.49 kg de CO₂ (equivalente a 1.19 toneladas).
Próximos Pasos
Conceptos Básicos
Antes de profundizar en la API, es importante entender algunos conceptos fundamentales que te ayudarán a usarla de manera efectiva.
¿Qué es una Expedición?
Una expedición representa un envío de mercancías desde un origen hasta un destino, pasando potencialmente por múltiples tramos con diferentes modos de transporte.
Componentes de una Expedición
- Información general: Cliente, fecha, ID único
- Carga (cargo): Peso, unidad, refrigeración
- Ruta (route): Secuencia de ubicaciones y transportes
- Resultados: Distancias y emisiones calculadas
Estructura de una Ruta
La ruta es un array alternado de ubicaciones y transportes, siempre empezando y terminando con una ubicación:
route: [
location, // Origen
transport, // Cómo viajar
location, // Destino (puede ser origen del siguiente tramo)
transport, // Siguiente transporte
location // Destino final
]
Regla Importante
La ruta SIEMPRE debe tener un número impar de elementos.
Mínimo 3: [location, transport, location]
Ejemplo: Ruta Simple (1 tramo)
{
"route": [
{"location": {"address": "Madrid"}}, // Origen
{"transport_mode": "road", "details": {...}}, // Transporte
{"location": {"address": "Barcelona"}} // Destino
]
}
Ejemplo: Ruta Multi-modal (3 tramos)
{
"route": [
{"location": {"address": "Madrid"}}, // Origen
{"transport_mode": "road", "details": {...}}, // Tramo 1: Carretera
{"location": {"code": "BCN"}}, // Puerto Barcelona
{"transport_mode": "sea", "details": {}}, // Tramo 2: Marítimo
{"location": {"code": "ITGOA"}}, // Puerto Génova
{"transport_mode": "road", "details": {...}}, // Tramo 3: Carretera
{"location": {"address": "Milano, Italia"}} // Destino final
]
}
Tipos de Ubicaciones (Locations)
Existen tres formas de especificar una ubicación:
| Tipo | Cuándo Usar | Ejemplo | Ventajas |
|---|---|---|---|
| Address | Direcciones específicas | "Calle Alcalá 15, Madrid" |
Flexibilidad, cualquier dirección |
| Code (IATA) | Aeropuertos | "MAD" (3 caracteres) |
Rápido, sin geocoding necesario |
| Code (UN/LOCODE) | Puertos marítimos | "ESBCN" (5 caracteres) |
Estándar internacional |
Modos de Transporte
La API soporta cuatro modos de transporte:
| Modo | Código | Parámetros Requeridos | Uso Común |
|---|---|---|---|
| Carretera | road |
vehicle_type, fuel_type, load_level | Transporte terrestre, última milla |
| Marítimo | sea |
Ninguno | Transporte intercontinental, contenedores |
| Aéreo | air |
Ninguno | Carga urgente, larga distancia |
| Ferrocarril | rail |
Ninguno | Transporte continental, grandes volúmenes |
Flujo de Cálculo
Cuando envías una expedición, la API ejecuta estos pasos automáticamente:
Verifica que todos los datos sean correctos
Convierte direcciones en coordenadas GPS (si es necesario)
Determina la distancia real de cada tramo
Selecciona los factores de emisión apropiados
Calcula CO₂ para cada tramo y totaliza
Estados de una Expedición
Una expedición puede tener los siguientes estados:
| Estado | Descripción | Acción |
|---|---|---|
PENDING |
Expedición creada, esperando procesamiento | Estado transitorio inicial |
CALCULATING |
Cálculo en progreso | Estado transitorio (geocoding, routing, etc) |
COMPLETED |
✅ Cálculo completado exitosamente | Resultados disponibles para usar |
ERROR |
❌ Error durante el procesamiento | Revisar campo error_message |
Rate Limiting & Throttling
Para garantizar la calidad del servicio, la API implementa límites de uso (throttling) en cuatro niveles temporales:
| Límite | Ventana | Máximo | Descripción |
|---|---|---|---|
| Por Minuto | 60 segundos | 30 requests | Límite de ráfagas cortas |
| Por Hora | 60 minutos | 500 requests | Límite sostenido medio |
| Por Día | 24 horas | 5,000 requests | Límite total diario |
| Expediciones Diarias | 24 horas | 1,500 expediciones | Límite de expediciones procesadas |
429 Too Many Requests.
Headers de Throttling
Cada respuesta incluye headers que te ayudan a monitorear tu uso:
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 25
X-RateLimit-Reset: 1699876543
| Header | Descripción |
|---|---|
X-RateLimit-Limit |
Límite máximo para esta ventana |
X-RateLimit-Remaining |
Requests restantes en esta ventana |
X-RateLimit-Reset |
Timestamp Unix cuando se reinicia el contador |
Autenticación
La API utiliza autenticación basada en tokens (API Keys). Cada cliente tiene un token único que debe incluirse en todas las peticiones.
Obtener tu Token
Tu API token se proporciona automáticamente al contratar el servicio de Zeolos. El token tiene las siguientes características:
- Formato: 64 caracteres alfanuméricos
- Validez: Permanente mientras tu suscripción esté activa
- Revocable: Sí, contactando con soporte
- Único: Un token por cliente
- Código versionado (Git, SVN, etc.)
- URLs públicas o logs
- Código del frontend (JavaScript visible)
- Documentación pública
Métodos de Autenticación
La API acepta el token en dos formatos de header. Puedes usar cualquiera:
Método 1: X-API-Key (Recomendado)
X-API-Key: abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567890abcd
✅ Este es el método recomendado y más simple
Método 2: Authorization Bearer (Alternativo)
Authorization: Bearer abc123def456ghi789jkl012mno345pqr678stu901vwx234yz567890abcd
X-API-Key por simplicidad, o Authorization: Bearer
si tu sistema ya lo implementa para otras APIs.
Headers Requeridos
Todas las peticiones deben incluir estos headers:
| Header | Valor | Requerido |
|---|---|---|
X-API-Key |
Tu token de 64 caracteres | Sí |
Content-Type |
application/json |
Sí (para POST) |
Validación del Token
En cada petición, el sistema valida automáticamente:
| Validación | Descripción | Error si Falla |
|---|---|---|
| Token existe | El token está registrado en el sistema | 401 - Invalid token |
| Token activo | El token no ha sido revocado manualmente | 401 - Token has been revoked |
| Cliente activo | Tu suscripción está activa | 403 - Client account is inactive |
Errores de Autenticación
❌ Token No Proporcionado (401)
{
"detail": "Authentication credentials were not provided."
}
Solución: Añade el header X-API-Key
❌ Token Inválido (401)
{
"detail": "Invalid token"
}
Solución: Verifica que el token sea correcto (64 caracteres exactos)
❌ Token Revocado (401)
{
"detail": "Token has been revoked. Please contact support."
}
Solución: Contacta con soporte para obtener un nuevo token
❌ Cliente Inactivo (403)
{
"detail": "Client account is inactive. Please contact support."
}
Solución: Tu suscripción está desactivada. Contacta con soporte para reactivarla
Buenas Prácticas
Almacenamiento Seguro
- Usa variables de entorno (
process.env.API_TOKEN,os.getenv()) - Gestores de secretos (AWS Secrets Manager, Azure Key Vault, etc.)
- Archivos de configuración fuera del repositorio (
.enven.gitignore)
Uso en Backend
Nunca expongas el token en el frontend. Todas las llamadas a la API deben hacerse desde tu servidor backend, no desde JavaScript del navegador.
Rotación de Tokens
Si sospechas que tu token ha sido comprometido, contacta con soporte inmediatamente para revocar el token actual y obtener uno nuevo.
Testing de Autenticación
Puedes verificar que tu token funciona con esta petición simple:
curl -X GET "https://api.zeolos.es/api/expeditions/" \
-H "X-API-Key: TU_TOKEN_AQUI"
Si el token es válido, recibirás un 200 OK con la lista de tus expediciones (puede estar vacía).
Expediciones
Estructura de una Expedición
Una expedición representa un envío de mercancías y contiene la siguiente información:
| Campo | Tipo | Descripción | Obligatorio |
|---|---|---|---|
client_name |
string | Nombre del cliente final | Sí |
expedition_id |
string | ID único (max 100 caracteres) | Sí |
expedition_date |
date | Fecha de expedición (YYYY-MM-DD) | Sí |
scope |
integer | Alcance de emisiones: 1 o 3 (default: 3) | Opcional |
cargo |
object | Información de la carga | Sí |
route |
array | Secuencia de ubicaciones y transportes | Sí |
Estados Posibles
| Estado | Descripción |
|---|---|
PENDING |
Expedición recibida, esperando procesamiento |
CALCULATING |
Cálculo en progreso |
COMPLETED |
Cálculo completado exitosamente |
ERROR |
Error durante el procesamiento (ver error_message) |
Scope (Alcance de Emisiones)
| Valor | Descripción | Incluye |
|---|---|---|
1 |
Scope 1 | Solo emisiones directas (TTW) |
3 |
Scope 3 (default) | Emisiones completas del ciclo (WTW) |
Locations (Ubicaciones)
Campos Disponibles
Cada ubicación acepta los siguientes campos. Debes proporcionar al menos address o code:
| Campo | Tipo | Formato | Ejemplo |
|---|---|---|---|
address |
string | Mínimo 5 caracteres, no solo números | "Calle Alcalá 15, Madrid, España" |
code |
string | 3 caracteres (IATA) o 5 (UN/LOCODE) | "MAD" o "ESBCN" |
distance |
decimal | 0 a 40,075 km (solo en 2ª ubicación en adelante) | 620.5 |
country |
string | Código ISO 2-3 caracteres | "ES" o "ESP" |
Validaciones
- Al menos uno de
addressocodees obligatorio address: mínimo 5 caracteres, no puede ser solo númeroscode: exactamente 3 o 5 caracteres, debe existir en la base de datosdistance: solo válida en ubicaciones a partir de la segundacountry: opcional, complementa aaddress
distance, se aplican ajustes según el
GLEC Framework para reflejar distancias reales de operación.
Ejemplos de Ubicaciones
Con Dirección
{
"location": {
"address": "Plaza Catalunya, Barcelona",
"country": "ES"
}
}
Con Código de Aeropuerto
{
"location": {
"code": "MAD"
}
}
Con Código de Puerto
{
"location": {
"code": "ESBCN"
}
}
Con Distancia Manual
{
"location": {
"code": "BCN",
"distance": 620
}
}
Modos de Transporte
Road (Carretera)
Requiere tres parámetros en details:
1. Vehicle Type (Tipo de Vehículo)
| Código | Descripción |
|---|---|
van_3.5t |
Van < 3.5 t |
rigid_truck_3.5_7.5t |
Rigid truck 3.5-7.5 t GVW |
rigid_truck_7.5_12t |
Rigid truck 7.5-12 t GVW |
rigid_truck_12_20t |
Rigid truck 12-20 t GVW |
rigid_truck_20_26t |
Rigid truck 20-26 t GVW |
rigid_truck_26_32t |
Rigid truck 26-32 t GVW |
artic_truck_34t |
Artic truck up to 34 t GVW |
artic_truck_34_40t |
Artic Truck 34-40 t GVW |
artic_truck_34_40t_gwv_hpdi |
Artic Truck 34-40 t GVW HPDI |
artic_truck_34_40t_gwv_si_engine |
Artic Truck 34-40t GVW SI engine |
artic_truck_34_44t |
Artic truck 34-44 t GVW |
artic_truck_40t_trailer |
Artic truck 40 t GVW, incl. lightweight trailer |
artic_truck_60t |
Artic truck up to 60 t GVW |
artic_truck_72t |
Artic truck up to 72 t GVW |
2. Fuel Type (Tipo de Combustible)
| Código | Descripción |
|---|---|
diesel |
Diesel |
petrol |
Petrol (Gasolina) |
cng |
CNG (Gas Natural Comprimido) |
cng_diesel |
CNG/Diesel |
lng |
LNG (Gas Natural Licuado) |
lng_diesel |
LNG/Diesel |
bio_lng |
Bio-LNG |
bio_lng_diesel |
Bio-LNG/Diesel |
lpg |
LPG (Gas Licuado de Petróleo) |
electric |
Electric (Eléctrico) |
3. Load Level (Nivel de Carga)
| Código | Descripción | Load Factor | Empty Running |
|---|---|---|---|
light |
Light (Carga ligera) | 30% | 9% |
average_mixed |
Average/Mixed (Promedio mixto) | 60% | 17% |
heavy |
Heavy (Carga pesada) | 100% | 38% |
container |
Container (Contenedor) | 72% | 30% |
combined |
Combined Load Factor & Empty Running | - | - |
Ejemplo Completo (Road)
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
}
Sea (Marítimo)
No requiere parámetros adicionales. details puede ser un objeto vacío:
{
"transport_mode": "sea",
"details": {}
}
Air (Aéreo)
No requiere parámetros adicionales. details puede ser un objeto vacío:
{
"transport_mode": "air",
"details": {}
}
Rail (Ferrocarril)
No requiere parámetros adicionales. details puede ser un objeto vacío:
{
"transport_mode": "rail",
"details": {}
}
Cargo (Carga)
Campos Obligatorios
| Campo | Tipo | Valores | Descripción |
|---|---|---|---|
weight |
decimal | > 0 | Peso de la carga |
weight_unit |
string | kg, t, lb, teu | Unidad de peso |
is_refrigerated |
boolean | true / false | ¿Requiere refrigeración? |
Campo Opcional
| Campo | Tipo | Valores | Descripción |
|---|---|---|---|
teu_equivalent_units |
string | light, average, heavy | Densidad de carga para transporte marítimo (default: average) |
Unidades de Peso
| Unidad | Descripción | Ejemplo |
|---|---|---|
kg |
Kilogramos | 1500 |
t |
Toneladas métricas | 1.5 |
lb |
Libras | 3306 |
teu |
Twenty-foot Equivalent Unit | 2 |
TEU Equivalent Units
Para transporte marítimo, especifica la densidad de la carga:
| Valor | Descripción |
|---|---|
light |
Carga ligera (baja densidad) |
average |
Carga promedio (default) |
heavy |
Carga pesada (alta densidad) |
Ejemplo Completo
{
"cargo": {
"weight": 1500,
"weight_unit": "kg",
"is_refrigerated": false,
"teu_equivalent_units": "average"
}
}
Estructura de Route
Patrón Alternado
El campo route es un array que alterna ubicaciones y transportes,
siempre empezando y terminando con una ubicación.
route: [
{"location": {...}}, // Origen
{"transport_mode": "...", "details": {...}},
{"location": {...}}, // Destino (o siguiente origen)
{"transport_mode": "...", "details": {...}},
{"location": {...}} // Destino final
]
Reglas
- Mínimo 3 elementos: [location, transport, location]
- Siempre número impar de elementos
- Empieza y termina con location
- Alterna: location → transport → location → transport → location
Ejemplo: Ruta Simple (1 tramo)
{
"route": [
{"location": {"address": "Madrid, España"}},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
},
{"location": {"address": "Barcelona, España"}}
]
}
Ejemplo: Ruta Multi-modal (3 tramos)
{
"route": [
{"location": {"address": "Madrid, España"}},
{"transport_mode": "road", "details": {...}},
{"location": {"code": "ESBCN"}},
{"transport_mode": "sea", "details": {}},
{"location": {"code": "ITGOA"}},
{"transport_mode": "road", "details": {...}},
{"location": {"address": "Milano, Italia"}}
]
}
Crear Expedición
/api/expeditions/
Crea una nueva expedición y calcula sus emisiones de forma síncrona.
Request Body
{
"client_name": "ACME Corp",
"expedition_id": "EXP-2025-001",
"expedition_date": "2025-11-08",
"scope": 3,
"cargo": {
"weight": 1500,
"weight_unit": "kg",
"is_refrigerated": false,
"teu_equivalent_units": "average"
},
"route": [
{"location": {"address": "Madrid, España"}},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
},
{"location": {"address": "Barcelona, España"}}
]
}
Response (201 Created)
{
"client_name": "ACME Corp",
"expedition_id": "EXP-2025-001",
"expedition_date": "2025-11-08",
"status": "COMPLETED",
"error_message": "",
"scope": 3,
"cargo": {
"weight": 1500.0,
"weight_unit": "kg",
"is_refrigerated": false,
"teu_equivalent_units": "average"
},
"route": [
{"location": {"query": "Madrid, España"}},
{
"transport_mode": "road",
"details": {...},
"results": {
"distance_km": 621.45,
"emission_wtw": 1186.49,
"emission_wtt": 273.33,
"emission_ttw": 913.16
}
},
{"location": {"query": "Barcelona, España"}}
],
"results": {
"total_distance_km": 621.45,
"total_emission_wtw": 1186.49,
"total_emission_wtt": 273.33,
"total_emission_ttw": 913.16
},
"created_at": "2025-11-08T10:30:00Z",
"updated_at": "2025-11-08T10:30:05Z"
}
Errores Posibles
| Código | Descripción |
|---|---|
400 |
Datos de entrada inválidos |
401 |
Token inválido o ausente |
429 |
Límite de throttling excedido |
500 |
Error interno del servidor |
Listar Expediciones
/api/expeditions/
Obtiene una lista paginada de expediciones del cliente autenticado.
Parámetros Query (Opcionales)
| Parámetro | Tipo | Descripción | Ejemplo |
|---|---|---|---|
page |
integer | Número de página (default: 1) | 1 |
page_size |
integer | Items por página (default: 50, max: 100) | 20 |
expedition_date_from |
date | Filtrar desde fecha de expedición | 2025-11-01 |
expedition_date_to |
date | Filtrar hasta fecha de expedición | 2025-11-30 |
created_from |
datetime | Filtrar desde fecha de creación en BD | 2025-11-01T00:00:00Z |
created_to |
datetime | Filtrar hasta fecha de creación en BD | 2025-11-30T23:59:59Z |
status |
string | Filtrar por estado | COMPLETED, ERROR |
Ejemplo de Request
curl -X GET "https://api.zeolos.es/api/expeditions/?expedition_date_from=2025-11-01&status=COMPLETED" \
-H "X-API-Key: TU_TOKEN_AQUI"
Response (200 OK)
{
"count": 150,
"next": "https://api.zeolos.es/api/expeditions/?page=2",
"previous": null,
"results": [
{
"client_name": "ACME Corp",
"expedition_id": "EXP-2025-001",
"expedition_date": "2025-11-08",
"status": "COMPLETED",
"cargo": {...},
"route": [...],
"results": {...},
"created_at": "2025-11-08T10:30:00Z",
"updated_at": "2025-11-08T10:30:05Z"
}
]
}
Obtener Expedición
/api/expeditions/{expedition_id}/
Obtiene una expedición específica por su expedition_id.
Parámetros URL
| Parámetro | Descripción |
|---|---|
expedition_id |
El ID de expedición que proporcionaste al crearla |
Ejemplo de Request
curl -X GET "https://api.zeolos.es/api/expeditions/EXP-2025-001/" \
-H "X-API-Key: TU_TOKEN_AQUI"
Response (200 OK)
Devuelve el mismo formato que la respuesta de crear expedición.
Errores Posibles
| Código | Descripción |
|---|---|
404 |
Expedición no encontrada |
Batch Sync
/api/expeditions/batch/
Procesa múltiples expediciones en un solo request (hasta 10). Respuesta síncrona.
Límites
- Mínimo: 1 expedición
- Máximo: 10 expediciones
- Todos los
expedition_iddeben ser únicos dentro del batch
Request Body
{
"batch_id": "BATCH-2025-001",
"expeditions": [
{
"client_name": "ACME Corp",
"expedition_id": "EXP-001",
"expedition_date": "2025-11-08",
"cargo": {...},
"route": [...]
},
{
"client_name": "ACME Corp",
"expedition_id": "EXP-002",
"expedition_date": "2025-11-08",
"cargo": {...},
"route": [...]
}
]
}
Response (200 OK)
{
"batch_id": "BATCH-2025-001",
"total": 2,
"successful": 2,
"failed": 0,
"processing_time_seconds": 3.45,
"results": [
{
"expedition_id": "EXP-001",
"status": "success",
"data": {
"expedition_id": "EXP-001",
"status": "COMPLETED",
"results": {...}
}
},
{
"expedition_id": "EXP-002",
"status": "success",
"data": {
"expedition_id": "EXP-002",
"status": "COMPLETED",
"results": {...}
}
}
]
}
Response con Errores Parciales
{
"batch_id": "BATCH-2025-001",
"total": 2,
"successful": 1,
"failed": 1,
"processing_time_seconds": 2.15,
"results": [
{
"expedition_id": "EXP-001",
"status": "success",
"data": {...}
},
{
"expedition_id": "EXP-002",
"status": "error",
"error": "Invalid weight: must be greater than 0",
"error_type": "validation_error"
}
]
}
Batch Async
/api/expeditions/batch/async/
Procesa grandes lotes de expediciones (hasta 500) de forma asíncrona. Respuesta inmediata.
Límites
- Mínimo: 1 expedición
- Máximo: 500 expediciones
- Procesamiento en segundo plano
Request Body
Mismo formato que batch sync, pero acepta hasta 500 expediciones.
Response (202 Accepted)
{
"batch_id": "BATCH-ASYNC-001",
"status": "QUEUED",
"message": "Batch accepted for processing",
"total_expeditions": 250,
"estimated_time_minutes": 5.0,
"status_url": "/api/expeditions/batch/BATCH-ASYNC-001/status/"
}
Listar Batches
/api/expeditions/batches/
Obtiene una lista de todos los batches del cliente autenticado.
Parámetros Query (Opcionales)
| Parámetro | Tipo | Descripción | Ejemplo |
|---|---|---|---|
created_from |
date | Filtrar desde fecha | 2025-11-01 |
created_to |
date | Filtrar hasta fecha | 2025-11-30 |
status |
string | Filtrar por estado | COMPLETED, PROCESSING, ERROR |
Response (200 OK)
{
"count": 15,
"results": [
{
"batch_id": "BATCH-2025-001",
"status": "COMPLETED",
"total": 10,
"processed": 10,
"successful": 10,
"failed": 0,
"progress_percent": 100.0,
"created_at": "2025-11-08T10:00:00Z",
"updated_at": "2025-11-08T10:05:30Z",
"completed_at": "2025-11-08T10:05:30Z"
}
]
}
Obtener Batch Completo
/api/expeditions/batch/{batch_id}/
Obtiene el batch completo con todas sus expediciones.
Parámetros URL
| Parámetro | Descripción |
|---|---|
batch_id |
El ID del batch a obtener |
Response (200 OK)
{
"batch_id": "BATCH-2025-001",
"status": "COMPLETED",
"total": 2,
"processed": 2,
"successful": 2,
"failed": 0,
"progress_percent": 100.0,
"created_at": "2025-11-08T10:00:00Z",
"updated_at": "2025-11-08T10:05:30Z",
"completed_at": "2025-11-08T10:05:30Z",
"expeditions": [
{
"client_name": "ACME Corp",
"expedition_id": "EXP-001",
"status": "COMPLETED",
"cargo": {...},
"route": [...],
"results": {...}
},
{
"client_name": "ACME Corp",
"expedition_id": "EXP-002",
"status": "COMPLETED",
"cargo": {...},
"route": [...],
"results": {...}
}
]
}
Estado del Batch
/api/expeditions/batch/{batch_id}/status/
Consulta el estado de procesamiento de un batch sin obtener todas las expediciones.
Response (200 OK) - En Proceso
{
"batch_id": "BATCH-ASYNC-001",
"status": "PROCESSING",
"is_async": true,
"total": 250,
"processed": 150,
"successful": 148,
"failed": 2,
"progress_percent": 60.0,
"created_at": "2025-11-08T10:00:00Z",
"updated_at": "2025-11-08T10:03:00Z",
"celery": {
"task_id": "a1b2c3d4...",
"state": "PROGRESS",
"current": 150,
"total": 250
}
}
Response (200 OK) - Completado
{
"batch_id": "BATCH-ASYNC-001",
"status": "COMPLETED",
"is_async": true,
"total": 250,
"processed": 250,
"successful": 248,
"failed": 2,
"progress_percent": 100.0,
"created_at": "2025-11-08T10:00:00Z",
"updated_at": "2025-11-08T10:05:00Z",
"completed_at": "2025-11-08T10:05:00Z"
}
Estados Posibles
| Estado | Descripción |
|---|---|
QUEUED |
Batch en cola, aún no ha empezado |
PROCESSING |
Batch en procesamiento activo |
COMPLETED |
Batch completado |
PARTIAL |
Completado con algunos errores |
ERROR |
Batch falló completamente |
Ejemplos de Uso
Casos prácticos de uso de la API.
Expedición Simple (Road)
Caso de Uso
Calcular emisiones de un envío por carretera entre dos ciudades.
import requests
API_TOKEN = "tu_token_aqui"
headers = {"X-API-Key": API_TOKEN, "Content-Type": "application/json"}
payload = {
"client_name": "ACME Corp",
"expedition_id": "EXP-001",
"expedition_date": "2025-11-08",
"cargo": {
"weight": 1500,
"weight_unit": "kg",
"is_refrigerated": False
},
"route": [
{"location": {"address": "Madrid, España"}},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
},
{"location": {"address": "Barcelona, España"}}
]
}
response = requests.post(
"https://api.zeolos.es/api/expeditions/",
headers=headers,
json=payload
)
if response.status_code == 201:
data = response.json()
print(f"Distancia: {data['results']['total_distance_km']} km")
print(f"CO₂: {data['results']['total_emission_wtw']} kg")
Expedición Multi-modal
Caso de Uso
Envío que combina carretera, marítimo y aéreo.
const payload = {
client_name: "Global Logistics",
expedition_id: "EXP-MULTI-001",
expedition_date: "2025-11-08",
cargo: {
weight: 5000,
weight_unit: "kg",
is_refrigerated: false
},
route: [
{ location: { address: "Madrid, España" } },
{
transport_mode: "road",
details: {
vehicle_type: "rigid_truck_12_20t",
fuel_type: "diesel",
load_level: "average_mixed"
}
},
{ location: { code: "ESBCN" } },
{ transport_mode: "sea", details: {} },
{ location: { code: "CNSHA" } },
{ transport_mode: "air", details: {} },
{ location: { code: "JFK" } }
]
};
fetch("https://api.zeolos.es/api/expeditions/", {
method: "POST",
headers: {
"X-API-Key": "tu_token_aqui",
"Content-Type": "application/json"
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => console.log(data));
Usando Códigos (IATA/UN/LOCODE)
Caso de Uso
Expedición usando códigos de aeropuertos y puertos.
curl -X POST "https://api.zeolos.es/api/expeditions/" \
-H "X-API-Key: tu_token_aqui" \
-H "Content-Type: application/json" \
-d '{
"client_name": "Express Cargo",
"expedition_id": "EXP-CODES-001",
"expedition_date": "2025-11-08",
"cargo": {
"weight": 2000,
"weight_unit": "kg",
"is_refrigerated": false
},
"route": [
{"location": {"code": "MAD"}},
{"transport_mode": "air", "details": {}},
{"location": {"code": "JFK"}}
]
}'
Con Distancia Manual
Caso de Uso
Proporcionar distancia conocida para evitar cálculo de rutas.
payload = {
"client_name": "Fast Delivery",
"expedition_id": "EXP-DIST-001",
"expedition_date": "2025-11-08",
"cargo": {
"weight": 1000,
"weight_unit": "kg",
"is_refrigerated": False
},
"route": [
{"location": {"code": "MAD"}},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
},
{"location": {"code": "BCN", "distance": 620}}
]
}
Batch Sync (hasta 10)
Caso de Uso
Procesar múltiples expediciones en una sola request.
import requests
expeditions = []
for i in range(1, 6):
expeditions.append({
"client_name": "ACME Corp",
"expedition_id": f"EXP-{i:03d}",
"expedition_date": "2025-11-08",
"cargo": {"weight": 1500, "weight_unit": "kg", "is_refrigerated": False},
"route": [
{"location": {"code": "MAD"}},
{"transport_mode": "road", "details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}},
{"location": {"code": "BCN"}}
]
})
batch = {
"batch_id": "BATCH-2025-001",
"expeditions": expeditions
}
response = requests.post(
"https://api.zeolos.es/api/expeditions/batch/",
headers={"X-API-Key": "tu_token_aqui", "Content-Type": "application/json"},
json=batch
)
if response.status_code == 200:
result = response.json()
print(f"Exitosas: {result['successful']}/{result['total']}")
Batch Async (hasta 500)
Caso de Uso
Procesar grandes lotes de forma asíncrona con polling de estado.
import requests
import time
# Generar 100 expediciones
expeditions = []
for i in range(1, 101):
expeditions.append({
"client_name": "Big Client",
"expedition_id": f"ASYNC-{i:04d}",
"expedition_date": "2025-11-08",
"cargo": {"weight": 2000, "weight_unit": "kg", "is_refrigerated": False},
"route": [
{"location": {"code": "MAD"}},
{"transport_mode": "road", "details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}},
{"location": {"code": "BCN", "distance": 620}}
]
})
# Enviar batch async
response = requests.post(
"https://api.zeolos.es/api/expeditions/batch/async/",
headers={"X-API-Key": "tu_token_aqui", "Content-Type": "application/json"},
json={"batch_id": "BATCH-ASYNC-2025", "expeditions": expeditions}
)
if response.status_code == 202:
data = response.json()
batch_id = data['batch_id']
status_url = data['status_url']
print(f"Batch aceptado: {batch_id}")
# Polling del estado
while True:
status = requests.get(
f"https://api.zeolos.es/api{status_url}",
headers={"X-API-Key": "tu_token_aqui"}
).json()
print(f"{status['status']}: {status['progress_percent']:.1f}%")
if status['status'] in ['COMPLETED', 'PARTIAL', 'ERROR']:
print(f"\nFinalizado - Exitosas: {status['successful']}")
break
time.sleep(10)
Listar y Filtrar Expediciones
Caso de Uso
Obtener expediciones de un periodo específico.
import requests
from datetime import datetime, timedelta
headers = {"X-API-Key": "tu_token_aqui"}
# Obtener expediciones del último mes
today = datetime.now()
last_month = today - timedelta(days=30)
params = {
"expedition_date_from": last_month.strftime("%Y-%m-%d"),
"expedition_date_to": today.strftime("%Y-%m-%d"),
"status": "COMPLETED",
"page_size": 50
}
response = requests.get(
"https://api.zeolos.es/api/expeditions/",
headers=headers,
params=params
)
if response.status_code == 200:
data = response.json()
print(f"Total: {data['count']} expediciones")
for exp in data['results']:
print(f"- {exp['expedition_id']}: {exp['results']['total_emission_wtw']} kg CO₂")
Carga Refrigerada
Caso de Uso
Expedición con mercancía que requiere refrigeración.
{
"client_name": "FreshFood Co",
"expedition_id": "EXP-COOL-001",
"expedition_date": "2025-11-08",
"cargo": {
"weight": 3000,
"weight_unit": "kg",
"is_refrigerated": true
},
"route": [
{"location": {"code": "MAD"}},
{
"transport_mode": "road",
"details": {
"vehicle_type": "rigid_truck_12_20t",
"fuel_type": "diesel",
"load_level": "average_mixed"
}
},
{"location": {"code": "BCN"}}
]
}
Transporte Marítimo con TEU
Caso de Uso
Expedición marítima especificando peso en TEU.
{
"client_name": "Ocean Freight",
"expedition_id": "EXP-SEA-001",
"expedition_date": "2025-11-08",
"cargo": {
"weight": 5,
"weight_unit": "teu",
"is_refrigerated": false,
"teu_equivalent_units": "heavy"
},
"route": [
{"location": {"code": "ESBCN"}},
{"transport_mode": "sea", "details": {}},
{"location": {"code": "CNSHA"}}
]
}