# Container Load Planner: 3D Truck, Trailer & Pallet Loading (`jumblemog/load-planner`) Actor

3D load plans for shipping containers (20ft, 40ft, 40ft HC), trailers, trucks and pallets: weights, payloads, stacking limits, this-side-up, floor-only, delivery stops, center of gravity. Loading steps, 3D viewer, PDF and CSV. Text, JSON or CSV input. For AI agents.

- **URL**: https://apify.com/jumblemog/load-planner.md
- **Developed by:** [Jumble Mog](https://apify.com/jumblemog) (community)
- **Categories:** AI, Automation, Developer tools
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Container Load Planner: 3D Truck, Trailer & Pallet Loading

**Find out what fits in a container, and exactly how to load it.** List your cargo and what you're shipping it in, as short text lines like `Carton: 60 x 40 x 40 cm, 14 kg, qty 120`, JSON, CSV or a Google Sheet, and it works out where every piece goes in **20ft, 40ft and 40ft high cube containers, 53ft and 13.6 m trailers, box trucks, vans and pallets**. It handles **weights and payload limits, this-side-up and any-side rotation, pieces that can't carry anything, weight limits on top, floor-only pieces, support under each piece, delivery stops, and several container types with prices**. Every plan comes back with **each piece's position and the loading order, the space and payload used, the load length and the center of gravity of each container**, plus a **3D drawing (PNG and SVG), an interactive 3D viewer that steps through the loading, a loading PDF, a CSV and Markdown**. Made for freight forwarders, shippers, importers and exporters, warehouse and dispatch teams, moving companies and e-commerce sellers shipping pallets, for developers of logistics, TMS and quoting tools, and for AI agents that need a real load plan from one API call.

![A load plan made by this Actor: a 20ft container drawn in 3D with 294 pieces stacked inside, cartons in stacks from the floor and no-stack crates on top, and a summary saying 294 of 294 pieces loaded, 75.5% of the space used and 18.9% of the payload, the fewest containers possible](https://api.apify.com/v2/key-value-stores/Km5b4PG3kYHdZSb1E/records/load-planner-example.png?signature=1uqwwMNWFSyaoIjCeyIGc)

### What it does

- **How many containers, trucks or pallets you need**, and which pieces go in each, with the fewest containers it can find (or the lowest cost when you give prices). When the plan meets the volume and weight lower bound, the result says no plan can use fewer.
- **Every piece's place**: its position from the front wall, the left wall and the floor, its size as loaded and how it was turned, in the order to load it: deepest first, and each piece after the ones it rests on.
- **Real loading rules**: the container's payload, the support under each piece (75% of its base by default), pieces kept upright (the default) or allowed on any side, pieces that must not be stacked on (`no stack`, `fragile`), a weight limit on top of a piece (`max load 100 kg`, passed down through the stack), floor-only pieces for heavy items, and must-ship or optional cargo.
- **Delivery stops**: give pieces a stop number and each stop's cargo is loaded so that it can be unloaded without moving cargo for a later stop. The result checks this and says so.
- **The numbers a dispatcher checks**: space used, payload used, floor used, load length and the length left at the doors, and the center of gravity of each container as an offset from the middle, with a warning when the weight sits off to one side.
- **Named containers and pallets**: ISO containers (20ft, 40ft, 40ft and 45ft high cube, reefers) with their usual inside sizes, payloads and door sizes; 53ft and 48ft dry vans, 13.6 m and mega trailers, 26ft, 16ft and 7.5 t box trucks with typical sizes; EUR, US (48 x 40), UK and half pallets with their deck height and a stack height limit. Or give your own inside sizes.
- **Pallets both ways**: build a pallet (cartons onto a EUR pallet, layer by layer, as low as they go), or load whole pallets into a trailer (`EUR pallet, 150 cm tall, 450 kg, qty 26`).
- **Files for the warehouse and the customer**: a 3D drawing of every container (PNG and SVG), a 3D viewer page you can turn, zoom and step through, a PDF with a summary page and, for each container, a 3D view, views from above and from the side and the numbered loading steps, a CSV of every piece's position, and Markdown for a chat reply or an email.
- **Reads what you have**: text lines in centimeters, millimeters, meters, inches or feet, kilograms, pounds or tonnes; JSON objects with many spellings of each field; CSV or TSV; Excel files; or a Google Sheet link.
- **Many loads in one run**: one per order, customer or week, up to 50, each with its own cargo, containers and settings.

### Who it's for

- **Freight forwarders, shippers, importers and exporters**: how many 20ft or 40ft containers an order needs before booking, and a load plan for the warehouse.
- **Warehouse, dispatch and transport teams**: pallets into trailers and trucks, multi-stop routes loaded in the right order, and payload checks.
- **E-commerce sellers and 3PLs**: cartons onto pallets for freight shipments and FBA-style pallet builds, with the pallet's height and weight.
- **Moving and furniture companies**: boxes and furniture into a box truck or a container, with fragile pieces on top.
- **Developers**: a 3D bin packing and container loading API for TMS, WMS, ERP, quoting tools and no-code flows (Make, n8n, Zapier), billed per plan with no monthly plan.
- **AI agents and LLM apps**: language models can't pack boxes in 3D or check payloads reliably. An agent can pass the cargo and the container as plain text and get back a checked plan, its numbers and a 3D viewer link to show the user.

### How to use it

1. List the cargo in **Cargo to load**, one line per kind of piece, such as `Carton: 60 x 40 x 40 cm, 14 kg, qty 120`. The form starts with a mixed load, so you can click **Start** right away.
2. List what to load into in **Containers**, such as `20ft container`, `40ft HC, $2,800`, `53ft trailer` or `EUR pallet, max height 180 cm`.
3. Change the rules if you need to: how cargo may turn, the support under each piece and what to optimize for.
4. Click **Start**. The **Plans** view shows the drawing, the summary and the 3D viewer and PDF links, **Containers** each container's load, **Loading steps** every piece in order, and **Not loaded** anything that didn't fit, with the reason.

You can also run it through the [Apify API](https://docs.apify.com/api/v2), from Make, n8n, Zapier and other integrations, or as a tool for AI agents.

### Writing cargo and containers

**Cargo as text**: the name (before a colon), length x width x height, then any of these, separated by commas:

| Cargo | Examples |
|---|---|
| Size | `60 x 40 x 40 cm`, `600x400x400 mm`, `48 x 40 x 36 in`, `L 60 W 40 H 40`, or a loaded pallet: `EUR pallet, 150 cm tall` |
| Weight (each) | `14 kg`, `30 lbs`, `1.2 t`, `weight 14`, or `total 1,680 kg` (split over the quantity) |
| Quantity | `qty 120`, `120 pcs`, `120 cartons`, `x 120`, `(120)`, `120x Carton: ...` |
| Turning | `upright` / `this side up` (turns on the floor only), `any side` (may lie on any side), `fixed` (no turning) |
| Stacking | `no stack` / `fragile` (nothing on top), `max load 100 kg` (most weight on top), `stack 3 high` |
| Floor | `floor only` (never on top of other cargo) |
| Stop | `stop 2` (stop 1 is unloaded first) |
| Priority | `priority` / `must ship` (loaded first when not everything fits), `optional` (loaded last) |
| Other | `group: PO-4512`, `color: #2a78d6` (for the drawings) |

**Cargo as an object** (field names are matched loosely, so `Name`, `qty`, `L`, `W`, `H`, `kg` or `this side up` work too):

```json
{"name": "Carton", "length": 60, "width": 40, "height": 40, "unit": "cm", "weight": 14, "quantity": 120, "rotation": "upright", "stackable": true, "maxLoad": 100, "floorOnly": false, "stop": 1}
```

**Containers as text**: a name from the list below or your own inside size, with an optional price, quantity and limits: `40ft HC, $2,800`, `20ft x 2`, `53ft trailer, max 44,000 lb`, `Van: 330 x 170 x 180 cm, max 1,200 kg`, `EUR pallet, max height 180 cm, max 800 kg`. Without a quantity there are as many as needed; with several types the plan picks the mix.

| Name | Inside size | Payload |
|---|---|---|
| `20ft`, `20GP`, `20' dry` | 589.8 × 235.2 × 239.3 cm | 28,180 kg |
| `40ft`, `40GP` | 1203.2 × 235.2 × 239.3 cm | 26,730 kg |
| `40ft HC`, `40HQ`, `40' high cube` | 1203.2 × 235.2 × 269.8 cm | 26,540 kg |
| `45ft HC` | 1355.6 × 235.2 × 269.8 cm | 27,700 kg |
| `20ft reefer`, `40ft HC reefer` | 544.4 × 229.4 × 227.6 cm, 1158.3 × 229.4 × 255.4 cm | 27,400 kg, 29,000 kg |
| `53ft trailer`, `48ft trailer` (typical) | 52' 6" or 47' 6" × 100" × 108" | 45,000 lb |
| `13.6 m trailer`, `mega trailer` (typical) | 1360 × 248 × 270 or 300 cm | 24,000 kg |
| `26ft box truck`, `16ft box truck`, `7.5 t truck` (typical) | 26' × 8' × 8', 16' × 92" × 87", 610 × 245 × 240 cm | 10,000 lb, 4,000 lb, 2,600 kg |
| `EUR pallet`, `US pallet`, `UK pallet`, `half pallet` | 120 × 80, 48" × 40", 120 × 100, 80 × 60 cm, up to 180 cm (72") with the deck | 1,000 kg, 2,500 lb, 1,000 kg, 500 kg |

Trailer and truck sizes vary by make, so the result notes when a typical size was used; give your own inside size if you know it.

**As a table**: paste CSV or TSV into **Cargo as CSV** with a header row such as `name,length,width,height,quantity,weight,stackable,rotation,stop`, or give a link to a CSV, Excel or JSON file or a Google Sheet. Without a header the columns are name, length, width, height, quantity and weight.

**Units**: sizes and weights take the unit written with them. Bare numbers take the most common written unit, or are read as centimeters (inches next to North American trailers and pallets), and the notes say what was assumed. Results come in centimeters and kilograms, or in inches and pounds when the sizes are imperial.

### Examples

**A mixed export load in a 20ft container** (the form's example):

```json
{
    "cargo": [
        "Carton: 60 x 40 x 40 cm, 14 kg, qty 120",
        "Small carton: 40 x 30 x 30 cm, 6 kg, qty 160",
        "Crate: 120 x 80 x 90 cm, 180 kg, qty 6, no stack",
        "Drum: 60 x 60 x 90 cm, 200 kg, qty 8, floor only"
    ],
    "containers": ["20ft container"]
}
```

> 294 of 294 pieces loaded into 1 × 20ft container, 75.5% of the space used and 18.9% of the payload; 5,320 kg, 25.06 m³. That's the fewest possible.

**Pallets into a European trailer**:

```json
{"cargo": ["EUR pallet, 160 cm tall, 620 kg, qty 26", "Half pallet: 80 x 60 x 120 cm, 240 kg, qty 10"], "containers": ["13.6 m trailer"]}
```

> 36 of 36 pieces loaded into 1 × 13.6 m trailer, 50.2% of the space used and 77.2% of the payload; 18,520 kg, 45.7 m³. That's the fewest possible.

**Furniture, choosing the cheapest mix of containers**:

```json
{
    "cargo": [
        "Sofa box: 220 x 95 x 90 cm, 85 kg, qty 18",
        "Armchair box: 95 x 90 x 100 cm, 40 kg, qty 24",
        "Table box: 180 x 100 x 20 cm, 45 kg, qty 30, any side"
    ],
    "containers": ["20ft, $1,900", "40ft HC, $2,800"],
    "goal": "cheapest"
}
```

**A delivery truck with three stops**, each stop's cargo reachable when the truck gets there:

```json
{
    "cargo": [
        {"name": "Washer", "length": 60, "width": 60, "height": 85, "unit": "cm", "weight": 70, "quantity": 12, "stop": 3},
        {"name": "Fridge", "length": 70, "width": 70, "height": 180, "unit": "cm", "weight": 90, "quantity": 6, "stop": 2},
        {"name": "Oven", "length": 60, "width": 60, "height": 60, "unit": "cm", "weight": 45, "quantity": 10, "stop": 1}
    ],
    "containers": ["26ft box truck"]
}
```

> 28 of 28 pieces loaded into 1 × 26ft box truck, 23.6% of the space used and 40.3% of the payload; 1,830 kg, 11.12 m³. That's the fewest possible. Every stop's cargo can be unloaded without moving another stop's.

**Building a pallet** from cartons, with a height limit:

```json
{"cargo": ["Case: 40 x 30 x 25 cm, 9 kg, qty 40"], "containers": ["EUR pallet, max height 160 cm"]}
```

**Several loads in one run**: settings given at the top apply to every job unless a job sets its own, and a job with its own cargo or containers doesn't take the shared ones.

```json
{
    "containers": ["40ft HC"],
    "jobs": [
        {"title": "Order 4512", "cargo": ["Carton: 60 x 40 x 40 cm, 14 kg, qty 800"]},
        {"title": "Order 4513", "cargo": ["Crate: 120 x 100 x 110 cm, 400 kg, qty 20"], "containers": ["20ft"]}
    ]
}
```

### How-to examples

These are examples from **Examples** above; the result lines are what their runs say in `summary`.

#### How to work out how many boxes fit in a shipping container

```json
{"cargo": ["Carton: 60 x 40 x 40 cm, 14 kg, qty 120", "Small carton: 40 x 30 x 30 cm, 6 kg, qty 160",
           "Crate: 120 x 80 x 90 cm, 180 kg, qty 6, no stack", "Drum: 60 x 60 x 90 cm, 200 kg, qty 8, floor only"],
 "containers": ["20ft container"]}
```

Result: "294 of 294 pieces loaded into 1 × 20ft container, 75.5% of the space used and 18.9% of the payload; 5,320 kg, 25.06 m³. That's the fewest possible." Every piece gets its position and the order to load it, with a 3D drawing.

#### How to plan pallets in a truck or trailer

```json
{"cargo": ["EUR pallet, 160 cm tall, 620 kg, qty 26", "Half pallet: 80 x 60 x 120 cm, 240 kg, qty 10"], "containers": ["13.6 m trailer"]}
```

Result: "36 of 36 pieces loaded into 1 × 13.6 m trailer, 50.2% of the space used and 77.2% of the payload; 18,520 kg, 45.7 m³. That's the fewest possible." The weight limit counts as well as the space.

#### How to load a delivery truck in stop order

```json
{"cargo": [{"name": "Washer", "length": 60, "width": 60, "height": 85, "unit": "cm", "weight": 70, "quantity": 12, "stop": 3},
           {"name": "Fridge", "length": 70, "width": 70, "height": 180, "unit": "cm", "weight": 90, "quantity": 6, "stop": 2},
           {"name": "Oven", "length": 60, "width": 60, "height": 60, "unit": "cm", "weight": 45, "quantity": 10, "stop": 1}],
 "containers": ["26ft box truck"]}
```

Result: "28 of 28 pieces loaded into 1 × 26ft box truck, 23.6% of the space used and 40.3% of the payload; 1,830 kg, 11.12 m³. That's the fewest possible. Every stop's cargo can be unloaded without moving another stop's."

#### How to let an AI agent plan loads

Add this Actor to the [Apify MCP server](https://mcp.apify.com) with `https://mcp.apify.com/?tools=jumblemog/load-planner`. The agent writes the cargo as short lines like `Carton: 60 x 40 x 40 cm, 14 kg, qty 120`, names the container, and gives the user the summary and the 3D viewer link.

### FAQ

**Which containers does it know?** ISO containers (20ft, 40ft, 40ft and 45ft high cube, reefers), 53ft and 48ft dry vans, 13.6 m and mega trailers, box trucks and pallets, with their usual inside sizes, payloads and doors. You can also give your own sizes.

**Does it follow stacking rules?** Yes: `no stack`, `floor only`, upright or any side, the payload, and support under each piece.

**Can it choose the cheapest mix of containers?** Yes. Give each container a price and set `"goal": "cheapest"`.

**Does it check the weight balance?** Yes. It reports each container's center of gravity and warns when it is off.

**Which files do I get?** A 3D drawing of every container (PNG and SVG), a 3D viewer page you can turn and step through, and a PDF with views and the loading order.

### Input

| Field | What it's for |
|---|---|
| `cargo` | The cargo: text lines or objects with `name`, `length`, `width`, `height`, `unit`, `weight`, `quantity`, `rotation`, `stackable`, `maxLoad`, `floorOnly`, `stop`, `priority`, `group`, `color`. |
| `containers` | What to load into: names or text lines, or objects with `name`, `length`, `width`, `height` (inside), `unit`, `maxWeight`, `price`, `quantity`, `maxHeight`. Empty: 40ft high cube containers. |
| `cargoCsv`, `containersCsv` | The same as CSV or TSV text, or a link to a CSV, Excel or JSON file or a Google Sheet. |
| `units` | `auto` (default), `cm`, `mm`, `m`, `in`, `ft`, `metric` or `imperial`: the unit of bare numbers. |
| `weightUnit` | `auto` (default: kg, or lb with imperial sizes), `kg`, `lb` or `t`. |
| `rotation` | For cargo that doesn't say: `upright` (default), `any` or `fixed`. |
| `minSupport` | The share of each piece's base that must rest on something, in percent (default 75). |
| `goal` | `fewest` containers (default) or `cheapest`. |
| `currency`, `title` | The currency of prices (default the one written with them, or $) and the plan's name. |
| `timeLimitSeconds` | How long to try other loading orders (default 10, up to 300). |
| `jobs` | Several loads in one run. |

### Output

One dataset item per load (shortened here):

```json
{
    "job": 1,
    "title": "Mixed export load",
    "status": "complete",
    "summary": "294 of 294 pieces loaded into 1 × 20ft container, 75.5% of the space used and 18.9% of the payload; 5,320 kg, 25.06 m³. That's the fewest possible.",
    "units": {"length": "cm", "weight": "kg", "volume": "m³"},
    "containersUsed": 1,
    "piecesTotal": 294,
    "piecesLoaded": 294,
    "piecesNotLoaded": 0,
    "totalWeight": 5320,
    "totalVolume": 25.06,
    "cost": null,
    "optimal": true,
    "fewestContainersPossible": 1,
    "containers": [
        {
            "number": 1, "name": "20ft container", "type": "container",
            "insideSize": {"length": 589.8, "width": 235.2, "height": 239.3},
            "pieces": 294, "weight": 5320, "maxWeight": 28180, "weightPercent": 18.9,
            "volume": 25.06, "volumePercent": 75.5, "floorPercent": 94.3,
            "loadLength": 580, "loadHeight": 210, "lengthLeft": 9.8,
            "centerOfGravity": {"x": 245, "y": 104.2, "z": 90.9, "fromCenterLengthPercent": -8.5, "fromCenterWidthPercent": -5.7},
            "items": [{"name": "Carton", "count": 120}, {"name": "Small carton", "count": 160}, {"name": "Crate", "count": 6}, {"name": "Drum", "count": 8}],
            "piecesBlockedForUnloading": 0
        }
    ],
    "placements": [
        {"container": 1, "step": 1, "item": 4, "name": "Drum", "x": 0, "y": 0, "z": 0, "length": 60, "width": 60, "height": 90, "orientation": "as listed", "weight": 200, "stop": null, "group": null}
    ],
    "items": [{"item": 1, "name": "Carton", "size": {"length": 60, "width": 40, "height": 40}, "quantity": 120, "loaded": 120, "notLoaded": 0, "weightEach": 14, "rotation": "upright", "stackable": true}],
    "unplaced": [],
    "solver": {"seconds": 1.8, "plansTried": 9, "bestRule": "longest side first (shuffled), floor building"},
    "report": "# Mixed export load\n\n294 of 294 pieces loaded ...",
    "imageUrl": "https://api.apify.com/v2/key-value-stores/.../records/load-plan-001-Mixed-export-load.png",
    "viewerUrl": "https://api.apify.com/v2/key-value-stores/.../records/load-plan-001-Mixed-export-load-3d.html",
    "pdfUrl": "https://api.apify.com/v2/key-value-stores/.../records/load-plan-001-Mixed-export-load.pdf",
    "csvUrl": "https://api.apify.com/v2/key-value-stores/.../records/load-plan-001-Mixed-export-load.csv",
    "svgUrl": "https://api.apify.com/v2/key-value-stores/.../records/load-plan-001-Mixed-export-load.svg",
    "markdownUrl": "https://api.apify.com/v2/key-value-stores/.../records/load-plan-001-Mixed-export-load.md",
    "jsonUrl": "https://api.apify.com/v2/key-value-stores/.../records/load-plan-001-Mixed-export-load.json",
    "warnings": [],
    "notes": []
}
```

Positions are measured from the front wall (`x`, toward the doors), the left wall (`y`, looking from the doors) and the floor (`z`), or from the pallet's corner and deck. Steps are in loading order. A load where some pieces didn't fit is `incomplete`, and `unplaced` says why, such as "too big for every container in any allowed orientation", "heavier than every container's weight limit" or "no room left in the containers given".

`report` is the whole plan as Markdown, ready for a chat reply or an email. The run's `OUTPUT` record holds every plan as one JSON object, so the `run-sync` API endpoint answers with them directly.

### How it works

Sizes become whole millimeters, rounding cargo up and containers down, so every check is exact. Pieces are placed one at a time on extreme points, the corners that earlier pieces leave, choosing for each piece the deepest, lowest, leftmost spot and the best way to turn it, with every candidate checked against all placed pieces at once with NumPy. A spot counts only when the piece is supported, nothing below it is a no-stack piece or would carry more than its limit (weight is passed down through the stack by contact area), the container's payload holds, floor-only pieces are on the floor, and nothing for a later stop stands between the piece and the doors. Pieces of one cargo line stay together, so identical cartons build up in walls from the front.

Many plans are tried within the time limit: different orders of the cargo lines (by volume, footprint, height, weight, length), with no-stack pieces first or last, and three ways of building (walls, compact fronts or layers), then shuffled orders. The best plan loads the most must-ship and other cargo, then uses the fewest containers (or the lowest cost), then the shortest load. With several container types, each next container is chosen to take the remaining cargo at the best fill (or volume per price), and the last one is the cheapest or smallest that takes everything left. Volume and weight lower bounds tell when the number of containers can't go lower.

### Checks

Every plan is checked again by separate code before it's returned: each piece inside the container, at its own size in an allowed orientation, overlapping nothing, resting on enough support, floor-only pieces on the floor, and the payload kept. The tests cover exact fits (64 cubes in a 2 m box), 34 EUR pallets in a 13.6 m trailer, weight-limited trucks, no-stack, top-load and floor-only rules, stops loaded in order, and random mixed loads in 20ft and 40ft containers. 16 tests pass, and a load of 2,300 mixed cartons fills three 40ft high cubes (the fewest possible) in about 4 seconds.

### Pricing

You pay per **load plan made**, at the price shown on this page, with every file included. Loads where nothing fits are never charged. Searching past the first 30 seconds of a plan adds a small **solver time** charge per started 10 seconds, so with the default 10-second time limit it never applies. If you set a maximum cost per run, the Actor checks before each plan that it fits and shortens the time limit to stay within it.

### Speed

Most loads (up to a few hundred pieces) are planned in one to three seconds, and a run through the API answers in about 10 to 20 seconds, including the cloud container starting and the files being saved. Thousands of pieces take a few seconds per plan tried, so the time limit decides how many orders are compared; give them 30 to 60 seconds to search further.

### Tips and limits

- **Upright by default**: cargo turns on the floor only unless a line says `any side` or **How cargo may turn** is set to any side. Use `fixed` for pieces that must keep their length along the container.
- **Support**: 75% of each piece's base must rest on something by default. Use 100 for no overhang at all, or less for sturdy cartons.
- **Heavy and fragile pieces**: `floor only` keeps heavy pieces on the floor, `no stack` keeps everything off fragile ones, and `max load 50 kg` lets light cargo go on top up to that weight.
- **Doors and real-world loading**: positions assume cargo can be placed anywhere it fits; pieces taller or wider than a container's door are not checked against the door, and nothing is secured or braced. Treat the plan as a guide for the loading crew.
- **Axle weights** are not computed; the center of gravity and the warnings show when a load is off-center.
- **Limits**: 500 cargo lines and 5,000 pieces per load (load whole pallets as single pieces for bigger shipments), 30 container types, 50 loads per run, and up to 300 seconds of searching per plan. A run stops once loads that can't be planned have taken 3 minutes.
- **Links**: CSV, TSV, JSON and Excel files, GitHub file links and Google Sheets links (shared as "Anyone with the link" or published to the web). Every site's robots.txt is checked first.

### For AI agents and developers

This Actor works with the [Apify MCP server](https://mcp.apify.com), so AI agents can call it as a tool: send the cargo and containers as text lines and read `summary`, `containers`, `unplaced` and the `viewerUrl` and `pdfUrl` links from the result. A direct API call that waits for the answer:

```bash
curl -X POST "https://api.apify.com/v2/acts/jumblemog~load-planner/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"cargo": ["Carton: 60 x 40 x 40 cm, 14 kg, qty 400"], "containers": ["20ft", "40ft HC"]}'
```

`run-sync` instead returns the run's `OUTPUT`: the status, how many plans were made and every plan as JSON.

In Python, with the `apify-client` package:

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("jumblemog/load-planner").call(run_input={
    "cargo": [
        {"name": "Carton", "length": 60, "width": 40, "height": 40, "unit": "cm", "weight": 14, "quantity": 400},
        {"name": "Crate", "length": 120, "width": 80, "height": 90, "unit": "cm", "weight": 180, "quantity": 6, "stackable": False},
    ],
    "containers": ["40ft HC"],
})
for plan in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(plan["summary"], plan["viewerUrl"])
    for c in plan["containers"]:
        print(c["number"], c["name"], c["pieces"], c["volumePercent"], c["weightPercent"])
```

### Credits

- Packing: [NumPy](https://numpy.org) (BSD-3-Clause).
- Files: [vl-convert](https://github.com/vega/vl-convert) (BSD-3-Clause) for the PNG and PDF pages, [pypdf](https://github.com/py-pdf/pypdf) (BSD-3-Clause) to join them, the Inter font (SIL Open Font License), subset with [fontTools](https://github.com/fonttools/fonttools) (MIT), and [three.js](https://threejs.org) (MIT) in the 3D viewer.

# Actor input Schema

## `cargo` (type: `array`):

One cargo line per entry, as text or an object. Text: "Carton: 60 x 40 x 40 cm, 14 kg, qty 120, upright": the name, length x width x height, the weight of one piece, the quantity, then any of: upright / any side / fixed (how it may turn), no stack (nothing on top), max load 100 kg (most weight on top), stack 3 high, floor only, stop 2 (delivery stop; stop 1 is unloaded first), priority or optional, group: PO-123, color: #2a78d6. Loaded pallets: "EUR pallet, 150 cm tall, 450 kg, qty 20". Objects: {"name": "Carton", "length": 60, "width": 40, "height": 40, "unit": "cm", "weight": 14, "quantity": 120, "rotation": "upright", "stackable": true, "maxLoad": 100, "floorOnly": false, "stop": 1}.

## `containers` (type: `array`):

What to load into, as text or objects. Named sizes: 20ft, 40ft, 40ft HC, 45ft HC, 20ft reefer, 40ft HC reefer, 53ft trailer, 48ft trailer, 13.6 m trailer, mega trailer, 26ft box truck, 16ft box truck, 7.5 t truck, EUR pallet, US pallet (48 x 40), UK pallet, half EUR pallet. Add a price, a quantity or limits: "40ft HC, $3,200", "20ft x 2", "EUR pallet, max height 180 cm, max 800 kg". Your own: "Van: 330 x 170 x 180 cm, max 1,200 kg" (inside sizes). Objects: {"name": "Truck", "length": 600, "width": 245, "height": 250, "unit": "cm", "maxWeight": 5000, "price": 800, "quantity": 2}. With several types, the plan picks the mix. Empty: 40ft high cube containers.

## `cargoCsv` (type: `string`):

Cargo as CSV or TSV text with a header row such as name,length,width,height,quantity,weight,stackable,rotation,stop, or a link to a CSV, Excel or JSON file or a Google Sheet (shared to anyone with the link). Without a header the columns are name, length, width, height, quantity, weight. Added to Cargo to load.

## `containersCsv` (type: `string`):

Containers as CSV text with a header row such as name,length,width,height,maxWeight,price,quantity, or a link to a file or Google Sheet. Added to Containers.

## `units` (type: `string`):

The unit of sizes written without one. Results come in centimeters and kilograms, or inches and pounds when the sizes are imperial. Automatic uses the unit written with the sizes, or guesses and says so in the notes.

## `weightUnit` (type: `string`):

The unit of weights written without one.

## `rotation` (type: `string`):

For cargo lines that don't say. Upright keeps every piece's height up and lets it turn a quarter turn; any side tries all six ways; fixed keeps length along the container's length.

## `minSupport` (type: `integer`):

How much of a piece's base must rest on the floor or on pieces below it. 100 means fully supported; lower values allow overhangs.

## `goal` (type: `string`):

What the plan makes as small as it can when several container types are given.

## `currency` (type: `string`):

The currency of container prices, such as $, € or EUR. Empty: the one written with the prices, or $.

## `title` (type: `string`):

A title for the plan and its files, such as Order 4512 to Rotterdam.

## `timeLimitSeconds` (type: `integer`):

How long to try other loading orders for a better plan. Most loads are done in a few seconds; large mixed loads (thousands of pieces) gain from 30 to 60 seconds. Searching past 30 seconds is billed per started 10 seconds.

## `jobs` (type: `array`):

Plan several loads in one run: a list of objects with their own cargo, containers or settings (any field above). Fields not given come from above: \[{"title": "Order 1", "cargo": \[...]}, {"title": "Order 2", "cargo": \[...], "containers": \["53ft trailer"]}].

## Actor input object example

```json
{
  "cargo": [
    "Carton: 60 x 40 x 40 cm, 14 kg, qty 120",
    "Small carton: 40 x 30 x 30 cm, 6 kg, qty 160",
    "Crate: 120 x 80 x 90 cm, 180 kg, qty 6, no stack",
    "Drum: 60 x 60 x 90 cm, 200 kg, qty 8, floor only"
  ],
  "containers": [
    "20ft container"
  ],
  "units": "auto",
  "weightUnit": "auto",
  "rotation": "upright",
  "minSupport": 75,
  "goal": "fewest",
  "timeLimitSeconds": 10
}
```

# Actor output Schema

## `results` (type: `string`):

One row per plan: the drawing, the summary and the files.

## `containers` (type: `string`):

Each loaded container with its pieces, weight, space used and center of gravity.

## `placements` (type: `string`):

Every piece's position, in loading order.

## `unplaced` (type: `string`):

Cargo that couldn't be loaded, with the reason.

## `output` (type: `string`):

The OUTPUT record: every plan in one JSON object.

## `files` (type: `string`):

The drawings, 3D viewers, PDFs, CSV, Markdown and JSON files in the run's key-value store.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "cargo": [
        "Carton: 60 x 40 x 40 cm, 14 kg, qty 120",
        "Small carton: 40 x 30 x 30 cm, 6 kg, qty 160",
        "Crate: 120 x 80 x 90 cm, 180 kg, qty 6, no stack",
        "Drum: 60 x 60 x 90 cm, 200 kg, qty 8, floor only"
    ],
    "containers": [
        "20ft container"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("jumblemog/load-planner").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "cargo": [
        "Carton: 60 x 40 x 40 cm, 14 kg, qty 120",
        "Small carton: 40 x 30 x 30 cm, 6 kg, qty 160",
        "Crate: 120 x 80 x 90 cm, 180 kg, qty 6, no stack",
        "Drum: 60 x 60 x 90 cm, 200 kg, qty 8, floor only",
    ],
    "containers": ["20ft container"],
}

# Run the Actor and wait for it to finish
run = client.actor("jumblemog/load-planner").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "cargo": [
    "Carton: 60 x 40 x 40 cm, 14 kg, qty 120",
    "Small carton: 40 x 30 x 30 cm, 6 kg, qty 160",
    "Crate: 120 x 80 x 90 cm, 180 kg, qty 6, no stack",
    "Drum: 60 x 60 x 90 cm, 200 kg, qty 8, floor only"
  ],
  "containers": [
    "20ft container"
  ]
}' |
apify call jumblemog/load-planner --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jumblemog/load-planner"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/8cNXxxmeXzpgEjsZZ/builds/DuF9mK928BUBalgnk/openapi.json
