Skip to content
Docs
Sign in

Nesting API

Send part outlines and a roll or sheets, get placements back: request, methods, response, sync and queued jobs, cut files and limits.

For integrators

The nesting used by imposition and estimating is also available on its own. Send part outlines and a roll or a stack of sheets, and get back where every copy goes. Nesting explains the settings and what a layout is checked for. All calls are team-scoped under /a/<team_slug>/nesting/api/v1/ and need an API key (see the API overview).

POST /a/{team_slug}/nesting/api/v1/nest/

Nest parts on a roll or on sheets.

nesting_nest · View in API reference

The request#

Every length uses the request's unit: pt (the default), in, mm, cm or ft (see Units and measurements). Outlines are closed rings of [x, y] points with y pointing up, without repeating the first point, starting anywhere.

curl -sS -H "Authorization: Api-Key $API_KEY" -H "Content-Type: application/json" \
  "https://app.example.com/a/acme/nesting/api/v1/nest/" -d '{
  "unit": "in",
  "container": {"type": "roll", "width": 54, "margins": {"left": 0.5, "right": 0.5},
                "lead_in": 6, "lead_out": 6, "segment_length": 120, "segment_policy": "within"},
  "parts": [
    {"id": "panel-a", "quantity": 2, "outline": [[0, 0], [30, 0], [30, 20], [0, 20]],
     "rotation": {"mode": "any", "step": 5, "max_angles": 8}, "cut_method": "thru_cut"},
    {"id": "label-3x2", "quantity": 10, "outline": [[0, 0], [3, 0], [3, 2], [0, 2]], "cut_method": "kiss_cut"}
  ],
  "options": {"spacing": 0.25, "bleed": 0.0625, "time_limit_s": 5, "layouts": 1},
  "include": ["outline", "cut", "transform"]
}'

container is a roll or sheets:

  • Roll: width, side margins (left, right), lead_in and lead_out (material before and after the parts), segment_length (the cutter's bed length) and segment_policy: span (the default) ignores beds, within keeps every part inside one bed. cost_per_length fills in the cost in the summary. max_length is reserved and must be null.
  • Sheets: width, height, four margins, max_count (the most sheets to use) and cost per sheet.

parts[]: id (unique, up to 100 characters), quantity (1 to 100,000), outline, rotation, bleed (overrides options.bleed), meta (any object, returned on every placement of the part) and cut_method (thru_cut or kiss_cut, used in cut files). For keeping parts together and ganging a part also takes split (inherit, the default, allow or never), properties, own_sheet and inks.

parts[].rotation:

Field Values
mode none (as drawn), flip (0° and 180°), quarter (the four quarter turns; the default), any (each part may turn) or list (the angles you give, up to 72).
step, max_angles For any. step is 0.5° to 90° (default 5°). max_angles is 1 to 72 (default 8).
grain along keeps the angles at 0° and 180°, across at 90° and 270°.
mirror Must be false: mirroring isn't supported yet.

options:

Option Meaning
spacing The gap between parts (default 0).
bleed Added around every outline before nesting (default 0).
panelling Rolls: split parts wider than the roll into strips, {"overlap": 0.5, "strategy": "fewest_seams"} or "least_material". null (the default) rejects oversize parts instead.
time_limit_s How long to search, from 0.1 s (default 2 s) up to the limit of the mode.
layouts How many layouts to return, best first (default 1).
anchor Sheets: the corner parts pack towards, lower_left (default), lower_right, upper_left or upper_right.
objective auto (default), min_containers, min_length or min_cost. Accepted for later use. Nesting currently aims to minimise roll length or sheet count regardless of this value.
seed Optional value for repeat runs. Identical inputs can still produce different layouts. Allow more time with time_limit_s to look for a better fit.
method free (true shape, the default), guillotine (N-up) or strips, with its rules in guillotine or strips (below).
split Whether a part's copies may be split across sheets or forms: {"mode": "allow", "min_saving_pct": 5} (see keeping parts together).
ganging Which parts may share a sheet, from their properties (see ganging rules). Leave it out, or null, for no rules.
stream_candidates Queued jobs: how many layouts found while the job runs are listed (0 to 20, default 10; 0 turns the list off). See candidates.

include picks the geometry returned with each placement: outline, cut and transform (all three by default). Position, size and angle are always returned.

Methods#

  • free: true-shape nesting of any outlines, on rolls or sheets.
  • guillotine: N-up for rectangles on sheets, laid out so a guillotine can cut them. Rules in options.guillotine: gutter_mode (shared: neighbours share a cut, the default; gutter: spacing between them), max_stages (knife stages, 2 to 4, default 4), cut_weight (0 to 1: how many sheets a knife stroke per lift is worth), layout_penalty (0 to 5: sheets per extra distinct form), caliper and max_lift (sheet thickness and knife lift height, which give the sheets per lift), and edge_trim (auto, always or never; never is accepted, and cut as auto).
  • strips: parts in straight strips between slit lines. Rules in options.strips: content (single: one part per strip, the default; mixed: rectangles only), direction (sheets: auto, x or y; rolls: auto, lanes down the roll or bands across it), gutter between strips (null uses spacing), rows per strip (1 to 20), max_width and max_strips (1 to 100).

Keeping parts together#

By default a part's copies are split across sheets (or N-up forms) whenever that saves sheets. options.split changes that for the whole request, and parts[].split for one part:

  • allow (the default): split when it saves sheets.
  • avoid: a split layout is preferred only when it saves more than min_saving_pct (0 to 50, default 5). Both kinds of layout are returned, and the best whole and the best split layout both stay within layouts.
  • never: every copy of a part is on one sheet design. True shape then plans repeated sheets, which takes longer: with more than 10 parts it needs a queued job (mode sync answers 400), and it takes at most 40 parts. Rolls and sheets with max_count 1 don't plan repeated sheets, so they stay synchronous.

parts[].split is inherit (the request's rule), allow or never. Rolls print one continuous layout, so the rule doesn't apply there: the response warns instead.

Ganging rules#

Give parts properties (key → text, number or null; at most 32 per part, keys in lowercase letters, digits and _, due as YYYY-MM-DD) and say in options.ganging which parts may share a sheet:

"options": {"ganging": {"match_on": ["coating"],
                        "keep_apart": [{"key": "customer", "values": ["Brand A", "Brand B"]}],
                        "due_within_days": 2,
                        "prefer_together": [{"key": "stock", "min_saving_pct": 5}]}}
  • match_on: parts share a sheet only when these properties are equal (text compares without case or extra spaces). A part without the property only shares with other parts without it. The group property is always matched.
  • keep_apart: parts holding two different listed values never share a sheet.
  • due_within_days: parts whose due dates are more than this many days apart never share a sheet (parts without a date are exempt).
  • prefer_together: a soft rule. Parts with different values of the key stay together only when mixing them saves more than min_saving_pct, like avoid above.
  • parts[].own_sheet (true): no other part shares this part's sheets.

A rule key must be a well-known property (stock, coating, finishing, customer, order, due, group) or appear on at least one part. Each group of parts that may share is nested on its own sheets and the results are joined; on a roll the groups follow each other as segments. A roll holds one stock, so matching on stock with two stocks on a roll is an error.

parts[].inks ({"front": [ink], "back": [ink] | null}, where an ink is {"key", "variant", "name", "kind", "role", "process_step"}) doesn't change a digital layout; each container lists the union of its parts' inks.

The response#

{
  "id": "00000000-0000-0000-0000-000000000000", "status": "ok", "unit": "in",
  "summary": {"containers": 1, "used_length": 54.2, "consumed_area": 2926.8, "part_area": 1200.0,
              "utilization": 0.41, "unplaced": [], "cost": null},
  "layouts": [{
    "rank": 1, "method": "free", "used_length": 54.2, "utilization": 0.41, "sheet_total": 1, "form_count": 1,
    "containers": [{"index": 0, "used_length": 54.2, "utilization": 0.41, "copies": 1}], "unplaced": [],
    "placements": [{"part_id": "panel-a", "piece_id": "panel-a_1", "copy": 1, "container_index": 0,
                    "x": 0.5, "y": 6.0, "width": 30.125, "height": 20.125, "angle": 0.0, "mirrored": false,
                    "meta": {}, "panel": null, "transform": [1, 0, 0, 1, 0.5625, 6.0625]}],
    "cut_programs": [], "strips": [], "slits": []
  }],
  "diagnostics": {"elapsed_s": 4.8, "warnings": []}
}
  • status: ok; partial (sheets: some pieces fit no sheet or max_count ran out, and are listed in unplaced); or, for jobs, cancelled with the best layout so far.
  • summary describes the best layout. cost is containers × the sheet cost, or the used length × cost_per_length, and null when you sent no cost. For a guillotine layout containers counts distinct forms, not printed sheets, so cost is the cost of one sheet of each form: multiply the sheet cost by sheet_total for the whole run.
  • The frame. The origin is the container's corner: a sheet's lower-left corner, or the roll's left edge where it starts. Positions include the margins and the lead-in. container_index is the sheet (or, on a roll kept within beds, the bed). x and y are the lower-left corner of the placed part's bounding box.
  • transform maps the part's outline, in the coordinates you sent, onto the container: [a, b, c, d, e, f] with x' = a·x + c·y + e and y' = b·x + d·y + f, the order PDF uses. cut is the placed outline and outline the same with bleed added.
  • N-up and strips. A guillotine layout's containers are forms: each is printed copies times, and sheet_total adds them up. Each form has a knife program in cut_programs (steps with positions and back-gauge settings, and total_strokes). A strips layout lists its strips and slits.
  • Splits and groups. Each layout lists split_parts (the parts on more than one sheet or form) and distribution: for each part id, [{"container", "per_sheet", "copies"}]. With ganging rules, groups gives each container's group and its signature ([{"container": 0, "group": 0, "signature": {"coating": "Gloss UV"}}]), a roll layout lists its segments ([{"group", "start", "end"}] along the roll), and each container carries the inks union of its parts ({"front": [...], "back": [...]}, or null). The summary adds split_parts and group_count.
  • diagnostics has the time the nest took, warnings, and other details that may change between versions.

Sync and queued jobs#

mode on nest/ chooses how the request runs:

  • auto (the default) answers 200 with the result when the request is within the sync limits and the server can run it now. Otherwise it queues a job and answers 202.
  • sync never queues: 413 over the sync limits, 429 when a synchronous request can't start right now.
  • async always queues.

A queued job answers 202 with {"id", "status", "url"} and a Location header. Poll the URL every one or two seconds until status is succeeded, failed or cancelled; while it runs, progress shows the time spent, the layouts found and a summary of the best one. Sync nests are stored as jobs too, so the id of a sync result works with the job endpoints. Jobs are deleted 7 days after they are created.

POST /a/{team_slug}/nesting/api/v1/jobs/

Queue a nest job (the request's `mode` is ignored).

nesting_jobs_create · View in API reference

GET /a/{team_slug}/nesting/api/v1/jobs/{id}/

Status, progress (`elapsed_s`, `layouts`, `best` summary) and, once finished, the `result`.

nesting_jobs_retrieve · View in API reference

GET /a/{team_slug}/nesting/api/v1/jobs/

The team's jobs.

nesting_jobs_list · View in API reference

POST /a/{team_slug}/nesting/api/v1/jobs/{id}/cancel/

Ask a queued or running job to stop; it ends `cancelled` (with its best layout so far, if any).

nesting_jobs_cancel · View in API reference

Cancelling a job that has already finished answers 409. A cancelled job keeps its best layout so far, if it had one.

Candidates#

While a queued job runs, the layouts it finds are listed (up to options.stream_candidates, at most 20). Sheet and roll jobs can publish distinct arrangements before the search ends. Extra search time does not guarantee more layouts; simple jobs may have only one useful result. Poll jobs/<id>/candidates/?cursor=<n> with the cursor of the previous answer (start at 0): it returns the rows changed since then, has_more when you should poll again at once, and the job's status. Each row has its seq, status (streamed, then verified or removed once the job ends), found_ms, score (lower is better), repeats (layouts with the same figures) and a summary. Once the list is full, a better layout replaces the worst row (same seq, a new cursor position), and the job's final layouts are always listed. jobs/<id>/candidates/<seq>/ returns the row's layout. A layout larger than 5 MB is listed without it (layout_kept: false) and answers 410, as does a row removed by the final checks. The final result is always the verified list.

GET /a/{team_slug}/nesting/api/v1/jobs/{id}/candidates/

Layouts an async job found while running (up to `options.stream_candidates`, at most 20), listed by cursor.

nesting_jobs_candidates · View in API reference

GET /a/{team_slug}/nesting/api/v1/jobs/{id}/candidates/{seq}/

One candidate's layout (the request's `include` applies).

nesting_jobs_candidate · View in API reference

Cut files#

GET /a/{team_slug}/nesting/api/v1/jobs/{id}/cutfile/

Cut file of a succeeded job's layout: one page per sheet, or the roll.

nesting_jobs_cutfile · View in API reference

Download a succeeded job's layout as format=pdf (the default), dxf or cff2, with layout as the layout's index (0 is the best). You get one page per sheet, or one for the whole roll; pages=beds gives one PDF page per bed for a roll kept within beds. Each part is cut with its cut_method.

curl -sS -H "Authorization: Api-Key $API_KEY" -OJ \
  "https://app.example.com/a/acme/nesting/api/v1/jobs/00000000-0000-0000-0000-000000000000/cutfile/?format=pdf&pages=beds"

Errors and limits#

Status When
400 {"errors": [{"path": "parts[3].outline", "message": "…"}]}: an invalid field, or input that can't be nested (such as a part wider than the roll without panelling; the path is then empty).
413 The body is over 5 MB, or the request is over a limit of its mode: {"detail", "limit", "value", "maximum"}.
410 A candidate's layout wasn't kept (too large) or failed the final checks.
422 A sync nest found no valid layout: {"detail", "warnings"}.
429 More than 60 nests started in a minute by the same user; a synchronous request that can't start right now; or the team already has as many queued or running jobs as it may.
503 Nesting is unavailable. Try again later.

Lengths and coordinates may be at most 100,000 inches (in any unit). The limits of each mode (more in Sizes and limits):

LimitSyncAsyncError when exceeded
Time limit (s)10120The time limit is above the maximum for this mode.
Pieces1,0004,000Too many pieces (after quantities and panelling).
Distinct parts5002,000Too many distinct parts.
Points per outline2,0002,000An outline has too many points; simplify it.
Outline points in total200,000200,000Too many outline points in total; simplify the outlines.
Outline points in the response400,0001,000,000The response would carry too many outline points; ask for fewer layouts, simpler outlines or include=["transform"] only.
Raster grid cells50,000,00050,000,000The container is too large for the raster grid.
Layouts per request55Too many layouts requested.
Queued or running jobs per team–2More async jobs than this at once are refused until one finishes.
GET /a/{team_slug}/nesting/api/v1/limits/

The effective limits (`sync`, `async`), free sync slots in this process and the team's active jobs.

nesting_limits · View in API reference

limits/ returns your team's limits, whether a synchronous nest can start now, and how many of the team's jobs are queued or running. Validation errors are always 400, whatever the mode.

Every nesting endpoint#

API operations tagged nesting
MethodPathWhat it does
GET /a/{team_slug}/nesting/api/v1/jobs/ The team's jobs.
POST /a/{team_slug}/nesting/api/v1/jobs/ Queue a nest job (the request's `mode` is ignored).
GET /a/{team_slug}/nesting/api/v1/jobs/{id}/ Status, progress (`elapsed_s`, `layouts`, `best` summary) and, once finished, the `result`.
POST /a/{team_slug}/nesting/api/v1/jobs/{id}/cancel/ Ask a queued or running job to stop; it ends `cancelled` (with its best layout so far, if any).
GET /a/{team_slug}/nesting/api/v1/jobs/{id}/candidates/ Layouts an async job found while running (up to `options.stream_candidates`, at most 20), listed by cursor.
GET /a/{team_slug}/nesting/api/v1/jobs/{id}/candidates/{seq}/ One candidate's layout (the request's `include` applies).
GET /a/{team_slug}/nesting/api/v1/jobs/{id}/cutfile/ Cut file of a succeeded job's layout: one page per sheet, or the roll.
GET /a/{team_slug}/nesting/api/v1/limits/ The effective limits (`sync`, `async`), free sync slots in this process and the team's active jobs.
POST /a/{team_slug}/nesting/api/v1/nest/ Nest parts on a roll or on sheets.

All “nesting” operations in the API reference

Last updated Sept. 27, 2026