API

One endpoint. Describe the shipment in plain English, get back the containers it fits in, optional item placements and a link to the 3D result.

Quick start

  1. Get an API key

    Sign up, pick a plan, and the key arrives by email.

    Sign up

  2. Or try it with the demo credentials

    These work without an account, against the live solver. The demo returns the load summary and the link to the 3D plan; item coordinates need a key of your own.

    "apiKey": "test",
    "username": "test"
  3. Make the call

    curl -X POST 'https://3dpack.ing/api/ai/calculate' \
      -H 'Content-Type: application/json' \
      -d '{
        "prompt": "Pack 500 boxes of 100x100x100 mm into a 1m x 1m x 1m container",
        "apiKey": "test",
        "username": "test"
      }'

    Response

    {
      "containers": [{
        "containerDims": { "length": 1000, "width": 1000, "height": 1000 },
        "totalQuantity": 500,
        "itemQuantities": { "Boxes": 500 },
        "volumeUsage": 50.0,
        "weightUsage": 0.0
      }],
      "unpackedItems": { "total": 0, "breakdown": {} },
      "linkToResult": "https://3dpack.ing/app?g=example-guid"
    }

    500 items packed, half the container used, and a link you can open.

The endpoint

POST/api/ai/calculate

Send a description of the shipment and your credentials as JSON. The description is parsed into items, constraints and container preferences, packed by the solver, and returned with a shareable link to the 3D view.

Request body

Field Type Required Notes
prompt string Yes What you are shipping, in plain English. Truncated at 4,000 characters.
apiKey string Yes Your key, or the string “test” for the shared demo — which answers everything except item coordinates. May also be sent as an X-API-Key header.
username string Yes The account the key belongs to. Required whenever a key is sent.
speed string No One of fast, normal or thorough. Defaults to the solver's own choice.
stability integer No How much of a box must rest on what is underneath it, 75 to 100. Omit for 75, the standard rule. Raise it for cargo that must not overhang — 100 means every stacked box sits fully supported, which is steadier and fits fewer.

Prompts it understands

  • Pack 50 boxes of 60x40x30 cm into a 20ft container
  • Load 100 fragile items (80x60x40cm, max stack 3) into a 40ft high cube
  • Ship mixed pallets: 10x euro pallets, 15x US pallets using the best container mix
  • Ship 24 pcs 200.3x120.2x100.2 cm (non-tiltable) using an optimal mix of 40ft and 20ft containers

Response

containers is one entry per container used. Each entry includes utilisation, the five largest overlapping free-space pockets and, when requested, every item placement. unpackedItems reports anything that did not fit. linkToResult opens the same plan in the 3D viewer.

{
  "containers": [
    {
      "containerDims": { "length": 1203.2, "width": 235.0, "height": 269.24 },
      "totalQuantity": 20,
      "itemQuantities": { "Pallets": 20 },
      "volumeUsage": 63.4,
      "weightUsage": 41.8
    },
    {
      "containerDims": { "length": 589.28, "width": 235.0, "height": 239.0 },
      "totalQuantity": 4,
      "itemQuantities": { "Pallets": 4 },
      "volumeUsage": 29.2,
      "weightUsage": 18.3
    }
  ],
  "unpackedItems": { "total": 0, "breakdown": {} },
  "linkToResult": "https://3dpack.ing/app?g=56455ed9-5339-4f46-8c41-cb7462f7aaa1"
}

Item coordinates

Add the coordinates query parameter when your system needs the placement of every packed item. Each container then includes an items array with the item name, its packed dimensions and the x, y and z coordinates of its corner, in the request's unit.

curl -X POST 'https://3dpack.ing/api/ai/calculate?coordinates=true' \
  -H 'Content-Type: application/json' \
  -d '{
    "prompt": "Pack 2 boxes of 60x40x30 cm into a 1m cube",
    "apiKey": "YOUR_KEY",
    "username": "you@company.com"
  }'
// Each packed container also includes:
"items": [{
  "name": "Boxes",
  "length": 60.0, "width": 40.0, "height": 30.0,
  "x": 0.0, "y": 0.0, "z": 0.0
}]

Coordinates are part of a paid API plan; the shared demo account answers 402 when they are requested. The parameter is opt-in to keep summary responses small. The packed dimensions show the chosen orientation. Source-system item IDs and an explicit loading sequence are not part of the public contract today.

Typed rows: the solver API

For systems that already hold the shipment as rows. Send candidate containers and items as JSON and get every placement back — the solver's own contract, with no language model in between. Part of the API plans and Business; the plain-English endpoint above is the one with the free demo.

POST/api/v1/calculate

Headers

Field Type Required Notes
X-API-Key header Yes The key from your API plan or Business subscription.
X-API-Username header Yes The account the key belongs to. A key without it is a 400.
Content-Type header Yes application/json

Request body

Whole numbers in any unit, used consistently — millimetres and grams are the usual choice; each dimension may be 1 to 19,999. Candidates are the containers or trucks the solver may choose from; items carry a quantity, an optional weight, and the stacking rules the planner exposes. Every field is in the OpenAPI specification.

curl -X POST 'https://3dpack.ing/api/v1/calculate' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: YOUR_KEY' \
  -H 'X-API-Username: you@company.com' \
  -d '{
    "Candidates": [
      { "Length": 12030, "Width": 2350, "Height": 2390, "MaxWeight": 26700000 }
    ],
    "Items": [
      { "TagOrColor": "pallets", "Length": 1200, "Width": 800, "Height": 1400,
        "Weight": 450000, "Quantity": 24 },
      { "TagOrColor": "crates", "Length": 1000, "Width": 1000, "Height": 900,
        "Weight": 300000, "Quantity": 6, "KeepTop": true, "NoTop": true }
    ],
    "Speed": "Fast"
  }'

Response

The container the solver chose, every placed piece with its packed dimensions and the position of its corner in your units, the pieces that did not fit with their quantities, and the empty spaces left over.

{
  "SelectedContainer": { "Length": 12030, "Width": 2350, "Height": 2390, "MaxWeight": 26700000 },
  "ItemsPut": [
    { "Item": { "TagOrColor": "pallets", "Length": 1200, "Width": 800, "Height": 1400,
                "Quantity": 1, "ContainerIndex": 0 },
      "Coord": { "X": 0, "Y": 0, "Z": 0 } },
    { "Item": { "TagOrColor": "pallets", "Length": 1200, "Width": 800, "Height": 1400,
                "Quantity": 1, "ContainerIndex": 0 },
      "Coord": { "X": 0, "Y": 800, "Z": 0 } }
  ],
  "ItemsNotPut": [],
  "EmptyContainers": [
    { "Coord": { "X": 0, "Y": 0, "Z": 1400 }, "Length": 12030, "Width": 2350, "Height": 990,
      "ContainerIndex": 0 }
  ]
}

Which box: cartonization

POST/api/v1/best-boxes

The order-to-box question. Send one item and up to twenty candidate boxes, and get back, for each box, how many of the item it takes, the placements, and the volume left over — the shape an order system needs to pick a carton.

curl -X POST 'https://3dpack.ing/api/v1/best-boxes' \
  -H 'Content-Type: application/json' \
  -H 'X-API-Key: YOUR_KEY' \
  -H 'X-API-Username: you@company.com' \
  -d '{
    "Candidates": [
      { "Length": 400, "Width": 300, "Height": 300 },
      { "Length": 600, "Width": 400, "Height": 400 }
    ],
    "Item": { "Length": 180, "Width": 120, "Height": 90 }
  }'
// Answers one entry per candidate:
[{ "ContainerDimensions": "400x300x300", "Quantity": 16, "RemainingVolume": 4896000, "ItemsPut": [ ... ] }, ...]

Both endpoints forward your rows to the solver unchanged and return its answer unchanged, so the specification is the contract. They are available on the API plans and Business — the plans that include several containers, stacking capacities and no ceiling on load size. A pack, Pro or free key is answered 402.

Switching from 3dbinpacking.com

Already calling 3dbinpacking.com's packIntoMany? Point it here. The same request — bins, items, params — goes to the same path on our host, and the same response comes back, so the switch is the base URL and the credentials: your 3DPACK.ING account email as username, and a 3DPACK.ING API key as api_key. POST works, and so does the GET with a JSON body that their PHP client sends.

POSThttps://3dpack.ing/packer/packIntoMany

curl -X POST 'https://3dpack.ing/packer/packIntoMany' \
  -H 'Content-Type: application/json' \
  -d '{
    "username": "you@example.com",
    "api_key": "YOUR_KEY",
    "bins": [
      { "id": "Box M", "w": 40, "h": 30, "d": 60, "max_wg": 20 },
      { "id": "Box L", "w": 60, "h": 40, "d": 80, "max_wg": 30, "q": 2 }
    ],
    "items": [
      { "id": "Mug", "w": 12, "h": 10, "d": 12, "wg": 0.4, "q": 20, "vr": 0 },
      { "id": "Book", "w": 15, "h": 4, "d": 23, "wg": 0.6, "q": 12, "vr": 1 }
    ],
    "params": { "item_coordinates": 1 }
  }'

The same: w, h, d, wg and q on items and bins; max_wg; a bin's q, which caps how many of it are used; vr, where an item without it stays upright; item_coordinates; and the response — status, errors, bins_packed with bin_data and every item's coordinates, and not_packed_items — with your ids as you sent them, in your units. Different: no images are drawn, so image_complete is empty, and plan_url in the response opens the interactive 3D plan instead; group, separate, limit_per_bin, cost and optimization_mode are accepted but not applied — bins are chosen to use as few as possible, and the response's notes say so. Each call is one pack from your plan or credit, as on the plain-English endpoint, and a call that produces no plan is not charged.

Errors

Failures come back as JSON with a single error field, and the status code says which kind it is.

Status Meaning
400 A key was sent without a username. Send both, or neither.
401 No API key in the body or the X-API-Key header.
402 Not an error. The request is valid and asks for something the plan does not cover — multi-container optimisation, or a shipment above the free-tier size. Relay the offer rather than reporting a failure.
403 The key was rejected, or the account it belongs to has no credit left. The message says which.
500 The request reached the solver and something went wrong there. The message says what.
502 The solver did not answer in time. Nothing was charged for a request the solver never received; try again in a moment.
{ "error": "When using API key, 'username' is also required in the request." }

OpenAPI specification

Examples

The same call, in four languages.

Language
const response = await fetch('https://3dpack.ing/api/ai/calculate', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    prompt: 'Pack 50 boxes of 60x40x30 cm into a 20ft container',
    apiKey: 'YOUR_API_KEY',
    username: 'YOUR_USERNAME'
  })
});

const result = await response.json();
console.log(`3D visualisation: ${result.linkToResult}`);
console.log(`Containers used: ${result.containers.length}`);
import requests

response = requests.post(
    'https://3dpack.ing/api/ai/calculate',
    json={
        'prompt': 'Pack 50 boxes of 60x40x30 cm into a 20ft container',
        'apiKey': 'YOUR_API_KEY',
        'username': 'YOUR_USERNAME',
    },
)
response.raise_for_status()

result = response.json()
print(f"3D visualisation: {result['linkToResult']}")
print(f"Containers used: {len(result['containers'])}")
using var client = new HttpClient();

var request = new
{
    prompt = "Pack 50 boxes of 60x40x30 cm into a 20ft container",
    apiKey = "YOUR_API_KEY",
    username = "YOUR_USERNAME"
};

var response = await client.PostAsJsonAsync(
    "https://3dpack.ing/api/ai/calculate", request);
response.EnsureSuccessStatusCode();

var result = await response.Content.ReadFromJsonAsync<JsonElement>();
Console.WriteLine(result.GetProperty("linkToResult").GetString());
OkHttpClient client = new OkHttpClient();

String json = """
    {"prompt": "Pack 50 boxes of 60x40x30 cm into a 20ft container",
     "apiKey": "YOUR_API_KEY",
     "username": "YOUR_USERNAME"}
    """;

Request request = new Request.Builder()
    .url("https://3dpack.ing/api/ai/calculate")
    .post(RequestBody.create(json, MediaType.parse("application/json")))
    .build();

try (Response response = client.newCall(request).execute()) {
    System.out.println(response.body().string());
}

MCP server

The same solver, as a tool an AI assistant can call for itself. Describe a shipment in plain English and it answers with the containers the cargo fits in, how full each one is, what did not fit, and a link to the interactive 3D plan. Asked whether 500 cartons fit in a 40-foot, an assistant without it does arithmetic on volumes — which ignores stacking, orientation and weight limits, and overstates what fits by a wide margin on real cargo.

Hosted endpoint

Nothing to install. Add this address to your AI assistant and sign in with your 3DPACK.ING account when it asks. A free account works, on its free monthly packs; a plan or credit goes further. Prefer an API key? That works too, below.

https://3dpack.ing/mcp

Claude (custom connector)

Settings → Connectors → Add custom connector. Paste this address and choose sign-in: Claude opens the 3DPACK.ING login, you approve, and it is connected, with the 3D plan drawn under each answer.

https://3dpack.ing/mcp

With an API key instead: choose No sign-in, add ?username=<your account email> to the address and send the key in the x-api-key request header. If your organisation has no Request headers section yet, put both in the address:

https://3dpack.ing/mcp?apiKey=YOUR_KEY&username=you@example.com
Claude answering through the 3DPACK.ING connector: 22 euro pallets drawn in 3D inside a 20 ft container, each face labelled with its size.
A real run in Claude, signed in as above. The answer comes with the load in 3D, labelled and turnable, and a link to the full plan.

Claude Code

claude mcp add --transport http 3dpacking https://3dpack.ing/mcp
# then run /mcp in Claude Code and choose Authenticate

ChatGPT (developer mode, on Plus, Pro, Business, Enterprise or Edu): add the same address with OAuth and sign in. Any other client that supports MCP sign-in works the same way; otherwise send an API key as X-API-Key or Authorization: Bearer with ?username= in the address, or pass apiKey and username both as query parameters.

Local install (npm)

Claude Code, one line:

claude mcp add 3dpacking -- npx -y @3dpacking/mcp-server

Claude Desktop

{
  "mcpServers": {
    "3dpacking": {
      "command": "npx",
      "args": ["-y", "@3dpacking/mcp-server"]
    }
  }
}

The same server as a local process. Without a key it runs on the shared demo account; for your own limits set THREEDPACKING_API_KEY and THREEDPACKING_USERNAME together — the API rejects a key without the username it belongs to. Published as @3dpacking/mcp-server on npm, and as ing.3dpack/container-loading in the MCP registry.

Behind your own sign-in

A connector with one key puts everyone on one account. If your users should connect through your own platform, each with their own login, the MCP layer can run under your domain and authentication and call this API with your company key; the packing still happens here. It is open source, and white-labelling is available.

Discuss an integration Source on GitHub

Pricing

Priced on calculations, not seats, because calculations are what your volume actually costs us and what your savings actually track. Every tier has the whole API — natural-language input, all six orientations, stacking and weight rules, centre of gravity. There is no feature held back for a higher tier.

Plan Per month Calculations included Beyond that
Starter $49 1,000 Talk to us Get a key
Growth $199 10,000 Talk to us Get a key
Scale $499 50,000 Talk to us Get a key
Enterprise Talk to us Agreed volume, on-premise if you need it Custom constraints, SLA Talk to us

Prices in USD, billed monthly; cancel whenever. Automatic per-call overage billing is not currently offered. If you expect to exceed the included monthly volume or need an evaluation allowance, contact us to agree capacity and pricing before increasing usage.

That is the whole API

One call in, a packed container out.