Packing Optimization API
The Packing Optimization API calculates optimal 3D packing solutions for SKUs into pallets and containers using a hybrid 3-layer architecture combining rule-based systems, AI prediction, and mathematical optimization.
Overview
The Packing API solves the 3D bin packing problem with support for:
- Multiple pallet types: Mixed pallet sizes and specifications
- Weight constraints: Per-pallet and per-SKU weight limits
- Dangerous goods: Isolation and special handling
- Stacking constraints: Non-stackable item support
- Pre-assigned pallets: Items already packed in specific pallets
- Unavailable pallet filtering: Exclude specific pallet types
- Vehicle loading: Container/truck loading optimization
Architecture
The packing service uses a unique three-layer hybrid architecture:
┌─────────────────────────────────────────────────────────┐
│ Layer 3: OR-Tools Mathematical Solver │
│ - 100% correctness guarantee │
│ - Constraint Programming (CP-SAT │
│ - Optimal or near-optimal solutions │
└─────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────┐
│ Layer 2: AI Prediction Model │
│ - SKU-pallet compatibility scoring │
│ - Placement sequence optimization │
│ - Pattern recognition from historical data │
└─────────────────────────────────────────────────────────┘
↑
┌─────────────────────────────────────────────────────────┐
│ Layer 1: Rule Engine │
│ - Pre-assigned pallet handling │
│ - Unavailable pallet filtering │
│ - Dangerous goods isolation │
│ - Stacking constraints │
└─────────────────────────────────────────────────────────┘Performance Characteristics:
| Layer | Speed | Optimality | Use Case |
|---|---|---|---|
| Rule Engine | < 50ms | Basic | Fast validation |
| AI Model | 100-500ms | Good | Quick solutions |
| OR-Tools | 1-30s | Optimal | High-quality results |
Endpoints
Calculate Packing
POST /api/packing/calculate
Calculate the optimal packing plan for a set of SKUs.
Request Body:
{
"skus": [
{
"id": "SKU-001",
"weight": 2.5,
"weight_unit": "kg",
"length": 20.0,
"width": 15.0,
"height": 10.0,
"quantity": 50,
"is_dangerous": false,
"is_stackable": true
}
],
"pallets": [
{
"id": "EUR-PALLET",
"length": 120.0,
"width": 80.0,
"height": 150.0,
"weight": 25.0,
"max_weight": 1500.0,
"cost": 8.50,
"flexible_height_ratio": 0.05
}
],
"vehicle": {
"length": 1200.0,
"width": 240.0,
"height": 260.0,
"max_weight": 24000.0,
"unit": "cm"
},
"entropy_factor": 1.0,
"pre_assigned_pallets": null,
"unavailable_pallet_types": null,
"use_ai": true,
"time_limit_ms": 30000
}SKU Fields:
| Field | Type | Default | Description |
|---|---|---|---|
| id | string | ✓ | Unique SKU identifier |
| weight | float | ✓ | Weight of one unit |
| weight_unit | string | "kg" | Weight unit (kg, g, lb, oz) |
| length | float | ✓ | Length dimension |
| width | float | ✓ | Width dimension |
| height | float | ✓ | Height dimension |
| quantity | integer | 1 | Number of units |
| is_dangerous | boolean | false | Dangerous goods flag |
| is_stackable | boolean | true | Can be stacked |
Dimension Units
All SKU dimensions default to cm unless specified otherwise. The API automatically converts units based on unit specifications.
Pallet Fields:
| Field | Type | Default | Description |
|---|---|---|---|
| id | string | ✓ | Pallet type identifier |
| length | float | ✓ | Pallet length |
| width | float | ✓ | Pallet width |
| height | float | ✓ | Pallet height |
| weight | float | 0.0 | Pallet tare weight |
| max_weight | float | ✓ | Maximum loaded weight |
| length_unit | string | "cm" | Dimension unit |
| weight_unit | string | "kg" | Weight unit |
| cost | float | 0.0 | Cost per pallet use |
| max_height | float | null | Optional max stack height |
| flexible_height_ratio | float | 0.05 | Height tolerance ratio |
Flexible Height
The flexible_height_ratio allows small height overruns. Default 5% means a ratio means items can exceed pallet height by 5% if otherwise impossible to pack.
Vehicle Fields (Optional):
| Field | Type | Description |
|---|---|---|
| length | float | Vehicle interior length |
| width | float | Vehicle interior width |
| height | float | Vehicle interior height |
| max_weight | float | Maximum vehicle payload |
| unit | string | Dimension unit |
Optimization Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
| entropy_factor | float | 1.0 | Solution diversity (0.8-2.0) |
| pre_assigned_pallets | array | null | Already packed pallets |
| unavailable_pallet_types | array | null | Pallet types to exclude |
| use_ai | boolean | true | Enable AI enhancement |
| time_limit_ms | integer | 30000 | Max solve time in ms |
Response:
{
"pallets": [
{
"id": "pallet_1",
"pallet_type": "EUR-PALLET",
"items": [
{
"sku_id": "SKU-001",
"quantity": 45
}
],
"length": 120.0,
"width": 80.0,
"height": 140.0,
"total_weight": 112.5,
"total_volume": 135000.0,
"pallet_volume": 1440000.0,
"sku_volume": 135000.0,
"pallet_cost": 8.50,
"utilization": 0.78
}
],
"total_pallets": 2,
"total_weight": 225.0,
"total_volume": 270000.0,
"gross_weight": 275.0,
"net_weight": 225.0,
"total_items": 100,
"loaded_items": 90,
"unloaded_items": 10,
"total_sku_base_volume": 300000.0,
"volume_reduction": 0.1,
"pallet_volume": 2880000.0,
"total_cost": 17.0,
"vehicle_utilization": 0.15,
"algorithm_used": "IntelligentPacking 3-Layer Architecture",
"pallet_type_usage": {
"EUR-PALLET": 2
},
"solve_time_ms": 1523.45,
"errors": [],
"unloaded_items_detail": [
{
"sku_id": "SKU-001",
"quantity": 10,
"reason": "weight_limit_exceeded"
}
],
"success": false,
"message": "Packing complete with 10 items unloaded due to weight constraints",
"statistics": {
"weight_utilization": 0.75,
"volume_utilization": 0.78,
"pallet_efficiency": 0.92
},
"layers_used": ["rule_engine", "ai_model", "or_tools"],
"processing_time_ms": 1650.23
}Pre-Assigned Pallets
Specify items already packed in specific pallets:
{
"skus": [...],
"pallets": [...],
"pre_assigned_pallets": [
{
"pallet_type_id": "EUR-PALLET",
"pallet_id": "PRE-PAL-001",
"items": [
{
"sku_id": "SKU-001",
"quantity": 10
}
],
"used_volume": 30000.0,
"used_weight": 25.0
}
]
}The optimizer will respect these assignments and optimize around them.
Unavailable Pallet Types
Exclude specific pallet types from consideration:
{
"skus": [...],
"pallets": [...],
"unavailable_pallet_types": ["CHEP-PALLET", "ONE-WAY"]
}Legacy Endpoint
POST /api/packing/calculate/legacy
Legacy endpoint with same interface as /calculate for backward compatibility with older clients.
Health Check
GET /api/packing/health
Check packing service health status.
Response:
{
"status": "healthy",
"service": "packing",
"version": "7.0.0",
"architecture": "3-Layer Hybrid (Rule Engine + AI + OR-Tools)"
}Error Handling
The API returns structured errors in the errors array:
"errors": [
{
"sku_id": "SKU-001",
"error_type": "dimension_too_large",
"message": "SKU dimensions exceed all available pallets",
"details": {
"sku_dimensions": [200, 150, 100],
"largest_pallet": [120, 80, 150]
},
"severity": "error"
}
]Error Types:
| Type | Severity | Description |
|---|---|---|
dimension_too_large | error | SKU larger than any pallet |
weight_limit_exceeded | warning | Cannot fit due to weight |
dangerous_isolation | warning | Dangerous goods isolation constraint |
stacking_violation | warning | Non-stackable stacking violation |
unloading_unsolvable | error | Packing problem infeasible |
Performance Tips
1. Start Simple
- Order SKUs by volume descending:
# Best packing efficiency
skus.sort(key=lambda x: x.length * x.width * x.height, reverse=True)2. Weight Distribution
Distribute weight evenly across pallets:
{
"pallets": [
{"id": "heavy", "max_weight": 2000 /* heavy items */ },
{"id": "light", "max_weight": 500 /* light items */ }
]
}3. Dangerous Goods Isolation
Separate dangerous goods from regular items:
for sku in skus:
if sku.is_dangerous:
# These items get their own pallets
pass4. Time Limit Tuning
Adjust based on problem size:
| SKU Count | Recommended Time Limit |
|---|---|
| < 10 | 5000ms |
| 10-50 | 15000ms |
| 50-200 | 30000ms |
| > 200 | 60000ms |
Examples
Example 1: Simple E-Commerce Order
payload = {
"skus": [
{"id": "BOX-SMALL", "weight": 0.5, "length": 15, "width": 10, "height": 8, "quantity": 200},
{"id": "BOX-MEDIUM", "weight": 2.0, "length": 30, "width": 20, "height": 15, "quantity": 50},
{"id": "BOX-LARGE", "weight": 5.0, "length": 50, "width": 40, "height": 30, "quantity": 10}
],
"pallets": [
{
"id": "STD", "length": 120, "width": 100, "height": 150, "max_weight": 1000, "cost": 5.0
}
],
"use_ai": True,
"time_limit_ms": 15000
}
response = requests.post("/api/packing/calculate", json=payload)Example 2: Mixed Pallet Types
payload = {
"skus": [...],
"pallets": [
{"id": "EUR", "length": 120, "width": 80, "height": 200, "max_weight": 1500, "cost": 8.0},
{"id": "HALF", "length": 80, "width": 60, "height": 150, "max_weight": 500, "cost": 4.0},
{"id": "INDUSTRIAL", "length": 120, "width": 100, "height": 180, "max_weight": 2000, "cost": 12.0}
]
}Example 3: With Vehicle Loading
payload = {
"skus": [...],
"pallets": [...],
"vehicle": {
"length": 1360, "width": 244, "height": 260, "max_weight": 20000, "unit": "cm"
}
}Cost Optimization
Minimize total cost when multiple pallet types:
# API automatically selects optimal pallet mix to minimize:
# total_cost = sum(pallet_type.cost * count)Set appropriate costs on pallets to get cost-optimized solutions.