Documentación API Zeolos

v1.0

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
Nota sobre Alcance: Por defecto, la API calcula emisiones con Scope 3 (WTW completo). Si solo necesitas emisiones directas, puedes especificar 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)
Seguridad: Todas las comunicaciones se realizan por HTTPS (TLS 1.2+). Nunca compartas tu API token públicamente ni lo incluyas en código versionado sin cifrar.

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
¡Importante! Nunca compartas tu token. Trátalo como una contraseña. Si crees que ha sido comprometido, contacta con soporte inmediatamente.

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

Bash / Terminal
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

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)

JavaScript
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:

JSON Response
{
  "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
¡Felicitaciones!
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

Entiende cómo funciona la API

📚 API Reference

Explora todos los parámetros

💻 Más Ejemplos

Casos de uso avanzados

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

  1. Información general: Cliente, fecha, ID único
  2. Carga (cargo): Peso, unidad, refrigeración
  3. Ruta (route): Secuencia de ubicaciones y transportes
  4. 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
Consejo de Optimización: Usar códigos (IATA/UN/LOCODE) es más rápido y consume menos recursos que usar direcciones, ya que se evita el paso de geocodificación.

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:

1
Validación
Verifica que todos los datos sean correctos
2
Geocodificación
Convierte direcciones en coordenadas GPS (si es necesario)
3
Cálculo de Rutas
Determina la distancia real de cada tramo
4
Aplicación de Factores
Selecciona los factores de emisión apropiados
5
Cálculo de Emisiones
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
Importante: Estos límites son independientes. Si alcanzas cualquiera de ellos, recibirás un error 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
Tip: Si necesitas procesar grandes volúmenes, usa el endpoint de batch processing que permite hasta 500 expediciones en una sola request.

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
Seguridad Crítica: Nunca compartas tu token. Trátalo como una contraseña. No lo incluyas en:
  • 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
¿Cuál usar? Ambos funcionan igual. Usa 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
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 (.env en .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
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
expedition_id string ID único (max 100 caracteres)
expedition_date date Fecha de expedición (YYYY-MM-DD)
scope integer Alcance de emisiones: 1 o 3 (default: 3) Opcional
cargo object Información de la carga
route array Secuencia de ubicaciones y transportes

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 address o code es obligatorio
  • address: mínimo 5 caracteres, no puede ser solo números
  • code: exactamente 3 o 5 caracteres, debe existir en la base de datos
  • distance: solo válida en ubicaciones a partir de la segunda
  • country: opcional, complementa a address
Ajustes de Distancia: Cuando proporcionas el campo 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

POST /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

GET /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

GET /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

POST /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_id deben 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

POST /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

GET /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

GET /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

GET /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.

Python
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.

JavaScript
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
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.

Python
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}}
    ]
}
Nota: Se aplicarán ajustes GLEC a la distancia proporcionada.

Batch Sync (hasta 10)

Caso de Uso

Procesar múltiples expediciones en una sola request.

Python
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.

Python
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.

Python
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.

JSON
{
  "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.

JSON
{
  "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"}}
  ]
}