CutOptim Engine API
A cutting-optimization API: send parts and stock — 2D panels, 1D bars, timber or true-shape nesting of irregular parts — and get back the layout, cut plan and yield. By default the same request always returns the same plan.
See the full benchmark → · Measured, not claimed
{
"parts": [
{ "name": "bracket", "polygon": […], "qty": 12, … },
{ "name": "gusset", "polygon": […], "qty": 14, … },
{ "name": "plate", "polygon": […], "holes": […], "qty": 12, … },
{ "name": "flange", "source": { … }, "qty": 10, … }
],
"stock": [{ "w": 2500, "h": 1250, "exclusions": […], … }],
"options": { "minSeparation": 5, "edgeClearance": 10 }
}Why build on it
The same input always returns the same output — no randomness, no clock in the algorithm. You can cache results and diff them in tests. The one opt-in exception, the time-limited nesting max search, says so in every response (deterministic: false).
Not just rectangles: the guillotine cut sequence, cut lines vs. saw passes, sawn length, and a flag telling you whether it is producible on a panel saw.
Kerf, per-side trim, tolerance, cost mode, grain direction and grain matching, max cutting stages, rotation minimising — the same options the app exposes.
Build it, or call it
A cutting optimizer looks like a weekend project and turns into a long tail of edge cases: kerf and trim arithmetic, stock that runs out, rotation and grain rules, layouts that must be sawable edge to edge, polygons with holes — and a regression suite that proves a change never makes a plan worse. The Engine API is that work, already done and measured.
Writing and validating four optimizers — 2D guillotine panels, 1D linear stock, timber by cross-section and true-shape nesting — and keeping them correct as your requirements grow. Your team integrates one HTTP contract instead.
Plan quality is published, not claimed: the open benchmark runs the engine on public academic instance sets and generated jobs, with the method and the raw results downloadable. Run the same instances against anything else you are considering.
A price on the page, a 14-day trial and a key from your dashboard — no sales call and no licence negotiation before you can test. The validate endpoints need no key at all.
Your data model, your UI and your pricing rules. Synchronous calls store nothing; an asynchronous max job keeps its result only for a short retention window so you can poll it. A meta object on any part or sheet comes back verbatim, so the plan reconciles with your records.
Need a ready-made ordering screen instead of building one?
The CutOptim Configurator runs on this engine →Connect your own shop or ERP →Pricing
Every request counts the same — nesting included · We email you at 90% of the quota and when it runs out.
Try it free: a public demo key on the API reference, and test keys with 1,000 requests a month that are never billed
Larger volumes. 10,000 requests a month is the standard plan, not a ceiling on what we can run. If you need more — higher volume, separate keys for staging and production, or a dedicated arrangement — tell us your numbers and we price it individually. Tell us your volume →
By subscribing you accept the Engine API Service Terms (in English).
The regular CutOptim plans (Free / Pro / Workshop) are unrelated to the API — the in-app optimizer stays included in them. See app pricing →
What goes in, what comes out
One real job per mode: the request body on top, and below it the plan that came back, drawn straight from the response.
POST /v1/optimize/2d{
"parts": [
{ "name": "Side", "w": 720, "h": 560, "qty": 6 },
{ "name": "Bottom", "w": 764, "h": 560, "qty": 3 },
{ "name": "Top", "w": 764, "h": 560, "qty": 3 },
{ "name": "Shelf", "w": 762, "h": 520, "qty": 3 },
{ "name": "Rail", "w": 764, "h": 100, "qty": 6 },
{ "name": "Plinth", "w": 764, "h": 120, "qty": 6 },
{ "name": "Door", "w": 397, "h": 716, "qty": 6, "rotatable": false },
{ "name": "Drawer front", "w": 796, "h": 176, "qty": 3 }
],
"stock": [
{ "w": 2800, "h": 2070, "qty": 5, "price": 38.5 }
],
"options": { "kerf": 4, "trim": { "left": 10, "right": 10, "top": 10, "bottom": 10 } }
}200 · sheets[].parts- Sheets
- 2
- sheetCount
- Yield
- 80.31%
- yieldPct
- Parts placed
- 36 / 36
- placed
- Cut lines
- 39
- cutLines
- Material cost
- 77
- totalPrice
A base-cabinet run on standard chipboard. The doors keep their grain (rotatable: false); every other part may turn.
Want the picture from the API itself? For 2D and nesting, add "svg" to include and the response carries the drawing.
POST /v1/optimize/1d{
"parts": [
{ "name": "Post", "length": 2400, "qty": 4 },
{ "name": "Rail", "length": 1750, "qty": 6 },
{ "name": "Brace", "length": 940, "qty": 8 },
{ "name": "Cross", "length": 620, "qty": 10 }
],
"stock": [
{ "length": 6000, "qty": 10, "price": 24.9 }
],
"options": { "kerf": 3, "trim": { "start": 10 } }
}200 · rods[].parts- Bars
- 6
- rodCount
- Yield
- 93.94%
- yieldPct
- Parts placed
- 28 / 28
- placed
- Cuts
- 28
- cuts
- Material cost
- 149.4
- totalPrice
A steel-frame cut list from full-length bars. Each bar shows where its parts start (pos) and what is left over (remaining).
POST /v1/optimize/wood{
"parts": [
{ "name": "Stud", "sw": 45, "sh": 95, "length": 2350, "qty": 6 },
{ "name": "Nogging", "sw": 45, "sh": 95, "length": 555, "qty": 12 },
{ "name": "Joist", "sw": 45, "sh": 145, "length": 3600, "qty": 3 },
{ "name": "Blocking", "sw": 45, "sh": 145, "length": 1150, "qty": 3 }
],
"stock": [
{ "name": "C24 45x95", "sw": 45, "sh": 95, "length": 4800, "qty": 10, "price": 9.8 },
{ "name": "C24 45x145", "sw": 45, "sh": 145, "length": 4800, "qty": 6, "price": 14.6 }
],
"options": { "kerf": 3 }
}200 · sections[].rods[].parts- Cross-sections
- 2
- sectionCount
- Bars
- 8
- rodCount
- Yield
- 91.17%
- yieldPct
- Parts placed
- 24 / 24
- placed
- Material cost
- 92.8
- totalPrice
Framing timber in two cross-sections. Each section is cut only from its own stock and comes back with its own totals.
POST /v1/optimize/nest{
"parts": [
{ "name": "bracket", "polygon": [[0,0], [300,0], [300,100], [100,100], [100,300], [0,300]], "qty": 12, "allowedRotations": [0,90,180,270] },
{ "name": "gusset", "polygon": [[0,0], [250,0], [0,250]], "qty": 14, "allowedRotations": [0,90,180,270] },
{ "name": "plate", "polygon": [[0,0], [240,0], [240,160], [0,160]], "holes": [[[60,65], [180,65], [180,95], [60,95]]], "qty": 12, "allowedRotations": [0,90] },
{ "name": "flange", "source": { "content": "<svg xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"90\" cy=\"90\" r=\"90\"/><circle cx=\"90\" cy=\"90\" r=\"32\"/><circle cx=\"90\" cy=\"30\" r=\"9\"/><circle cx=\"150\" cy=\"90\" r=\"9\"/><circle cx=\"90\" cy=\"150\" r=\"9\"/><circle cx=\"30\" cy=\"90\" r=\"9\"/></svg>", "filename": "flange.svg" }, "qty": 10, "allowedRotations": [0] }
],
"stock": [
{ "w": 2500, "h": 1250, "qty": 3, "price": 96, "exclusions": [{ "polygon": [[1150,0], [1350,0], [1350,70], [1150,70]], "quality": 0 }] }
],
"options": { "minSeparation": 5, "edgeClearance": 10 }
}Nesting isn't in the public demo — run this example with a free test key. Create a free test key
200 · sheets[].parts- Sheets
- 1
- sheetCount
- Fill
- 56.32%
- density
- Parts placed
- 48 / 48
- placed
- Cut length
- 49,463
- cutLength
- Pierces
- 110
- pierces
Laser-cut steel parts: three sent as coordinates, the flange as an SVG file (source). Every part stays out of the clamp zone (exclusions) and keeps the requested gap (minSeparation).
Want the picture from the API itself? For 2D and nesting, add "svg" to include and the response carries the drawing.
Every drawing is made from the coordinates in the response — nothing is placed by hand. The four jobs are solved by the same engine the API runs, and our test suite re-solves them so the numbers stay true.
What the API does
One call each for 2D panels, 1D linear stock, timber with cross-section matching, and true-shape nesting of irregular polygons — POST /v1/optimize/2d, /1d, /wood and /nest.
POST /v1/optimize/nest packs arbitrary polygons (with holes) onto fixed sheets, interlocking parts into each other’s concave pockets — 6 sheets where the same parts by bounding box need 9. For laser, plasma and waterjet. Per-sheet exclusion zones (a defect, a clamp) come with it.
Tag parts and stock with a material and the optimizer partitions the job: each material is cut only from its own stock. On every mode; a per-material rollup comes back.
Name a banding type per edge (2D) and the response totals the running metres per type — per part and per order. Metadata: it never moves a part.
Name the parts whose figure must run on — a drawer front over two doors — in a matched set (matchedSets, 2D), and they are cut side by side from one piece of board, one kerf apart. Every member comes back with its set and position, and the CSV, SVG and DXF exports mark them too.
Ask for include:["svg","csv","dxf"] and the response carries the layout as a ready-made file, inline — a standalone 2D SVG drawing, an R12/AC1009 DXF, or a CSV cut list. No storage, no second call.
Attach a meta object — your ERP article number, order-line id, customer ref — to any part or stock row and it comes back verbatim on every placed piece and every sheet/rod, so the plan reconciles with your system.
POST /v1/validate/{2d,1d,wood,nest} schema-validates the same body without solving — no key, no quota. Check a payload will not be rejected and get feasibility warnings before you spend a call.
minimizeCost ranks by the lowest total bill across priced stock sizes, mixing formats; the default minimizes material. Both run the same guillotine algorithm.
Mark stock to use up first, treat quantities as a hard cap with respectStock, and flag must-cut parts that win board space when material is short.
Not just rectangles: the guillotine cut sequence with per-step stop positions, cut lines vs. saw passes, sawn length, and a producible-on-a-panel-saw flag.
kerf, per-side trim, tolerance, grain direction, matched sets, maxCutStages and rotation minimising — the same options the app exposes.
The same input always returns the same output — cache and diff it (the opt-in, time-limited nesting max search reports deterministic: false). A pinnable engine version and an OpenAPI 3.1 document describe the whole contract.
Send an SVG or DXF instead of coordinates: parts[].source reads the outline and its holes out of the drawing, and POST /v1/import/nest splits a multi-part file first. Nothing is stored — the file is parsed in memory and is gone with the response.
Claude Code, Cursor, VS Code and Claude Desktop call the engine as tools through https://api.cutoptim.com/mcp — with the same API key and quota as REST. It also runs locally as the npm package @cutoptim/mcp.
Two speeds: quote now, save material later
Most integrations need both — an answer while a customer waits, and a tighter plan when the job goes to the machine. You choose per call.
The synchronous engines — heuristic for 2D, 1D and timber, lbf for nesting — answer inside the HTTP request, deterministically, so the plan behind a quote is the plan behind the order.
Send the job with engine: "max" and poll GET /v1/jobs/{id} for the result. On 2D panels it searches toward the proven optimum; on true-shape nesting it spends a time budget (options.timeBudgetMs) looking for fewer sheets and never returns more than the instant answer.
The response has the same shape either way. On 2D the max engine accepts a narrower set of options than heuristic (the reference lists them), and a nesting job with exclusion zones is still served by lbf.
Which engine serves which mode
The endpoint you POST to picks the mode; the engine parameter picks the algorithm. The default heuristic engine serves 2D, 1D and timber; balanced is 2D only; the asynchronous max engine serves 2D panels and true-shape nesting; and nesting defaults to its own lbf engine. Same engine as the app, over HTTP.
- heuristic — The default — a guillotine multi-strategy packer. Highest yield, every layout is saw-cuttable, and it always returns a cut plan. It serves 2D, 1D and timber.
- balanced — An opt-in MaxRects free-nester for 2D. Much faster on very large jobs for slightly less yield, but its layouts often cannot be sawn edge-to-edge, so they carry no cut plan.
- max — The asynchronous tier: you submit the job and poll GET /v1/jobs/{id} for the result. On 2D it is a tree search that reaches the proven optimum on far more jobs, at seconds-to-a-minute per solve — still deterministic and saw-cuttable. On true-shape nesting it runs lbf first, then spends a time budget looking for a layout with fewer sheets and never returns more; that search is time-limited, so its nests say deterministic: false.
- lbf — The true-shape nesting default. It packs irregular polygons into each other’s pockets for laser, plasma and waterjet — instantly and deterministically. max (above) can then search the same job for fewer sheets.
Which endpoint for which material: flat sheets — plywood, MDF, glass, acrylic, sheet metal — go to /v1/optimize/2d; bars, tube, pipe and profile to /v1/optimize/1d; structural timber (a 50×150 only from 50×150) to /v1/optimize/wood; irregular polygons for laser, plasma and waterjet to /v1/optimize/nest.
One call, a full plan
curl https://api.cutoptim.com/v1/optimize/2d \
-H "Authorization: Bearer co_live_…" \
-H "Content-Type: application/json" \
-d '{
"parts": [
{ "name": "Door", "w": 600, "h": 400, "qty": 4 },
{ "name": "Shelf", "w": 800, "h": 300, "qty": 6 }
],
"stock": [{ "w": 2440, "h": 1220, "price": 42 }],
"options": { "kerf": 3, "effort": "balanced" },
"engine": "heuristic"
}'Comes back as:
{
"metrics": { "sheetCount": 1, "yieldPct": 80.62, "placed": 10, "total": 10,
"cutLines": 10, "sawPasses": 13, "cutLength": 10720, "totalPrice": 42 },
"sheets": [ … ],
"cutPlan": [{ "sheet": 0, "step": 1, "axis": "h", "pos": 400, "length": 2440, "stage": 1 }, … ],
"guillotineValid": true,
"engineVersion": "1.0.0+10e0c941",
"deterministic": true
}Full request and response shape, every option, all error codes and every engine:API reference → · Integration guide →
Fast or dense? Pick with one option
The effort option trades solve time against yield. Here is that trade, measured on one demanding job — every figure comes from the real packer.
And it is fast either way: even the largest production jobs — 2,000 parts and more — solve in single-digit seconds on the default engine, comfortably inside the API time budget.
Solutions by industry
The same engine, positioned for the way one trade cuts. Each page shows the endpoint, a request and a response, and the fields that matter for that material.
The engine works on geometry, not on a material catalogue: sheet metal and plate, wood-based panels, glass, plastics and acrylic, composites, foam, cardboard and paper all go through the same endpoints. The material only changes the numbers you send — kerf or part spacing, edge clearance, stock sizes.
True-shape nesting over REST: polygons or DXF/SVG parts in, placements and a DXF/SVG layout out. POST /v1/optimize/nest
1D cut-to-length for tube, pipe and hollow section. POST /v1/optimize/1d
1D cut-to-length for steel and aluminium profile, bar and section, with cost mode. POST /v1/optimize/1d
2D guillotine, zero-kerf score-and-snap for float, tempered and mirror glass. POST /v1/optimize/2d
1D cut-to-length for structural timber with cross-section matching. POST /v1/optimize/wood
True-shape nesting of irregular polygons, with exclusion zones. POST /v1/optimize/nest
True-shape nesting onto plate for laser, plasma or waterjet. POST /v1/optimize/nest
Bar bending schedule optimization for reinforcement steel. · design partners wanted
The why, what and how of the CutOptim Engine API on two pages — the four modes, a real request and its plan, how to try it free, and the two plans. Print-ready, with a QR back to this page.