Industrial API · v1
Build Bolt optimization into your own systems.
Bolt Cut and Bolt Nest are available as web apps and as an industrial REST API. Send parts and stock as JSON from your ERP, MES or CAM pipeline, and get back sheet layouts you can route straight to production.
https://www.bolt-optimizations.comjobId, poll until it finishes.Get started
Authentication
Every request must include your secret API key in the x-api-key header. CLGS keys begin with clg_; standalone Bolt keys begin with bolt_ and are shown only once when they are created — store them in a secrets manager, never in client-side code or a public repository.
Requests without a key, or with a revoked key, return 401 Unauthorized.
x-api-key: clg_<your-60-character-key>
{ "error": "Invalid or inactive API key" }
Get started
Asynchronous jobs
For every submission, send an Idempotency-Key of 1–160 letters, numbers, underscores, dots, colons or hyphens. Save it before sending and reuse it for retries of the same input. A new key means new work. Changing input under a used key returns a conflict; uncertain work keeps its reservation for review.
Optimization is compute-heavy, so both APIs use the same job model. A submit request returns 202 Accepted immediately with a jobId; the engine runs in the background and you poll the job until it reaches a final state.
- Poll with a short delay (1–3 seconds). Polling is free — it never counts against your quota.
- Finished jobs are kept in memory for a limited time and may be removed under load or on a server restart. Collect results promptly and store them on your side.
- A
404means this credential cannot find an owned job. Keep the original request key; do not start duplicate work to recover an uncertain outcome. - Jobs belong to the account and product. Another active key on that same account may read them only with the required current operation permission. Another account cannot read them.
Get started
Quotas & limits
API keys share one monthly allowance for the account and product. Bolt uses its account API-unit allowance. Nest starts with at least one available credit, then deducts ceil(active engine seconds / 200) credits after measured work. A usable partial layout is billed normally; a confirmed failed or cancelled job returns unused reservations. Nest shortfalls become negative credits carried into the next monthly allowance. Polling and downloads do not add usage. Enterprise web/API channels share their bundle wallets.
When the quota is used up, submissions return 429 Too Many Requests until the next reset. The engine also has a bounded queue: if it is saturated, a submission can be rejected with 429 or 503 — retry with back-off.
{ "error": "Account allowance exhausted", "code": "USAGE_QUOTA_EXHAUSTED" }
Get started
Errors
Errors use conventional HTTP status codes with a JSON body that explains what went wrong.
| Status | Meaning | What to do |
|---|---|---|
400 | The request body failed validation. | Read error — it names the invalid field or array index. |
401 | Missing, invalid or revoked API key. | Check the x-api-key header. |
404 | Job not found for this account, or engine outcome awaiting review. | Keep the same request key and contact support for uncertain work. |
413 | Request body too large. | Split the job or reduce geometry detail. |
429 | Account allowance exhausted, insufficient Nest start credit, or the engine queue is full. | Wait for the reset or retry with back-off. |
5xx | Engine or server error. | Retry with the same request key; contact support for unknown outcomes and include the jobId. |
Bolt Cut API
Rectangular panel & sheet optimization
Send the parts you need (pieces) and the stock you have (inventory). All dimensions are in millimetres. Each job optimizes one material; submit one job per material.
/api/v1/optimizeSubmit an optimization job
Validates the request and queues it. Returns 202 with a jobId. Uses one account Bolt API unit on successful completion under the current job-unit policy.
Body
materialoptional | string Label for the material, echoed back in the job. |
payloadrequired | object Job settings plus the parts and stock (below). |
payload settings
payloadrequired | object Contains the pieces and inventory arrays. |
bladeThickness | number ≥ 0 Saw kerf in mm. Default 0. |
cuttingMode | 0 | 1 | 20 guillotine (table / panel saw) · 1 free nesting (CNC router, laser) · 2 multistage saw. Default 0. optimizationType is accepted as an alias; if both are sent they must match. |
optimizationProfile | "quick" | "production" | "max" Search depth. production is what the web app uses. |
searchTimeBudgetSeconds | integer 0–300 Search-time budget for the optimizer. |
payload.payload.pieces[] — parts to cut
lengthrequired | number > 0 Finished length in mm (grain direction when rotation is locked). |
widthrequired | number > 0 Finished width in mm. |
canrotaterequired | 0 | 11 lets the part rotate 90°; 0 preserves grain. |
pieceExternalIdrequired | integer, unique Your ID for the part, returned in the result. One entry per physical part. |
priority | integer Placement priority. Default 0. |
payload.payload.inventory[] — stock sheets
length, widthrequired | number > 0 Sheet size in mm. |
inventoryExternalIdrequired | integer, unique Your ID for the sheet or offcut. |
TrimTop TrimLeftTrimBottom TrimRight | number ≥ 0 Edge trims in mm. The usable area after trims must be positive. |
priority | integer Use order: offcuts/remnants before full boards, for example. |
curl -X POST https://www.bolt-optimizations.com/api/v1/optimize \ -H "x-api-key: $BOLT_API_KEY" \ -H "Idempotency-Key: $JOB_REQUEST_KEY" \ -H "Content-Type: application/json" \ -d '{ "material": "18mm MDF White", "payload": { "payload": { "pieces": [ { "length": 600, "width": 400, "canrotate": 1, "pieceExternalId": 101 }, { "length": 600, "width": 400, "canrotate": 1, "pieceExternalId": 102 }, { "length": 800, "width": 300, "canrotate": 0, "pieceExternalId": 103 } ], "inventory": [ { "length": 2800, "width": 2070, "TrimTop": 0, "TrimLeft": 0, "TrimBottom": 0, "TrimRight": 0, "inventoryExternalId": 1001, "priority": 1 } ] }, "bladeThickness": 4, "cuttingMode": 0, "optimizationProfile": "production" } }'
{ "jobId": "72d1e2c2-eb9a-4f6e-97cb-cc2b6871c5d1", "status": "queued", "message": "Optimization queued" }
/api/v1/optimize/status/:jobIdGet job status & result
Returns the job's current state. Poll until status is completed or error. Free — does not use quota.
status | string One of the states above. |
material | string Echo of the submitted label. |
result | array | null One entry per used sheet — see Result schema. |
dxf_file | string | null Full cut-sheet DXF as text (also available from the DXF endpoint). |
error, errorCode, retryable | Populated when status is error. If retryable is true, resubmitting may succeed. |
queuedAt, startedAt, completedAt | epoch ms Lifecycle timestamps. |
curl https://www.bolt-optimizations.com/api/v1/optimize/status/$JOB_ID \ -H "x-api-key: $BOLT_API_KEY"
{ "status": "completed", "material": "18mm MDF White", "result": [ { "inventoryExternalId": 1001, "length": 2800, "width": 2070, "pieces": { "0": { "pieceExternalId": 101, "topX": 0, "topY": 0, "bottomX": 600, "bottomY": 400 }, … }, "cuts": { "1": { "topX": 600, "topY": 0, "bottomX": 600, "bottomY": 2070 }, … }, "wastes": { "1": { "topX": 1410, "topY": 0, "bottomX": 2800, "bottomY": 2070 }, … } } ], "dxf_file": "0\nSECTION\n…", "error": null }
/api/v1/optimize/:jobId/dxfDownload the cut-sheet DXF
Returns the DXF for a completed job as a file download (application/dxf), ready for CAM or machine software. Returns 400 if the job has not completed yet. Free.
curl -o cutsheet.dxf \ https://www.bolt-optimizations.com/api/v1/optimize/$JOB_ID/dxf \ -H "x-api-key: $BOLT_API_KEY"
Result schema
Each result entry is one sheet. Coordinates are in mm from the sheet's top-left corner.
inventoryExternalId | The stock sheet used. length, width and trims are echoed. |
pieces | Placed parts keyed "0", "1"… Each has pieceExternalId and its bounding box topX, topY, bottomX, bottomY. A part is rotated when bottomX − topX differs from its submitted length. |
cuts | Ordered saw passes keyed "1", "2"… Each is a line from (topX, topY) to (bottomX, bottomY). |
wastes | Leftover rectangles — use them to track reusable offcuts. |
completed. A layout that fails the check ends as error rather than returning bad geometry.Bolt Nest API
True-shape nesting
Describe the sheet and each part as a polygon outline (plus optional inner holes). Coordinates are in millimetres with the origin at the sheet's bottom-left corner and Y pointing up. Every result is geometrically validated by the engine before it's returned.
{ "error": { "code": "INVALID_INPUT", "message": "…" } }. A submission the engine rejects — invalid input, a full queue, or the engine being unavailable — is not charged against your quota./api/v1/nest/jobsSubmit a nesting job
Validates and queues the job. Returns 202 with a jobId and a one-credit start reservation. Invalid input returns 422 with the exact field and part at fault.
Body
sheet.width | number > 0 Sheet width (X) in mm. Default 2440. |
sheet.height | number > 0 Sheet height (Y) in mm. Default 1220. |
sheet.margin | number ≥ 0 Edge clearance kept free on all sides. Default 15. |
kerf | number ≥ 0 Minimum spacing between parts (tool / cut width). Default 6. 0 is allowed. |
rotationSteps | integer 1–360 Number of evenly spaced rotations to try for rotatable parts — 4 means 0°, 90°, 180°, 270°. Default 4. |
partsrequired | array, 1–5000 Part definitions (below). |
optimization | object Advanced search tuning, e.g. passes (1–64) and seed for reproducible runs. Unknown keys are rejected. |
parts[]
idrequired | integer, unique Your ID for the part definition, returned on every placement. |
verticesrequired | [{ x, y }], 3–1024 Outer outline as a closed polygon (don't repeat the first point). |
holes | [[{ x, y }]], ≤ 256 Inner cut-outs, each a polygon. Holes are preserved in the output. |
quantity | integer 0–10000 How many copies to place. Default 1. |
canRotate | booleanfalse keeps the part at its drawn orientation (grain, print direction). Default true. |
Limits per job
- Up to 10,000 part instances and 5,000 part definitions.
- Up to 50,000 input vertices in total, outlines and holes combined.
- Request body up to 10 MB; each job runs for up to 180 seconds.
curl -X POST https://www.bolt-optimizations.com/api/v1/nest/jobs \ -H "x-api-key: $BOLT_API_KEY" \ -H "Idempotency-Key: $JOB_REQUEST_KEY" \ -H "Content-Type: application/json" \ -d '{ "sheet": { "width": 1200, "height": 800, "margin": 10 }, "kerf": 4, "rotationSteps": 4, "parts": [ { "id": 1, "quantity": 3, "vertices": [ {"x":0,"y":0}, {"x":300,"y":0}, {"x":300,"y":200}, {"x":0,"y":200} ] }, { "id": 2, "quantity": 2, "vertices": [ {"x":0,"y":0}, {"x":250,"y":0}, {"x":125,"y":220} ] }, { "id": 3, "quantity": 2, "vertices": [ {"x":0,"y":0}, {"x":200,"y":0}, {"x":200,"y":200}, {"x":0,"y":200} ], "holes": [ [ {"x":70,"y":70}, {"x":130,"y":70}, {"x":130,"y":130}, {"x":70,"y":130} ] ] } ] }'
{ "jobId": "c1c8de2f-5ad0-41dd-a102-f6e55b85f08c", "status": "queued", "billing": { "reservedToStart": 1, "secondsPerCredit": 200, "overdraft": "carry_to_next_allowance" } }
/api/v1/nest/jobs/:jobIdGet job status & result
Poll until the job reaches a final state. Free — does not use quota. Saved billing results are read by account ownership. Final customer retention and recovery policies remain under review.
completed | Every requested part was placed. |
partial | A valid layout was produced, but some parts could not fit — they're listed in result.unplacedParts with a reason. Check this before sending to production. |
error | The job failed; see error.code and error.message. |
queuePosition | Position in the engine queue while queued, otherwise 0. |
elapsedSeconds | Run time so far, or total run time once finished. |
curl https://www.bolt-optimizations.com/api/v1/nest/jobs/$JOB_ID \ -H "x-api-key: $BOLT_API_KEY"
{ "jobId": "c1c8de2f-5ad0-41dd-a102-f6e55b85f08c", "status": "completed", "elapsedSeconds": 1.4, "result": { "status": "ok", "success": true, "sheets": [{ "sheetIndex": 0, "width": 1200, "height": 800, "utilizationPercent": 32.06, "placedParts": [{ "id": 3, "instanceId": 6, "x": 314, "y": 234, "rotation": 0, "vertices": [{"x":314,"y":234}, {"x":514,"y":234}, …], "holes": [[{"x":384,"y":304}, …]], "netArea": 36400 }, …] }], "unplacedParts": [], "statistics": { "validated": true, "requestedParts": 7, "placedParts": 7, … }, "warnings": [ … ] }, "error": null }
/api/v1/nest/jobs/:jobIdCancel a job
Stops a queued or running job and returns its current state. The job ends as cancelled within a moment. Free.
curl -X DELETE https://www.bolt-optimizations.com/api/v1/nest/jobs/$JOB_ID \ -H "x-api-key: $BOLT_API_KEY"
/api/v1/nest/healthEngine status & limits
Whether the engine is ready, how busy it is, and the input limits it enforces. Useful as a pre-flight check. Free.
{ "status": "ok", "engineReady": true, "engineTimeoutSeconds": 180, "activeJobs": 0, "queuedJobs": 0, "limits": { "instances": 10000, "definitions": 5000, "verticesPerPart": 1024, "inputVertices": 50000, "holesPerPart": 256 } }
Result schema
sheets[] | One entry per used sheet: sheetIndex, width, height, utilizationPercent (net of holes) and placedParts. |
placedParts[] | id (your part ID) and instanceId (which copy), the placement x, y and rotation in degrees, plus the final placed vertices and holes in sheet coordinates — ready to write to DXF or send to CAM. outerArea, holeArea and netArea are in mm². |
unplacedParts[] | Copies that couldn't be placed, each with id, instanceId, a machine-readable reason and a readable message. |
statistics | Run details. validated: true confirms the layout passed the engine's final geometry audit; requestedParts and placedParts give the counts. |
warnings[] | Notes about the run to show to operators — for example, that parts are not nested inside other parts' holes. |
[{
"id": 9, "instanceId": 2,
"reason": "DOES_NOT_FIT_EMPTY_SHEET_IN_ALLOWED_ROTATIONS",
"message": "Part cannot fit the usable sheet in any allowed orientation"
}]
Guides
Client examples
A complete submit-and-poll loop. The same pattern works for both products — only the paths and body change.
const BASE = 'https://www.bolt-optimizations.com'; const KEY = process.env.BOLT_API_KEY; const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); async function optimize(body, requestKey) { // 1. Submit const res = await fetch(`${BASE}/api/v1/optimize`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'x-api-key': KEY, 'Idempotency-Key': requestKey }, body: JSON.stringify(body), }); if (res.status !== 202) throw new Error((await res.json()).error); const { jobId } = await res.json(); // 2. Poll until a final state for (;;) { await sleep(2000); const job = await (await fetch(`${BASE}/api/v1/optimize/status/${jobId}`, { headers: { 'x-api-key': KEY }, })).json(); if (job.status === 'completed') return job.result; if (job.status === 'error') throw new Error(job.error); } }
import os, time, requests BASE = "https://www.bolt-optimizations.com" HEADERS = {"x-api-key": os.environ["BOLT_API_KEY"]} def optimize(body, request_key): # 1. Submit r = requests.post(f"{BASE}/api/v1/optimize", json=body, headers={**HEADERS, "Idempotency-Key": request_key}, timeout=30) if r.status_code != 202: raise RuntimeError(r.json().get("error")) job_id = r.json()["jobId"] # 2. Poll until a final state while True: time.sleep(2) job = requests.get(f"{BASE}/api/v1/optimize/status/{job_id}", headers=HEADERS, timeout=30).json() if job["status"] == "completed": return job["result"] if job["status"] == "error": raise RuntimeError(job["error"])
Guides
Support
Questions about an integration, higher quotas or a failed job? Email support@bolt-optimizations.com and include the jobId and the time of the request.