API Reference
Complete reference for the P4P Packing API. All endpoints accept and return JSON.
The API can be used without authentication for demonstration and educational purposes,
limited to 1 request per minute. Pack results are stored for 30 days, after which they are automatically deleted.
For unrestricted usage, register for an account
and include your API key via the X-Api-Key header. Authenticated requests are not rate-limited.
Prefer to start from working code? Clone the
sample client repository
— runnable C#, JavaScript, Python, Java, and PHP programs that call this API and print the result.
Runs the packing algorithm. Accepts a list of items and available container types,
returns the packed containers with item placements sorted in packing order.
Items are placed in the order they should be physically loaded into each container.
Request
Include X-Api-Key: your-key header to bypass rate limits. Without a key, requests are limited to 1 per minute. Register to get your API key.
| Field | Type | Description |
| items |
array of Item |
Items to pack, each with dimensions, quantity, and optional constraints. At least 1, maximum 100 distinct item types. Total quantity across all items must not exceed 10,000. Entries that share identical dimensions, weight, and constraints are recognized internally as one shape, so many same-sized SKUs do not slow packing down — the response still reports each entry separately. |
| containers |
array of Container |
Available container types the engine may use. The engine selects the best-fit containers automatically and may use multiple containers of the same or different types. At least 1, maximum 20. |
| singleSkuPerContainer |
boolean |
When true, each container will only hold items of a single SKU. Each distinct item entry in the request is treated as a separate SKU, even if two entries share the same name. A SKU whose quantity does not fit in one container spans multiple containers, each still holding only that SKU. When false (default), different item types can be mixed within the same container. |
| maxContainerWeight |
number |
Maximum total weight allowed per container, in the unit specified by weightUnit. The engine will not produce containers whose combined item weight exceeds this limit. 0 (default) means no weight limit is applied. |
| weightUnit |
string |
Unit of measure for all weight values in the request and response. "lb" (pounds, default) or "kg" (kilograms). |
| optimizeBy |
string |
Optimization strategy. "volume" (default) maximizes space utilization, packing items into fewer containers. "cost" minimizes total container cost, preferring cheaper containers even if they use space less efficiently. When using "cost", every container must have a cost greater than 0; the request is rejected otherwise. |
| heavyOnBottom |
boolean |
When true, heavier items are placed toward the container floor: stacking order is derived from each item's weight, with the heaviest item kept lowest. For items with a weight greater than 0 this replaces their crashability value; items without a weight keep their own crashability. When false (default), stacking order comes from crashability alone. |
Item Object
| Field | Type | Description |
| name |
string |
Display name for the item (e.g. "Electric Kettle"). Echoed back in the response for identification. |
| length |
number |
Item length, measured in the unit specified by unit. Accepts decimals (e.g. 12.5). |
| width |
number |
Item width, measured in the unit specified by unit. Accepts decimals. |
| height |
number |
Item height, measured in the unit specified by unit. Accepts decimals. When uprightOnly is true, this axis is kept vertical. |
| unit |
string |
Unit of measure for this item's dimensions. "in" (inches) or "cm" (centimeters). Defaults to "in". Note: "ft" is not supported for items. |
| quantity |
integer |
Number of individual units of this item to pack. Items with quantity ≤ 0 are ignored. |
| uprightOnly |
boolean |
When true, the item's height axis is always kept vertical. For example, a TV on a pallet must stay upright, but a box of socks can be rotated freely. When false (default), the engine may rotate the item into any orientation for a better fit. An upright-only item that would fit a container only when laid down counts as not fitting, and the request is rejected with 406. |
| crashability |
integer |
Stacking rank. Items with a lower rank must stay lower in the container: an item is only stacked on top of items whose crashability is less than or equal to its own. Give load-bearing items a low rank and fragile ones a high rank — for example, with Bricks at crashability 1 and Eggs at crashability 2, bricks are never placed on top of eggs. 0 (default) opts the item out of the constraint in both directions: a crashability-0 item may itself be stacked on top of anything — fragile ranked items included — and imposes no restriction on what goes above it. When you use ranks, assign one to every item that participates in stacking. Ignored for items with a weight greater than 0 when the request sets heavyOnBottom — weight then determines the stacking order. |
| weight |
number |
Weight of a single unit of this item, in the unit specified by the request's weightUnit. Used to compute totalWeight per container and enforce maxContainerWeight, and to rank stacking order when the request sets heavyOnBottom. 0 (default) means weight is not specified for this item. |
| color |
string |
Hex color string (e.g. "#ff6600"). Not used by the engine — echoed back in the response for visualization purposes. |
| payload |
string |
Arbitrary pass-through value. Not used by the engine — echoed back in the response so you can attach custom data to each item. |
Container Object
| Field | Type | Description |
| name |
string |
Display name for the container type (e.g. "24x18x18 Shipping Box"). Echoed back in the response for identification. |
| length |
number |
Internal length of the container (X axis), measured in the unit specified by unit. Accepts decimals. |
| width |
number |
Internal width of the container (Y axis), measured in the unit specified by unit. Accepts decimals. |
| height |
number |
Internal height of the container (Z axis), measured in the unit specified by unit. Accepts decimals. |
| unit |
string |
Unit of measure for this container's dimensions. "in" (inches), "cm" (centimeters), or "ft" (feet). Defaults to "in". The response echoes dimensions and item placements back in this same unit. |
| loadingMode |
string |
Determines loading direction and item sort order in the response. "topDown" (default) — loaded from the top, items sorted bottom-to-top. "frontLoad" — loaded from one end along the length axis. "sideLoad" — loaded from one end along the width axis. |
| payload |
string |
Arbitrary pass-through value. Not used by the engine — echoed back in the response so you can attach custom data to each container type. |
| cost |
number |
Cost per unit of this container type. Used when optimizeBy is set to "cost" — the engine will prefer cheaper containers to minimize total cost. Echoed back in the response on each packed container. 0 (default) means no cost is assigned, which is rejected when optimizing by cost. |
Response
| Field | Type | Description |
| id | string | Unique identifier for this pack result. Use this ID to retrieve the full result later via GET /api/pack/{id}. |
| createdAt | string | ISO 8601 timestamp (UTC) of when the packing was computed. |
| containers | array of Container | Packed containers with item placements, sorted in packing order. |
| unpackedItems | array of Item | Items that fit the containers but ran out of room — there was no space left for the remaining units. Only present when at least one unit was not placed. Each entry includes the remaining quantity that was not placed. An item that fits no container at all is not reported here; it is rejected with 406 instead. |
Container (response)
| Field | Type | Description |
| id | string | Unique identifier for this packed container instance. Use this ID to reference a specific container in other endpoints (e.g. GET /api/container/{id}/svg). |
| name | string | Echoed from the input container's name. |
| payload | string | Echoed from the input container's payload. |
| length | number | Internal length of this container instance (X axis), in the unit specified by unit. |
| width | number | Internal width of this container instance (Y axis), in the unit specified by unit. |
| height | number | Internal height of this container instance (Z axis), in the unit specified by unit. |
| unit | string | Unit of measure for all dimensions and coordinates in this container, including item placements. Same as the input container's unit. |
| loadingMode | string | Loading mode for this container. Same as the input container's loadingMode. Determines the sort order of items. |
| items | array of Packed Item | Item placements inside this container, sorted in physical packing order (the order you would load them). |
| weightUnit | string | Unit of measure for the totalWeight value. Same as the request's weightUnit. |
| totalWeight | number | Sum of all item weights in this container (each item's weight multiplied by its packed quantity). 0 when no items have a weight specified. |
| utilization | number | Space utilization percentage (0–100), calculated as packed volume divided by container volume, rounded to one decimal place. For example, 87.2 means 87.2% of the container's internal volume is occupied by items. |
| cost | number | The input container's cost, echoed back. When optimizeBy is "cost", the sum of all container costs represents the total packing cost. |
Packed Item (response)
Each packed item represents a grid of identical, identically-oriented units placed at a single origin point.
The total number of individual units in the grid is nx × ny × nz.
The grid occupies the volume from (x, y, z) to (x + dx*nx, y + dy*ny, z + dz*nz).
| Field | Type | Description |
| name | string | Echoed from the input item's name. Use this to identify which product type this placement belongs to. |
| color | string | Echoed from the input item's color. |
| payload | string | Echoed from the input item's payload. |
| x | number | Placement origin along the X (length) axis, in the container's unit. |
| y | number | Placement origin along the Y (width) axis, in the container's unit. |
| z | number | Placement origin along the Z (height) axis, in the container's unit. Z = 0 is the container floor. |
| dx | number | Oriented dimension of a single unit along the X axis. May differ from the input length/width/height due to rotation. In the container's unit. |
| dy | number | Oriented dimension of a single unit along the Y axis. In the container's unit. |
| dz | number | Oriented dimension of a single unit along the Z axis. In the container's unit. |
| nx | integer | Number of units stacked along the X axis. The grid spans from x to x + dx × nx. |
| ny | integer | Number of units stacked along the Y axis. The grid spans from y to y + dy × ny. |
| nz | integer | Number of units stacked along the Z axis. The grid spans from z to z + dz × nz. |
Error
| Status | Reason | Description |
| 400 | Bad Request | Invalid or missing request data. Returned when: dimensions are zero or negative, a dimension exceeds 100 ft (1,200 in), quantities are negative, the request body is not valid JSON, an item uses an unsupported unit (e.g. "ft" on items), or collection limits are exceeded (100 item types, 20 containers, 10,000 total items). Also returned when optimizeBy is "cost" and a container has no cost. |
| 406 | Not Acceptable | An item does not fit into any of the supplied container types in any orientation it is allowed to take, so the request can never be packed completely. The response body names the item. Note that an item with uprightOnly set to true triggers this when it would only fit lying down, even though a container is large enough for it rotated. Items that fit but simply ran out of room are not an error — they are returned in unpackedItems. |
| 408 | Request Timeout | The packing algorithm did not complete within 5 seconds. This can happen with very large or complex inputs. Try reducing the number of items or container types. |
| 429 | Too Many Requests | Rate limit exceeded (1 request per minute for unauthenticated calls). Register for an API key to remove rate limits. |
Retrieves a previously computed pack result, including the original request and
the packing response. The id is returned in the POST /api/pack response.
Request
| Parameter | Type | Description |
| id |
string |
The id from the POST /api/pack response. |
Response
| Field | Type | Description |
| id | string | The pack result identifier (same as the path parameter). |
| createdAt | string | ISO 8601 timestamp of when the pack was computed. |
| request | object | The original request body that was sent to POST /api/pack. |
| response | object | The packing response, identical to what POST /api/pack returned. |
Error
| Status | Reason | Description |
| 404 | Not Found | No pack result with the given id exists. |
Generates an animated isometric SVG visualization of a packed container.
Pass the container id from the POST /api/pack response
and receive a self-contained SVG image with a drop-in loading animation.
The SVG can be embedded in HTML, used as an <img> source, or saved as a file.
Request
| Parameter | Type | Description |
| id |
string |
The id of a packed container from the POST /api/pack response. |
Response
Returns image/svg+xml. The SVG is self-contained with embedded CSS animations —
no external dependencies. Items animate in one by one in packing order.
Item color values are used for rendering; items without a color are assigned one automatically.
Error
| Status | Reason | Description |
| 404 | Not Found | No container with the given id exists. Container IDs are generated by POST /api/pack and are only available after a packing request has been made. |