Shipping Costs

Shipping Costs Guide

This guide explains how to calculate shipping costs for outbound orders using the Spreetail Channel Integration API.

Overview

The shipping cost calculation endpoint allows you to:

  • Get accurate shipping cost estimates
  • Compare different shipping options
  • Calculate costs before order creation
  • Optimize shipping decisions
ℹ️

Note: The shipping cost endpoint requires enablement. Contact [email protected] to enable this feature for your account. The endpoint requires EnabledEndpoints.outbound_cost = true in your client configuration.

Endpoint

POST /outbound-cost

Full URL: https://api.spreetaileu.com/api/api/v1/outbound-cost

Authentication

⚠️

Important: This endpoint uses API Key authentication, not Bearer token authentication. Use your Client ID and Client Secret in the format: ApiKey CLIENT_ID:CLIENT_SECRET

Include your Client ID and Client Secret in the Authorization header:

Authorization: ApiKey your-client-id:your-client-secret

Request Structure

To calculate shipping costs, provide:

  • Country: Destination country code (ISO 3166-1 alpha-2, e.g., "DE", "GB", "US")
  • Items: Array of items, each with:
    • sku (string): Product SKU identifier
    • weight (number): Item weight in kilograms (minimum: 0.01, maximum: 10000)
    • length (number): Item length in centimeters (minimum: 0.01, maximum: 10000)
    • width (number): Item width in centimeters (minimum: 0.01, maximum: 10000)
    • height (number): Item height in centimeters (minimum: 0.01, maximum: 10000)

Required Fields

FieldTypeDescription
countrystringDestination country code (ISO 3166-1 alpha-2, exactly 2 characters)
itemsarrayArray of items to ship (minimum 1 item required)
items[].skustringProduct SKU identifier
items[].weightnumberItem weight in kilograms (0.01 - 10000)
items[].lengthnumberItem length in centimeters (0.01 - 10000)
items[].widthnumberItem width in centimeters (0.01 - 10000)
items[].heightnumberItem height in centimeters (0.01 - 10000)

Example Request: Single Item

{
  "country": "DE",
  "items": [
    {
      "sku": "PROD-12345",
      "weight": 2.5,
      "length": 20,
      "width": 15,
      "height": 10
    }
  ]
}

Example Request: Multiple Items

{
  "country": "GB",
  "items": [
    {
      "sku": "PROD-12345",
      "weight": 2.5,
      "length": 20,
      "width": 15,
      "height": 10
    },
    {
      "sku": "PROD-67890",
      "weight": 1.2,
      "length": 15,
      "width": 10,
      "height": 5
    }
  ]
}

Code Examples

Python

import requests

def calculate_shipping_cost(client_id, client_secret, cost_data):
    """Calculate shipping cost for an order"""
    url = "https://api.spreetaileu.com/api/api/v1/outbound-cost"
    headers = {
        "Authorization": f"ApiKey {client_id}:{client_secret}",
        "Content-Type": "application/json"
    }
    
    response = requests.post(url, headers=headers, json=cost_data)
    response.raise_for_status()
    return response.json()

# Example usage
cost_request = {
    "country": "DE",
    "items": [
        {
            "sku": "PROD-12345",
            "weight": 2.5,
            "length": 20,
            "width": 15,
            "height": 10
        }
    ]
}

result = calculate_shipping_cost("your-client-id", "your-client-secret", cost_request)

# Display eligible carriers
if result['success']:
    print("Available shipping options:")
    for carrier in result['data']['eligible_carriers']:
        print(f"  {carrier['carrier_name']} ({carrier['service_name']}): "
              f"{carrier['cost']} {carrier['currency']} - "
              f"{carrier['estimated_delivery_days']} days")
    
    # Display rejected carriers if any
    if result['data']['rejected_carriers']:
        print("\nUnavailable carriers:")
        for carrier in result['data']['rejected_carriers']:
            print(f"  {carrier['carrier_name']}: {carrier['reason']}")

JavaScript/Node.js

async function calculateShippingCost(clientId, clientSecret, costData) {
  const url = 'https://api.spreetaileu.com/api/api/v1/outbound-cost';
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': `ApiKey ${clientId}:${clientSecret}`,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(costData)
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Shipping cost calculation failed: ${error.error || response.statusText}`);
  }
  
  return await response.json();
}

// Example usage
const costRequest = {
  country: 'DE',
  items: [
    {
      sku: 'PROD-12345',
      weight: 2.5,
      length: 20,
      width: 15,
      height: 10
    }
  ]
};

calculateShippingCost('your-client-id', 'your-client-secret', costRequest)
  .then(result => {
    if (result.success) {
      console.log('Available shipping options:');
      result.data.eligible_carriers.forEach(carrier => {
        console.log(
          `${carrier.carrier_name} (${carrier.service_name}): ` +
          `${carrier.cost} ${carrier.currency} - ` +
          `${carrier.estimated_delivery_days} days`
        );
      });
      
      if (result.data.rejected_carriers.length > 0) {
        console.log('\nUnavailable carriers:');
        result.data.rejected_carriers.forEach(carrier => {
          console.log(`${carrier.carrier_name}: ${carrier.reason}`);
        });
      }
    }
  })
  .catch(error => console.error('Error:', error));

Response Format

Success Response (200)

{
  "success": true,
  "data": {
    "eligible_carriers": [
      {
        "carrier_name": "DHL",
        "service_name": "Express",
        "cost": 12.5,
        "currency": "EUR",
        "estimated_delivery_days": 2,
        "tracking_available": true
      },
      {
        "carrier_name": "DPD",
        "service_name": "Standard",
        "cost": 8.75,
        "currency": "EUR",
        "estimated_delivery_days": 5,
        "tracking_available": true
      }
    ],
    "rejected_carriers": [
      {
        "carrier_name": "Royal Mail",
        "reason": "Destination not supported"
      }
    ]
  }
}

Response Fields

FieldTypeDescription
eligible_carriersarrayArray of carriers that can fulfill the shipment
eligible_carriers[].carrier_namestringName of the shipping carrier
eligible_carriers[].service_namestringService level name
eligible_carriers[].costnumberShipping cost
eligible_carriers[].currencystringCurrency code (ISO 4217)
eligible_carriers[].estimated_delivery_daysintegerEstimated delivery time in days
eligible_carriers[].tracking_availablebooleanWhether tracking is available
rejected_carriersarrayArray of carriers that cannot fulfill the shipment
rejected_carriers[].carrier_namestringName of the shipping carrier
rejected_carriers[].reasonstringReason for rejection

Use Cases

1. Compare Shipping Options

def compare_shipping_options(client_id, client_secret, country, items):
    """Compare different shipping options for a shipment"""
    cost_request = {
        "country": country,
        "items": items
    }
    
    result = calculate_shipping_cost(client_id, client_secret, cost_request)
    
    if result['success']:
        eligible = result['data']['eligible_carriers']
        
        # Sort by cost
        sorted_by_cost = sorted(eligible, key=lambda x: x['cost'])
        
        # Sort by delivery time
        sorted_by_time = sorted(eligible, key=lambda x: x['estimated_delivery_days'])
        
        return {
            "cheapest": sorted_by_cost[0] if sorted_by_cost else None,
            "fastest": sorted_by_time[0] if sorted_by_time else None,
            "all_options": eligible
        }
    
    return None

# Usage
items = [
    {
        "sku": "PROD-12345",
        "weight": 2.5,
        "length": 20,
        "width": 15,
        "height": 10
    }
]

options = compare_shipping_options("client-id", "client-secret", "DE", items)
if options:
    print(f"Cheapest: {options['cheapest']['carrier_name']} - {options['cheapest']['cost']} EUR")
    print(f"Fastest: {options['fastest']['carrier_name']} - {options['fastest']['estimated_delivery_days']} days")

2. Validate Shipping Feasibility

def check_shipping_feasibility(client_id, client_secret, country, items):
    """Check if shipping is possible to a destination"""
    cost_request = {
        "country": country,
        "items": items
    }
    
    result = calculate_shipping_cost(client_id, client_secret, cost_request)
    
    if result['success']:
        eligible_count = len(result['data']['eligible_carriers'])
        rejected_count = len(result['data']['rejected_carriers'])
        
        return {
            "feasible": eligible_count > 0,
            "eligible_carriers": eligible_count,
            "rejected_carriers": rejected_count,
            "options": result['data']['eligible_carriers']
        }
    
    return {"feasible": False, "error": "Calculation failed"}

# Usage
feasibility = check_shipping_feasibility("client-id", "client-secret", "DE", items)
if feasibility['feasible']:
    print(f"Shipping available via {feasibility['eligible_carriers']} carrier(s)")
else:
    print("Shipping not available to this destination")

Important Notes

Endpoint Enablement

  • This endpoint requires special enablement
  • Contact [email protected] to enable
  • Not all accounts have access by default
  • The endpoint requires EnabledEndpoints.outbound_cost = true in your client configuration

Authentication

  • Uses API Key authentication, not Bearer token
  • Format: Authorization: ApiKey CLIENT_ID:CLIENT_SECRET
  • Do not use JWT tokens for this endpoint

Country Code

  • Must be a valid ISO 3166-1 alpha-2 country code
  • Exactly 2 characters (e.g., "DE", "GB", "US")
  • Case-sensitive (use uppercase)

Weight and Dimensions

  • Weight: Must be in kilograms (minimum: 0.01, maximum: 10000)
  • Dimensions: Must be in centimeters (minimum: 0.01, maximum: 10000)
  • All dimensions are required for each item
  • Providing accurate measurements improves cost calculation accuracy

Items Array

  • At least one item is required
  • Each item must include all required fields: sku, weight, length, width, height
  • Multiple items in one request represent a combined shipment

Error Handling

Endpoint Not Enabled (403)

If the endpoint is not enabled for your account:

{
  "success": false,
  "error": "Forbidden - outbound-cost endpoint not enabled for this client"
}

Solution: Contact [email protected] to enable the feature.

Invalid Request (400)

{
  "success": false,
  "error": "Invalid request (missing fields, invalid values)"
}

Solution: Verify all required fields are present and values are within valid ranges.

Authentication Error (401)

{
  "success": false,
  "error": "Unauthorized"
}

Solution: Verify your Client ID and Client Secret are correct and use the ApiKey format.

Best Practices

  1. Calculate Before Order Creation: Get shipping costs before finalizing orders
  2. Cache Results: Cache shipping costs for similar destinations/items
  3. Handle Errors: Implement fallback for when endpoint is unavailable
  4. Compare Options: Use the response to compare multiple carrier options
  5. Consider Delivery Time: Factor in estimated delivery days when selecting carriers
  6. Validate Dimensions: Ensure weight and dimensions are accurate for better cost estimates

Related Endpoints

  • POST /outbound - Create order (use shipping cost information in order)
  • GET /order/{referenceNumber} - Check order status and actual shipping details