LoadOptimizer.ai Saved loads API
Keep the runs that matter under a name, within your plan's limit per problem type.
Keep the runs that matter under a name, within your plan's limit per problem type.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/loadoptimizer-ai:loadoptimizer-ai-saved-loads-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Load Optimizer Saved loads API
version: 2.0.0
summary: Asynchronous box-packing / load-optimization API.
description: 'Submitting work is **asynchronous**: every solve is a *job* you create, poll, then collect.
This is the documentation of the **Load Optimizer API v2**: it describes what `/v2` supports;
what it does not describe is not supported.
## Job lifecycle
`queued → running → succeeded | failed | cancelled` (last three terminal). Poll `GET /jobs/{id}` until terminal.
## Conventions
Values from a fixed set (enum values, in a request body or in list filters such as `status`, `saved` and
`sort`) are matched without regard to case, and come back in the spelling published here: `"MM"` reads
back as `"mm"`. Names and codes keep their case: group names, a SKU''s code (`externalId`, also as a
filter) and system-catalogue slugs are case-sensitive. The idempotency fingerprint compares the body as you
sent it: an enum value in another case makes a different body, so while the key is kept (see `Idempotency-Key`)
the same key gets the `409`.'
contact:
email: contact@loadoptimizer.ai
servers:
- url: https://api.loadoptimizer.ai/v2
description: Production environment of the Load Optimizer API.
x-internal: false
security:
- ApiKey: []
tags:
- name: Saved loads
description: Keep the runs that matter under a name, within your plan's limit per problem type.
paths:
/saved-loads:
post:
tags:
- Saved loads
operationId: saveLoad
summary: Save a run as a saved load
description: 'Keeps one of your succeeded orders as a saved load, under the name you give it. A saved load is the run itself, kept: it has the run''s id, and the run''s request (`/jobs/{id}/input`), result, instructions and share link stay where they are, under `/jobs/{id}/...`. The name is the run''s name, so saving sets it and `PUT /jobs/{id}/name` changes it for both. Your plan allows a number of saved loads per problem type. At that limit the save is refused with `422`. A valid `replace` names one of your saved loads of the same problem type, which is released in the same call whether or not you are at the limit; a refused save releases nothing. Saving a run that is already saved answers `409` and changes nothing, so a retry after a timeout tells you the first attempt landed.'
parameters:
- name: jobId
in: query
required: true
description: 'The id of the run to save: one of your orders that has succeeded. A value that is not a UUID in its 36-character form is rejected with `400` against `jobId`.'
schema:
type: string
format: uuid
maxLength: 36
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- name
properties:
name:
type: string
minLength: 1
maxLength: 256
description: The name to keep it under. Trimmed, and stored on the run.
replace:
type:
- string
- 'null'
format: uuid
maxLength: 36
description: The id of one of your saved loads of the same problem type, released in the same call whether or not you are at the limit; a refused save releases nothing. A value that is not a UUID in its 36-character form, or that names a saved load of another problem type, is rejected with `400` against `/replace`.
examples:
save:
summary: Save a run
value:
name: Rotterdam week 40
replace:
summary: Save it in place of an older one
value:
name: Rotterdam week 41
replace: 01975cbe-8d17-7a64-b2c9-4f06a1d3e5b8
responses:
'201':
description: Saved. The run is now a saved load.
headers:
Location:
description: URL of the saved load.
schema:
type: string
format: uri
content:
application/json:
schema:
$ref: '#/components/schemas/SavedLoad'
examples:
saved:
summary: The run, kept as a saved load
value:
id: 01975cbc-1f40-7d21-9b3e-5a7c2e8f0d19
name: Rotterdam week 40
problemType: LoadPlanning
createdAt: '2026-09-28T08:12:33.412+00:00'
savedAt: '2026-10-01T14:03:10.007+00:00'
'400':
$ref: '#/components/responses/ValidationProblem'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
description: 'The run is not one of your orders (`code: "job_not_found"`: unknown, another account''s, or a loading-instructions job), or `replace` names no saved load of yours (`code: "saved_load_not_found"`).'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
unknownJob:
$ref: '#/components/examples/JobNotFound'
unknownSavedLoad:
$ref: '#/components/examples/SavedLoadNotFound'
'409':
description: 'The run has not succeeded (`code: "job_not_succeeded"`), or it is already saved (`code: "saved_load_exists"`; nothing was changed).'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
alreadySaved:
summary: The run is already a saved load
value:
type: https://tools.ietf.org/html/rfc9110#section-15.5.10
title: This run is already saved.
status: 409
code: saved_load_exists
'413':
$ref: '#/components/responses/PayloadTooLarge'
'415':
$ref: '#/components/responses/UnsupportedMediaType'
'422':
description: 'Your plan''s saved-load limit for this problem type is reached (`code: "max_saved_loads"`); `detail` names the limit. Release a saved load, or name it in `replace`.'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
limitReached:
summary: Every place for this problem type is taken
value:
type: https://tools.ietf.org/html/rfc4918#section-11.2
title: Saved-load limit reached.
status: 422
code: max_saved_loads
detail: The account keeps at most 10 saved loads of this problem type.
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerProblem'
get:
tags:
- Saved loads
operationId: listSavedLoads
summary: List saved loads
description: Your saved loads, a page at a time, the most recently saved first unless you sort otherwise. Each item is the same `SavedLoad` that `GET /saved-loads/{id}` returns. Follow `nextCursor` until it is null, passing the same filters, `sort` and `direction` on every page. There is no total count. Every invalid parameter is reported at once, in one `400` with one `errors[]` entry per parameter, whose `pointer` is the parameter's name.
parameters:
- name: problemType
in: query
required: false
style: form
explode: true
description: Only saved loads of one of these problem types. Repeat the parameter for several.
schema:
type: array
items:
type: string
enum:
- LoadPlanning
- MixedPalletizing
- name: q
in: query
required: false
description: 'Only saved loads whose `name` contains this text, ignoring case. Matched literally: `%` and `_` are ordinary characters. Leading and trailing whitespace is ignored. At most 256 characters.'
schema:
type: string
maxLength: 256
- name: savedFrom
in: query
required: false
description: Only loads saved at or after this moment (inclusive). An RFC 3339 date-time with an offset; in a URL, send a `+` offset as `%2B`.
schema:
type: string
format: date-time
maxLength: 33
- name: savedTo
in: query
required: false
description: Only loads saved before this moment (exclusive). Same format as `savedFrom`, and not earlier than it.
schema:
type: string
format: date-time
maxLength: 33
- name: sort
in: query
required: false
description: 'The order of the list: when it was saved, when the run was submitted, its name, or its problem type. Within equal names or problem types the most recently saved comes first. Within equal times (`savedAt`, `createdAt`) the order follows the id of the run, in the direction you ask for. Under `sort=name`, a load whose name was cleared comes last in both directions.'
schema:
type: string
enum:
- savedAt
- createdAt
- name
- problemType
default: savedAt
- name: direction
in: query
required: false
description: 'The sort direction: `desc` by default for `savedAt` and `createdAt` (newest first), `asc` for `name` and `problemType`.'
schema:
type: string
enum:
- asc
- desc
- name: limit
in: query
required: false
description: Saved loads per page, 1 to 50. Default 50.
schema:
type: integer
format: int32
minimum: 1
maximum: 50
default: 50
- name: cursor
in: query
required: false
description: The `nextCursor` of the previous page, passed back unchanged with the same `sort` and `direction`. A value this API did not issue, one issued by the other list, or one issued for another sort or direction, is rejected with `400`. At most 4,096 characters.
schema:
type: string
maxLength: 4096
responses:
'200':
description: One page of saved loads.
content:
application/json:
schema:
$ref: '#/components/schemas/SavedLoadPage'
examples:
onePage:
summary: Everything fits on one page
value:
items:
- id: 01975cbc-1f40-7d21-9b3e-5a7c2e8f0d19
name: Rotterdam week 40
problemType: LoadPlanning
createdAt: '2026-09-28T08:12:33.412+00:00'
savedAt: '2026-10-01T14:03:10.007+00:00'
- id: 01975cbe-8d17-7a64-b2c9-4f06a1d3e5b8
name: Rotterdam week 39
problemType: LoadPlanning
createdAt: '2026-09-21T08:12:33.412+00:00'
savedAt: '2026-09-24T09:30:00.000+00:00'
nextCursor: null
'400':
$ref: '#/components/responses/ValidationProblem'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerProblem'
/saved-loads/{id}:
parameters:
- $ref: '#/components/parameters/SavedLoadId'
get:
tags:
- Saved loads
operationId: getSavedLoad
summary: Get a saved load
description: One of your saved loads, by its id, which is also its run's job id. An unknown id, a released load and another account's load all answer `404`.
responses:
'200':
description: The saved load. Its request (`/jobs/{id}/input`), result, instructions and share link are under `/jobs/{id}/...`, with the same id.
content:
application/json:
schema:
$ref: '#/components/schemas/SavedLoad'
examples:
saved:
summary: A saved load
value:
id: 01975cbc-1f40-7d21-9b3e-5a7c2e8f0d19
name: Rotterdam week 40
problemType: LoadPlanning
createdAt: '2026-09-28T08:12:33.412+00:00'
savedAt: '2026-10-01T14:03:10.007+00:00'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/SavedLoadNotFoundProblem'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerProblem'
delete:
tags:
- Saved loads
operationId: deleteSavedLoad
summary: Release a saved load
description: 'Stops keeping the run, so it no longer counts against your limit. The run itself is untouched: its result and name stay, and `GET /jobs?saved=false` lists it again. To undo, save it again. Never limited by your plan, so an account over its limit can always make room.'
responses:
'204':
description: Released. The run itself is untouched.
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/SavedLoadNotFoundProblem'
'429':
$ref: '#/components/responses/RateLimited'
'500':
$ref: '#/components/responses/ServerProblem'
components:
responses:
SavedLoadNotFoundProblem:
description: 'Unknown saved-load id, one that is not a UUID in its 36-character form, one already released, or one belonging to another account: all answer identically (no existence leak).'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
unknownSavedLoad:
$ref: '#/components/examples/SavedLoadNotFound'
PayloadTooLarge:
description: 'The request body is larger than the API will read (`code: "payload_too_large"`). Permanent for that body: split the order into smaller jobs rather than retrying it unchanged.'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
bodyTooLarge:
summary: A body past the size the API reads
value:
type: https://tools.ietf.org/html/rfc9110#section-15.5.14
title: Payload too large.
status: 413
code: payload_too_large
detail: The request body is larger than this API accepts. Split the order into smaller jobs.
ValidationProblem:
description: 'Validation failed: `code: "validation_failed"`, with `errors[]` naming each offending field, query parameter or header. Every 400 on `/v2` has this shape; see Errors & troubleshooting.'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
invalidGeometry:
summary: A cargo line with a geometry type the API does not implement
value:
type: https://tools.ietf.org/html/rfc9110#section-15.5.1
title: Validation failed.
status: 400
code: validation_failed
errors:
- pointer: /cargo/0/geometry/type
code: validation_failed
message: Only 'box' geometry is supported.
ServerProblem:
description: 'The request could not be completed for a reason the API did not anticipate. Transient as far as a caller can tell: retry, and if it persists contact support. The cause is recorded internally and is deliberately absent from the body, so there is nothing here to branch on beyond `code`.'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
internalServerError:
summary: An unanticipated failure while handling the request
value:
type: https://tools.ietf.org/html/rfc9110#section-15.6.1
title: Internal server error.
status: 500
code: internal_server_error
detail: The request could not be completed. Retry; if it keeps failing, contact support.
UnsupportedMediaType:
description: 'The request body was sent under a content type other than `application/json`, or without one (`code: "unsupported_media_type"`). These operations read JSON only.'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
wrongContentType:
summary: A body sent as something other than JSON
value:
type: https://tools.ietf.org/html/rfc9110#section-15.5.16
title: Unsupported media type.
status: 415
code: unsupported_media_type
detail: Send the request body as application/json.
Unauthorized:
description: Missing, invalid or revoked API key. Empty body.
Forbidden:
description: 'The API key is valid, but the account''s plan does not include customer-API access (`code: "customer_api_not_entitled"`). Permanent, not transient: retrying will not help; the account''s plan must add customer-API access first.'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
notEntitled:
$ref: '#/components/examples/NotEntitled'
RateLimited:
description: 'Too many requests, too fast (`code: "rate_limit_exceeded"`). The account may make **300 requests per minute** across `/v2`, replenished continuously, and every operation except `GET /health` counts. The request was not carried out: wait the number of seconds in the **`Retry-After`** header, then send it again. A full queue and a used-up monthly quota answer `429` on `POST /jobs` only.'
headers:
Retry-After:
description: Seconds to wait before retrying.
schema:
type: integer
format: int32
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
examples:
rateLimited:
summary: Too many requests too fast
value:
title: Too many requests.
status: 429
code: rate_limit_exceeded
detail: The account exceeded the request rate for the customer API. Retry after the number of seconds in the Retry-After header.
schemas:
ProblemCode:
type: string
description: Stable, machine-readable top-level error-code registry, by HTTP status.
enum:
- validation_failed
- ambiguous_source
- override_field_not_allowed
- customer_api_not_entitled
- mixed_palletizing_not_entitled
- load_planning_not_entitled
- link_sharing_not_entitled
- request_body_not_supported
- libraries_not_entitled
- library_read_only
- internal_server_error
- job_not_found
- result_not_ready
- artifact_not_ready
- saved_load_not_found
- library_not_found
- library_entry_not_found
- job_already_terminal
- job_not_succeeded
- saved_load_exists
- external_id_taken
- payload_too_large
- unsupported_media_type
- max_items_per_job
- max_carriers_per_job
- max_carrier_instances_per_job
- max_saved_loads
- max_library_entries
- too_many_active_jobs
- monthly_quota_exceeded
- rate_limit_exceeded
SavedLoadPage:
type: object
required:
- items
- nextCursor
description: One page of `GET /saved-loads`.
properties:
items:
type: array
items:
$ref: '#/components/schemas/SavedLoad'
nextCursor:
type:
- string
- 'null'
pattern: ^[A-Za-z0-9_-]+$
description: 'Pass it as `cursor` for the next page; null on the last page. Opaque: do not build or parse it.'
Problem:
type: object
description: 'RFC 9457 `problem+json`. `code` is stable & machine-readable when present; `errors[].pointer` locates
the offending field, query parameter or header. Two `/v2` errors have an **empty body** instead:
`401` and the idempotency `409`.
See [Errors & troubleshooting](errors.html).'
properties:
type:
type: string
format: uri
description: 'A reference for the response''s status code, not for the specific failure: two failures with different `code` values share one `type`. RFC 9457 permits `about:blank` here; this API never sends that, and the `429` responses carry no `type` at all. Switch on `code`, never on this field.'
title:
type: string
status:
type: integer
format: int32
detail:
type: string
description: Human-readable explanation, present on some errors and often naming the limit value that was exceeded. Never switch on it; switch on code.
code:
$ref: '#/components/schemas/ProblemCode'
errors:
type: array
description: Field-level problems. Only on a 400 `validation_failed`.
items:
type: object
properties:
pointer:
type: string
description: For a body field, a JSON Pointer (RFC 6901) into the request body, over the member names as this contract spells them (camelCase, also when you sent another case), e.g. /carriers/0/externalId. A name you chose yourself (a group name, an overrides member) appears as sent, with ~ written as ~0 and / as ~1. For a query parameter or a header, its name without a leading /, e.g. limit or Idempotency-Key. An empty string means the body as a whole.
code:
type: string
enum:
- validation_failed
- ambiguous_source
- library_entry_not_found
- override_field_not_allowed
description: 'validation_failed, unless a field code applies: ambiguous_source or library_entry_not_found on the libraryId of a job carrier or cargo line, or override_field_not_allowed on its overrides.'
message:
type: string
required:
- title
- status
SavedLoad:
type: object
required:
- id
- name
- problemType
- createdAt
- savedAt
description: A run you kept. Its id is the run's job id; the request (`/jobs/{id}/input`), result, instructions and share link stay under `/jobs/{id}/...`.
properties:
id:
type: string
format: uuid
description: 'The run''s job id: its request, result, instructions and share link are under `/jobs/{id}/...`.'
name:
type:
- string
- 'null'
maxLength: 256
description: The run's name. Set when saving; null only when it was cleared afterwards with `PUT /jobs/{id}/name`.
problemType:
type: string
enum:
- LoadPlanning
- MixedPalletizing
createdAt:
type: string
format: date-time
description: When the run was submitted (UTC).
savedAt:
type: string
format: date-time
description: When it was saved (UTC).
examples:
NotEntitled:
summary: The key is valid; the plan is not
value:
type: https://tools.ietf.org/html/rfc9110#section-15.5.4
title: The account's plan does not include the customer API.
status: 403
code: customer_api_not_entitled
detail: 'Upgrade the account to a plan that includes customer-API access: https://app.loadoptimizer.ai/home/billing'
SavedLoadNotFound:
summary: Unknown, released, or someone else's saved load
value:
type: https://tools.ietf.org/html/rfc9110#section-15.5.5
title: Saved load not found.
status: 404
code: saved_load_not_found
JobNotFound:
summary: Unknown id, or someone else's job
value:
type: https://tools.ietf.org/html/rfc9110#section-15.5.5
title: Job not found.
status: 404
code: job_not_found
parameters:
SavedLoadId:
name: id
in: path
required: true
description: The saved load's id, which is also its run's job id.
schema:
type: string
format: uuid
maxLength: 36
securitySchemes:
ApiKey:
type: apiKey
in: header
name: X-Api-Key
description: 'Customer API key bound to your account, e.g. `X-Api-Key: ck_live_...`. Create it in the web app (see https://app.loadoptimizer.ai/docs/api-keys); the full key is shown only once.'
x-provenance:
method: harvested
publisher: LoadOptimizer.ai
source: https://www.loadoptimizer.ai/docs/loadoptimizer-openapi.yaml
note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json);
stamped by stamp-openapi-provenance.py (roadmap#793)