🔄 Modal switch

Simulate the impact on CO₂e emissions when switching between different modes of transport for your shipments, and gain insights on lower-emitting modes.

🎯 What this guide is about

This guide shows you:

  • How to compare CO₂e emissions across different transport modes for the same route.
  • How to handle both simple and more complex modal switches with transshipment points.
  • Example payloads and code to help you integrate into your own app or scripts.

🧠 Scenarios covered

In this guide we’ll look at:

  • Road vs rail vs barge for a single leg;
  • Direct air vs ocean + road + rail with transshipment points;
  • Finding the closest port or rail station if none is provided.

🛠 Steps overview and prerequisites

  1. Build a payload for your shipment (orders + transportChainElements)
  2. Prepare mode-specific details (e.g. fuelType, truckSize)
  3. POST to the /shipment/v2/report/co2 endpoint
  4. Parse the co2e.total value from the JSON response
  5. Repeat for each mode or transshipment combination

Prerequisites

  • ✅ Valid API key for Shipment API v2.1 (ISO 14083-compliant).
  • ⚠️ Note that multi-leg requests return emissions for hubs. Run one request per scenario for easier comparisons.

🧑‍💻Scenarios

1️⃣ Modal switch with direct route

Simulate the CO₂e impact of switching between different modes.

⚙️ Example payload

one_leg_shipment_body = {
    "orders": [
        {
            "type": "fcl",
            "nContainers": 1,
            "containerSizeTypeCode": "40GP"
        }
    ],
    "transportChainElements": [
        {
            "from": "DEDUI",
            "to": "NLRTM",
            "type": "leg",
        }
    ]
}

📝 Mode-specific details

Define the details you'd like to apply for each mode. For example :

road_details = { truckSize: 33, fuelType: "DIESEL" };
rail_details = {};
inland_water_details = { vesselType: "CONTAINER_COUPLED" };

⚙️ Example request

modes_to_test = [
    ("road", road_details),
    ("rail", rail_details),
    ("inland_water", inland_water_details)
]

def one_tce_modal_switch(body, modes_and_details):
    results = []
    for mode, detail in modes_and_details:
        body["transportChainElements"][0]["mode"] = mode
        body["transportChainElements"][0]["details"] = detail
        data = requests.post(SHIPMENT_API_URL, headers=headers, json=body).json()
        results.append(data["co2e"]["total"])
    return results

print(one_tce_modal_switch(one_leg_shipment_body, modes_to_test))

✅ Example results

ModeCO₂e WTW (g)
Road585 570
Rail95 076
Barge115 331

2️⃣ Modal switch with known transshipment points

Compare a direct air shipment against a rail-ocean-road alternative.

When changing from one mode to another (for example, air to ocean), you often introduce one or more new transshipment points.

For example, switching from direct air freight to ocean freight usually requires:

  • Rail or road to and from an ocean port
  • Ocean shipping to a destination port

Here, your payload will list each leg explicitly in the transportChainElements.

Here’s a payload to compare a direct air shipment (ATL to LHR) against a combined rail → sea → road itinerary (ATLUSSAVGBSOUGBLHR):

⚙️ Example payload

# Direct air option
direct_air_payload = {
    "orders": [{"type": "PARCEL", "weight": 1000}],
    "transportChainElements": [
        { "type": "leg", "from": "ATL", "to": "LHR", "mode": "air",
          "details": { "aircraft": { "type": "CARGO" } } }
    ]
}

# Rail-ocean-road alternative
rail_ocean_road_payload = {
    "orders": [{"type": "PARCEL", "weight": 1000}],
    "transportChainElements": [
        { "type": "leg", "from": "USATL", "to": "USSAV", "mode": "rail", "details": { ... } },
        { "type": "leg", "from": "USSAV", "to": "GBSOU", "mode": "sea", "details": { "carrier": { "scac": "CMDU" } } },
        { "type": "leg", "from": "GBSOU", "to": "GBLHR", "mode": "road", "details": { "truckSize": 33, "fuelType": "DIESEL" } }
    ]
}

✅ Example results

ModeCO₂e WTW (g)Alternative modesCO₂e WTW (g)
TOTAL4 165 206TOTAL70 430
Rail USATL>USSAV7 299
Sea USSAV>GBSOU40 451
Road GBSOU>GBLHR14 183

In this example, switching from direct air to ocean-based routing reduces emissions by 98%.


3️⃣ Modal switch with unknown transshipment points

Simulate a switch to ocean/rail without pre-identified transshipment points — letting Searoutes pick the closest ports/stations for you.

When comparing modes like direct air to ocean-based routes, you may not already know the new transshipment points (ports, etc.). Searoutes’ geocoding API helps you discover the closest ones.

⚙️ Example payload

In this example, we’ll switch a direct air shipment from Chicago (ORD) to Milan (MXP) into an ocean-based shipment, step by step.

a. Get airport coordinates
Use the geocoding/all endpoint to retrieve the longitude/latitude of your origin (ORD) and destination (MXP).

  • Example with ORD: GET /geocoding/v2/all?iataCode=ORD
  • Response : "geometry": { "coordinates": [-87.9048, 41.9786] }

b. Find nearest ocean ports

Use the geocoding/closest endpoint with type=port and size=large,medium to get the nearest ocean ports.

  • Example: GET /geocoding/v2/closest?lon=-87.9048&lat=41.9786&type=port&size=large,medium
  • Response returns the closest large or medium port : USBAL (Baltimore port).

c. Build the ocean payload
With USBAL as origin port and ITGOA as destination port, add the ocean leg and the surrounding road legs as shown in the payload above.

Here’s the full python payload after resolving the transshipment points:

unknown_points_payload = {
  orders: [{ type: "PARCEL", weight: 1000 }],
  transportChainElements: [
    { type: "leg", from: "ORD", to: "USBAL", mode: "road" },
    { type: "leg", from: "USBAL", to: "ITGOA", mode: "sea" },
    { type: "leg", from: "ITGOA", to: "MXP", mode: "road" },
  ],
};

✅ Example results

Direct air (ATL->LHR)CO₂e WTW (g)Alternative modesCO₂e WTW (g)
TOTAL5 791 055TOTAL376 292
Road USORD>USBAL127 911
Sea USBAL>ITGOA74 519
Road ITGOA>ITMXP171 722

In this example, switching from direct air to ocean-based routing reduces emissions by 93%.


💡 Optional helper function for batch comparisons

If you’d like to quickly compare multiple routes in one script, you can use a helper like this:

def compare_different_legs(orders, alternative_legs):
    results = []
    body = {
        "orders": orders,
    }
    headers = {'x-api-key': API_KEY, 'Content-Type': 'application/json', 'Accept-Version': '2.1'}
    for alternative in alternative_legs:
        # Update the transport chain elements
        body["transportChainElements"] = alternative
        # Get the response
        response = requests.post(SHIPMENT_API_URL, headers=headers, json=body)
        data = response.json()
        # Save the total CO2 and the CO2 by leg
        alternative_result = {
            "total": data["co2e"]["total"],
            "by_leg": [tce["co2e"]["total"] for tce in data["transportChainElements"] if tce["type"] == "leg"]
        }
        results.append(alternative_result)
    return results


print(compare_different_legs([order], [alternative1_legs, alternative2_legs]))

✅ What’s next?

  • 🤝 Reach out to our [email protected], we’d love to hear your feedback on these guides or suggestions for new hands‑on topics!
  • 📖 Check the full Shipment API v2.1 docs for all supported params.