Skip to main content

Understanding Scope 3 Category 4

Upstream transportation and distribution covers emissions from transporting and distributing products purchased by your organization—in vehicles and facilities not owned or controlled by you. According to the GHG Protocol Scope 3 Standard, Category 4 includes:
  • Inbound logistics: Transportation of purchased goods from Tier 1 suppliers to your facilities
  • Third-party distribution: Distribution services purchased for inbound goods
  • Transportation between facilities: Movement between your locations (if using third-party logistics)
┌─────────────────────────────────────────────────────────────────────────────┐
│              SCOPE 3 CATEGORY 4: Upstream Transportation                    │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  YOUR SUPPLY CHAIN (UPSTREAM)                         YOUR OPERATIONS       │
│  ───────────────────────────────────────────          ─────────────────     │
│                                                                             │
│  ┌──────────────┐      ┌──────────────┐      ┌──────────────┐               │
│  │   Tier 2     │      │   Tier 1     │      │    Your      │               │
│  │  Suppliers   │ ───► │  Suppliers   │ ───► │  Facilities  │               │
│  │              │      │              │      │              │               │
│  └──────────────┘      └──────────────┘      └──────────────┘               │
│         │                     │                     ▲                       │
│         │                     │                     │                       │
│         └─────────────────────┴─────────────────────┘                       │
│                                     │                                       │
│                        ┌────────────▼────────────┐                          │
│                        │      CATEGORY 4         │                          │
│                        │  Transportation of      │                          │
│                        │  purchased goods to     │                          │
│                        │  your organization      │                          │
│                        │  from Tier 1 suppliers  │                          │
│                        └─────────────────────────┘                          │
│                                                                             │
│  INCLUDED IN CATEGORY 4:                                                    │
│  • Third-party trucking services                                            │
│  • Rail freight (purchased services)                                        │
│  • Air cargo (inbound shipments)                                            │
│  • Maritime shipping (imports)                                              │
│  • Courier and parcel services                                              │
│  • Warehousing & distribution centers (third-party)                         │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
Category 4 vs Category 9
  • Category 4 (Upstream): Transportation of products you purchase from suppliers to your facilities
  • Category 9 (Downstream): Transportation of products you sell from your facilities to customers
Important ClarificationsOutbound logistics you pay for = Category 4 (not Category 9)If your company purchases outbound logistics services (e.g., you hire a carrier to deliver products to your customers), those emissions belong in Category 4—not Category 9. This is because Category 4 includes all transportation services you purchase, regardless of direction. Category 9 only applies when the customer arranges and pays for transportation.Upstream of Tier 1 = Category 1 (not Category 4)Transportation between your Tier 2 and Tier 1 suppliers (i.e., upstream of your direct suppliers) is not included in Category 4. Those emissions are already embedded in Category 1 (Purchased Goods and Services) as part of the cradle-to-gate footprint of the products you purchase.

Prerequisites

Before starting, ensure you have:
Using the Dcycle App?You can track upstream transportation through our web interface:

Data Map: Category 4 Requirements Overview

┌─────────────────────────────────────────────────────────────────────────────────────────┐
│                   CATEGORY 4 DATA REQUIREMENTS OVERVIEW                                  │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│                                                                                         │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │ TRANSPORT ROUTE (Per shipment)                                                  │   │
│  ├─────────────────────────────────────────────────────────────────────────────────┤   │
│  │                                                                                 │   │
│  │  Required Fields              Optional Fields                                   │   │
│  │  ──────────────────           ──────────────────                                │   │
│  │  • start_date                 • name (shipment name)                            │   │
│  │  • end_date                   • supplier (supplier name)                        │   │
│  │  • quantity_transported       • refrigerated (true/false)                       │   │
│  │  • unit_id (kg or tonnes)                                                       │   │
│  │  • transport_direction                                                          │   │
│  │    ("upstream" for Cat 4)                                                       │   │
│  │                                                                                 │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                                                                         │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │ TRANSPORT SECTIONS (Per leg of journey)                                         │   │
│  ├─────────────────────────────────────────────────────────────────────────────────┤   │
│  │                                                                                 │   │
│  │  Conditionally Required       Optional Fields                                   │   │
│  │  ──────────────────────       ──────────────────                                │   │
│  │  • origin (city/address)*     • travel_method (car/truck                        │   │
│  │  • destination (city/addr)*     for road transport)                             │   │
│  │  • transport_type             • electric (true/false)                           │   │
│  │    (air/road/rail/maritime/                                                     │   │
│  │     unknown)                                                                    │   │
│  │  • kms (distance)*                                                              │   │
│  │                                                                                 │   │
│  │  * See conditional requirements below                                           │   │
│  │                                                                                 │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                                                                         │
│  ┌─────────────────────────────────────────────────────────────────────────────────┐   │
│  │ CALCULATION FLOW                                                                │   │
│  ├─────────────────────────────────────────────────────────────────────────────────┤   │
│  │                                                                                 │   │
│  │  Origin + Destination ──► Geocoding ──► Distance (kms)                          │   │
│  │                                             │                                   │   │
│  │  transport_type + electric + refrigerated ──┼──► Emission Factor                │   │
│  │                                             │                                   │   │
│  │  Quantity (kg/t) × Distance (km) × EF ──────┴──► CO₂e (kg)                      │   │
│  │                                                                                 │   │
│  └─────────────────────────────────────────────────────────────────────────────────┘   │
│                                                                                         │
└─────────────────────────────────────────────────────────────────────────────────────────┘
Unknown Transport TypeIf the transport type (vehicle) is unknown, you can set transport_type: "unknown" and Dcycle will estimate the most likely transport mode based on the origin and destination data.When using transport_type: "unknown":
  • Origin and destination are mandatory - Dcycle needs location data to estimate the appropriate transport mode
  • The system analyzes the route characteristics (distance, countries involved) to determine if it’s likely road, rail, maritime, or air transport
Distance (kms) FieldThe kms field allows you to provide the exact distance if known. This is useful when:
  • You have precise distance data from your logistics provider
  • The route is non-standard (e.g., specific highway routes)
  • Geocoding might not accurately represent the actual route
Conditional requirements:
  • If kms is provided: Origin and destination are optional (distance is already known)
  • If kms is not provided: Origin and destination are mandatory (Dcycle calculates distance via geocoding)

Calculation Methods

The most accurate method uses the Dcycle Transport and Distribution API:
┌─────────────────────────────────────────────────────────────────────────────┐
│                    DISTANCE-BASED CALCULATION FLOW                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  Your Data          Dcycle Processing                    Output             │
│  ─────────────      ──────────────────                   ──────             │
│                                                                              │
│  ┌─────────────┐    ┌─────────────────┐    ┌──────────┐    ┌─────────────┐  │
│  │ Origin:     │    │ Geocoding:      │    │ Distance │    │ CO₂e (kg):  │  │
│  │ "Shanghai,  │ ─► │ Lat/Lng lookup  │ ─► │ ~20,000  │ ─► │ 2,450 kg    │  │
│  │  China"     │    │                 │    │ km       │    │             │  │
│  └─────────────┘    └─────────────────┘    └──────────┘    └─────────────┘  │
│                                                     │                       │
│  ┌─────────────┐    ┌─────────────────┐             │                       │
│  │ Destination:│    │ Transport type: │             │                       │
│  │ "Rotterdam, │ ─► │ maritime        │ ────────────┤                       │
│  │  Netherlands│    │                 │             │                       │
│  └─────────────┘    └─────────────────┘             │                       │
│                                                     │                       │
│  ┌─────────────┐    ┌─────────────────┐             │                       │
│  │ Weight:     │    │ Emission Factor │             │                       │
│  │ 5,000 kg    │ ─► │ based on type,  │ ────────────┘                       │
│  │             │    │ electric, refrig│                                     │
│  └─────────────┘    └─────────────────┘                                     │
│                                                                              │
│  Formula: CO₂e = Quantity × Distance (km) × Emission Factor                 │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘
Dcycle supports four transport types:
Transport TypeDescriptionUse Case
airAir freightExpress international, high-value goods
roadRoad transportRegional/national freight
railRail freightBulk goods, continental routes
maritimeSea freightInternational shipping, containers
For road transport, you can further specify the travel_method:
  • truck - Heavy goods vehicles (default for freight)
  • car - Light vehicles
  • bicycle - Zero emission (non-electric)
  • electric_kick_scooter - Last-mile delivery
  • motorbike - Small deliveries
Emission factors are adjusted based on:Electric vehicles:
  • Set electric: true for electric vehicles
  • Significantly lower emissions for road and rail
Refrigerated transport:
  • Set refrigerated: true for cold chain logistics
  • Adds ~10-20% to emissions due to cooling energy
Aircraft size (for air transport):
  • Automatically determined by distance
  • Short-haul vs long-haul aircraft have different factors
For transport and distribution, Dcycle uses emission factors from:Ecoinvent:
  • Comprehensive lifecycle emission factors by transport mode
  • Covers road, rail, air, and maritime transport
  • Accounts for vehicle type, fuel, and regional variations
  • Updated regularly with latest LCA data
Distance calculation:
  • Road/Rail/Air: Google Maps API for accurate routing
  • Maritime: CERDI distance databases

Step 1: Calculate Shipment Emissions

Transport Route fields:
FieldTypeRequiredDescriptionExample
quantity_transportednumberCargo weight5000
unit_idUUIDUnit (kg or metric_tonne)"kg-unit-uuid"
start_datedateStart of transport period"2024-01-15"
end_datedateEnd of transport period"2024-01-20"
transport_directionstringDirection"upstream"
supplierstringSupplier name"Supplier A"
refrigeratedbooleanCold chain transportfalse
Transport Section fields:
FieldTypeRequiredDescriptionExample
originstring⚠️Supplier location (city, country)"Shanghai, China"
destinationstring⚠️Your facility location"Rotterdam, Netherlands"
transport_typestringMode of transport"maritime" or "unknown"
kmsnumber⚠️Distance in kilometers19500
travel_methodstringVehicle type (for road)"truck"
electricbooleanElectric vehiclefalse
⚠️ Conditional requirements:
  • If kms is provided → origin and destination are optional
  • If kms is NOT provided → origin and destination are required
  • If transport_type is "unknown"origin and destination are required
Where to get this data:
  • Origin/Destination: Supplier address from purchase orders or invoices
  • Weight: Bill of lading, packing list, or purchase order
  • Transport type: Carrier information (air, road, rail, maritime) or “unknown”
  • Kms: Distance from logistics provider or transport invoice
Track individual inbound shipments from suppliers:
import requests
import os

headers = {
    "Authorization": f"Bearer {os.getenv('DCYCLE_API_KEY')}",
    "Content-Type": "application/json",
    "x-organization-id": os.getenv("DCYCLE_ORG_ID"),
    "x-user-id": os.getenv("DCYCLE_USER_ID"),
}

# Get unit IDs
units = requests.get(
    "https://api.dcycle.io/api/v1/units",
    headers=headers,
    params={"page": 1, "size": 100}
).json()
kg_unit = next(u for u in units['items'] if u['abbreviation'] == 'kg')

# Example: International sea freight from supplier
# Step 1: Create transport route
transport_route = {
    "start_date": "2024-01-15",
    "end_date": "2024-01-20",
    "quantity_transported": 5000,  # 5 tonnes
    "unit_id": kg_unit['id'],
    "transport_direction": "upstream",  # Category 4
    "supplier": "Supplier A - Shanghai",
    "refrigerated": False,
}

route_response = requests.post(
    "https://api.dcycle.io/api/v1/transport_routes",
    headers=headers,
    json=transport_route
).json()

# Step 2: Create transport section (leg of journey)
transport_section = {
    "transport_route_id": route_response['id'],
    "origin": "Shanghai, China",
    "destination": "Rotterdam, Netherlands",
    "transport_type": "maritime",
    "electric": False,
}

section_response = requests.post(
    "https://api.dcycle.io/api/v1/transport_sections",
    headers=headers,
    json=transport_section
).json()

print(f"✅ Inbound shipment created")
print(f"   Route ID: {route_response['id']}")
print(f"   Origin: {transport_section['origin']}")
print(f"   Destination: {transport_section['destination']}")
print(f"   Transport type: {transport_section['transport_type']}")
print(f"   Weight: {transport_route['quantity_transported']:,} kg")
print(f"   CO₂e: {route_response.get('co2e', 'Calculating...')} kg")

Multi-Modal Shipments

For shipments using multiple transport modes (e.g., sea + road), create multiple transport sections for a single route:
# Multi-modal: Sea freight + road delivery
# Create one transport route with multiple sections

# Step 1: Create the transport route
transport_route = {
    "start_date": "2024-01-15",
    "end_date": "2024-01-25",
    "quantity_transported": 5000,
    "unit_id": kg_unit['id'],
    "transport_direction": "upstream",
    "supplier": "Supplier A - Shanghai",
}

route_response = requests.post(
    "https://api.dcycle.io/api/v1/transport_routes",
    headers=headers,
    json=transport_route
).json()

route_id = route_response['id']

# Step 2: Create transport sections for each leg
sections = [
    {
        "transport_route_id": route_id,
        "origin": "Shanghai, China",
        "destination": "Rotterdam, Netherlands",
        "transport_type": "maritime",
        "part": 1,  # First leg
    },
    {
        "transport_route_id": route_id,
        "origin": "Rotterdam, Netherlands",
        "destination": "Munich, Germany",
        "transport_type": "road",
        "travel_method": "truck",
        "part": 2,  # Second leg
    }
]

for section in sections:
    section_response = requests.post(
        "https://api.dcycle.io/api/v1/transport_sections",
        headers=headers,
        json=section
    ).json()
    print(f"   Part {section['part']}: {section['origin']}{section['destination']}")
    print(f"      Transport: {section['transport_type']}")
    print(f"      Distance: {section_response.get('kms', 'Calculating...')} km")

# Get total CO₂e for the route
route = requests.get(
    f"https://api.dcycle.io/api/v1/transport_routes/{route_id}",
    headers=headers
).json()

print(f"\n📊 Total shipment:")
print(f"   Total CO₂e: {route['co2e']:.2f} kg")

Step 2: Bulk Upload Inbound Shipments

For high-volume operations, upload shipments via CSV:

CSV Format

origin,destination,transport_type,travel_method,kms,quantity,unit,start_date,end_date,direction,supplier,refrigerated
"Shanghai, China","Rotterdam, Netherlands",maritime,,19500,5000,kg,2024-01-15,2024-01-20,upstream,Supplier A,false
"Rotterdam, Netherlands","Munich, Germany",road,truck,,5000,kg,2024-01-20,2024-01-22,upstream,Supplier A,false
"Milan, Italy","Barcelona, Spain",road,truck,950,2500,kg,2024-02-01,2024-02-02,upstream,Supplier B,false
"Paris, France","Madrid, Spain",unknown,,,500,kg,2024-02-10,2024-02-11,upstream,Supplier C,true
"Hamburg, Germany","Valencia, Spain",maritime,,2100,8000,kg,2024-03-01,2024-03-10,upstream,Supplier D,false
CSV Notes:
  • kms column: Leave empty to auto-calculate distance from origin/destination
  • transport_type: Use unknown if vehicle type is not known (origin/destination required)
  • travel_method: Only needed for road transport type (truck, car, etc.)

Upload and Process

import requests
import os
import time

headers = {
    "Authorization": f"Bearer {os.getenv('DCYCLE_API_KEY')}",
    "Content-Type": "application/json",
    "x-organization-id": os.getenv("DCYCLE_ORG_ID"),
    "x-user-id": os.getenv("DCYCLE_USER_ID"),
}

# Step 1: Get presigned upload URL
upload_response = requests.post(
    "https://api.dcycle.io/api/v1/logistics/bulk/csv",
    headers=headers
).json()

presigned_url = upload_response['upload_url']
status_url = upload_response['status_url']

# Step 2: Upload CSV to S3
with open('inbound_shipments_q1_2024.csv', 'rb') as f:
    requests.put(presigned_url, data=f)

print("✅ CSV uploaded, processing...")

# Step 3: Poll for completion
while True:
    status = requests.get(status_url, headers=headers).json()
    
    if status['status'] == 'completed':
        print(f"\n✅ Processed {status['records_processed']} inbound shipments")
        print(f"   Total CO₂e: {status['total_co2e']:,.2f} kg")
        print(f"   Total distance: {status['total_distance_km']:,.0f} km")
        print(f"   Category: Scope 3 - Category 4 (Upstream Transport)")
        break
    elif status['status'] == 'failed':
        print(f"❌ Upload failed: {status['error']}")
        break
    elif status['status'] == 'processing':
        progress = status.get('progress_percentage', 0)
        print(f"⏳ Processing... {progress}%")
    
    time.sleep(5)

Step 3: Query Category 4 Emissions

Get Category 4 Totals

import requests
import os

headers = {
    "Authorization": f"Bearer {os.getenv('DCYCLE_API_KEY')}",
    "Content-Type": "application/json",
    "x-organization-id": os.getenv("DCYCLE_ORG_ID"),
    "x-user-id": os.getenv("DCYCLE_USER_ID"),
}

# Query logistics emissions (Category 4)
response = requests.get(
    "https://api.dcycle.io/api/v1/logistics/report",
    headers=headers,
    params={
        "start_date": "2024-01-01",
        "end_date": "2024-12-31",
        "direction": "inbound",  # Category 4 only
    }
).json()

print(f"📊 Scope 3 Category 4: Upstream Transportation (2024)")
print("=" * 60)
print(f"   Total shipments: {response['total_shipments']:,}")
print(f"   Total distance: {response['total_distance_km']:,.0f} km")
print(f"   Total weight: {response['total_weight_kg']:,.0f} kg")
print(f"   Total CO₂e: {response['total_co2e']:,.0f} kg ({response['total_co2e']/1000:.1f} tonnes)")
print()

# Breakdown by transport type
print("   By Transport Type:")
for mode in response.get('by_transport_type', []):
    print(f"     - {mode['transport_type']}: {mode['co2e']:,.0f} kg ({mode['percentage']:.1f}%)")

Analyze by Supplier

# Get emissions by supplier origin
response = requests.get(
    "https://api.dcycle.io/api/v1/logistics/report",
    headers=headers,
    params={
        "start_date": "2024-01-01",
        "end_date": "2024-12-31",
        "direction": "inbound",
        "group_by": "origin_country"
    }
).json()

print("\n📊 Category 4 by Supplier Country:")
print("-" * 50)

for country in response.get('by_origin_country', []):
    print(f"   {country['country']}: {country['co2e']:,.0f} kg CO₂e")
    print(f"      Shipments: {country['shipments']}")
    print(f"      Avg distance: {country['avg_distance_km']:,.0f} km")
    print()

Compare Category 4 to Other Scope 3

# Get total Scope 3 for context
scope3_response = requests.get(
    "https://api.dcycle.io/api/v1/total_impacts",
    headers=headers,
    params={
        "start_date": "2024-01-01",
        "end_date": "2024-12-31",
        "scopes": "scope_3"
    }
).json()

scope3_total = sum(i["emissions_per_day"] for i in scope3_response.get("items", []))
cat4_total = response['total_co2e']

print(f"\n📊 Category 4 Context:")
print(f"   Category 4: {cat4_total:,.0f} kg CO₂e")
print(f"   Total Scope 3: {scope3_total:,.0f} kg CO₂e")
if scope3_total > 0:
    print(f"   Category 4 as % of Scope 3: {cat4_total/scope3_total*100:.1f}%")

Best Practices

1. Ensure Complete Shipment Data

def validate_shipment_data(shipment):
    """Validate required shipment data"""
    
    required_fields = ['origin', 'destination', 'transport_type', 'quantity_transported']
    missing = [f for f in required_fields if not shipment.get(f)]
    
    if missing:
        return False, f"Missing required fields: {missing}"
    
    return True, "Valid shipment data"

# Validate before submission
is_valid, message = validate_shipment_data(shipment)
if not is_valid:
    print(f"⚠️ {message}")

2. Integrate with Procurement

Link logistics to your purchase orders for traceability:
# Create transport route linked to supplier
transport_route = {
    "start_date": "2024-03-01",
    "end_date": "2024-03-03",
    "quantity_transported": 8000,
    "unit_id": kg_unit['id'],
    "transport_direction": "upstream",
    "supplier": "Supplier B - Germany",  # Supplier reference
    "name": "PO-2024-00456",  # Purchase order reference
}

# Create transport section with location details
transport_section = {
    "transport_route_id": route_id,
    "origin": "Supplier warehouse, Germany",
    "destination": "Your facility, Spain",
    "transport_type": "road",
    "travel_method": "truck",
}

3. Set Reduction Targets

# Track Category 4 intensity over time
def calculate_transport_intensity(year):
    """Calculate kg CO₂e per kg of goods received"""
    
    logistics = requests.get(
        "https://api.dcycle.io/api/v1/logistics/report",
        headers=headers,
        params={
            "start_date": f"{year}-01-01",
            "end_date": f"{year}-12-31",
            "direction": "inbound"
        }
    ).json()
    
    if logistics['total_weight_kg'] > 0:
        intensity = logistics['total_co2e'] / logistics['total_weight_kg']
        return intensity
    return 0

# Compare years
for year in [2022, 2023, 2024]:
    intensity = calculate_transport_intensity(year)
    print(f"{year}: {intensity:.3f} kg CO₂e/kg cargo")
Reduction Strategies for Category 4
  1. Consolidate shipments: Fewer, fuller trucks = lower emissions per kg
  2. Near-shoring: Source from closer suppliers
  3. Modal shift: Sea/rail instead of air/road where possible
  4. Supplier engagement: Work with logistics providers on efficiency
  5. Route optimization: Encourage direct routes, avoid transshipment

Troubleshooting

Issue: Missing Origin/Destination Data

# Fallback: Use supplier country + your facility
if not shipment.get('origin'):
    # Get supplier country from supplier record
    supplier = requests.get(
        f"https://api.dcycle.io/api/v1/suppliers/{supplier_id}",
        headers=headers
    ).json()
    
    shipment['origin'] = f"{supplier['city']}, {supplier['country']}"
    print(f"⚠️  Using supplier address: {shipment['origin']}")

# Or use manual distance if geocoding fails
shipment['manual_distance_km'] = 500  # Estimated distance

Issue: Unknown Transport Mode

# Suggest transport type based on context
def suggest_transport_type(origin_country, destination_country):
    """Suggest appropriate transport type based on route"""
    
    # International routes
    if origin_country != destination_country:
        # Check if likely sea routes (intercontinental)
        sea_countries = ['CN', 'JP', 'KR', 'US', 'BR', 'AU', 'IN']
        if origin_country in sea_countries or destination_country in sea_countries:
            return 'maritime'
        else:
            return 'road'  # European road freight
    
    # Domestic routes
    else:
        return 'road'

transport_type = suggest_transport_type('CN', 'ES')
print(f"Suggested transport type: {transport_type}")

# For road transport, also suggest travel method based on weight
def suggest_travel_method(weight_kg):
    """Suggest travel method for road transport"""
    if weight_kg > 3500:
        return 'truck'
    else:
        return 'car'

Issue: Category 4 Seems Too High/Low

# Benchmark Category 4 as % of purchased goods
cat1_response = requests.get(
    "https://api.dcycle.io/api/v1/total_impacts",
    headers=headers,
    params={
        "start_date": "2024-01-01",
        "end_date": "2024-12-31",
        "categories": "purchases"  # Category 1
    }
).json()

cat1_total = sum(i["emissions_per_day"] for i in cat1_response.get("items", []))

# Category 4 typically 5-15% of Category 1
expected_range = (0.05, 0.15)
actual_ratio = cat4_total / cat1_total if cat1_total > 0 else 0

if actual_ratio < expected_range[0]:
    print(f"⚠️  Category 4 ({actual_ratio:.1%}) below typical range")
    print("   Possible causes:")
    print("   - Missing shipment data")
    print("   - Local sourcing (short distances)")
    
elif actual_ratio > expected_range[1]:
    print(f"⚠️  Category 4 ({actual_ratio:.1%}) above typical range")
    print("   Possible causes:")
    print("   - Long-distance/international sourcing")
    print("   - High proportion of air freight")
    print("   - Duplicate entries")

Next Steps