CutOptim Engine API — Python SDK
Cutting optimization over HTTPS: 2D panels, 1D linear, timber and true-shape nesting.
Deterministic plans, saw-ready cut sequences — standard library only.
Quickstart · API reference · Webhooks · Changelog · Get an API key
Why this SDK
- Typed from the contract. Request and response shapes are
TypedDicts generated from the OpenAPI 3.1 document — mypy and your editor know every field. - No dependencies.
urllib,jsonandhmacfrom the standard library; Python 3.9+. - Retries that never cost twice. Every optimize call carries an
Idempotency-Key; a retried call returns the stored answer instead of a second charge. - Async jobs without the boilerplate.
wait_for_job()polls themaxengine for you — or let a webhook tell you. - Webhook verification built in.
verify_webhook()checks the signature on the raw body, with the replay window and secret rotation handled.
Install
pip install cutoptim
A free test key (co_test_…, never billed) is enough to build the whole integration — create one on your dashboard.
Quickstart
import os
from cutoptim import CutOptim, is_job_accepted
client = CutOptim(api_key=os.environ["CUTOPTIM_KEY"])
plan = client.optimize_2d({
"parts": [
{"name": "Side", "w": 720, "h": 560, "qty": 2},
{"name": "Shelf", "w": 764, "h": 540, "qty": 3, "rotatable": False},
],
"stock": [{"w": 2800, "h": 2070, "price": 48}],
"options": {"kerf": 4},
})
if not is_job_accepted(plan):
print(f"{plan['metrics']['sheetCount']} sheet(s), {plan['metrics']['yieldPct']}% yield")
for sheet in plan["sheets"]:
for p in sheet["parts"]:
print(p["name"], p["x"], p["y"], p["w"], p["h"], "rotated" if p["rotated"] else "")
The same input always returns the same plan — cache it, test against it, quote from it.
Every mode
# 1D linear — bars, profiles, tubes
bars = client.optimize_1d({"parts": [{"name": "Rail", "length": 1200, "qty": 6}], "stock": [{"length": 6000}]})
print(bars["metrics"]["rodCount"], bars["metrics"]["yieldPct"])
# Timber — lengths cut from beams of a given cross-section (sw × sh)
timber = client.optimize_wood({
"parts": [{"name": "Joist", "sw": 45, "sh": 145, "length": 3600, "qty": 4}],
"stock": [{"sw": 45, "sh": 145, "length": 4800}],
})
# True-shape nesting — polygons (or SVG/DXF sources) on sheets
nest = client.optimize_nest({
"parts": [{"name": "Bracket", "polygon": [[0, 0], [120, 0], [120, 40], [40, 120], [0, 120]], "qty": 10}],
"stock": [{"w": 1000, "h": 500}],
})
if not is_job_accepted(nest):
print(nest["metrics"]["sheetCount"], nest["metrics"]["density"])
Check a request without spending a call — the validate endpoints need no key and give the same verdict:
CutOptim().validate_2d(request) # {"valid": True, "warnings": [...]} or CutOptimError(400)
Async max jobs
"engine": "max" searches harder (seconds to a minute) and answers 202 with a job id instead of a plan:
accepted = client.optimize_2d({**request, "engine": "max"})
if is_job_accepted(accepted):
job = client.wait_for_job(accepted["jobId"], timeout_s=300) # polls GET /v1/jobs/{id}, no quota
if job["status"] == "succeeded":
print(job["result"])
Webhooks
Instead of polling, register an endpoint on your dashboard and receive a signed POST (job.succeeded / job.failed) when a max job finishes. Verify it on the raw body:
# Flask
from flask import Flask, request
from cutoptim import verify_webhook
@app.post("/cutoptim/webhook")
def cutoptim_webhook():
if not verify_webhook(request.get_data(), request.headers.get("CutOptim-Signature"), WEBHOOK_SECRET):
return "", 400
event = request.get_json() # deduplicate on event["id"] — delivery is at least once
return "", 204 # answer within 10 s, then do the work
# FastAPI
from fastapi import FastAPI, Request, Response
@app.post("/cutoptim/webhook")
async def cutoptim_webhook(request: Request):
raw = await request.body()
if not verify_webhook(raw, request.headers.get("CutOptim-Signature"), WEBHOOK_SECRET):
return Response(status_code=400)
return Response(status_code=204)
During a secret rotation both secrets sign for 24 hours — pass both: verify_webhook(raw, header, [new_secret, old_secret]).
Retries and idempotency
| Situation | What the client does |
|---|---|
429 busy, 503, network error |
retries (2× by default), waiting Retry-After when the API sends it |
409 idempotency_in_progress |
retries — the first request is still running; the retry receives its answer |
| any optimize call | sends an Idempotency-Key (a fresh UUID, or yours) — the same key on every retry |
Pass your own key to make retries safe across process restarts too:
client.optimize_nest(request, idempotency_key=f"order-{order_id}")
Within 24 hours the same key and request return the stored answer — not charged again, and a max job is not queued twice.
Errors
Any non-2xx answer raises CutOptimError:
from cutoptim import CutOptimError
try:
client.optimize_2d(request)
except CutOptimError as e:
print(e.status, e.code, e.details, e.retry_after) # branch on e.code, never on the message
code |
Status | Meaning |
|---|---|---|
invalid_request |
400 | the request is wrong — details["path"] names the field; never charged |
unauthorized |
401 | missing or unknown API key |
quota_exceeded |
402 | the account's monthly quota is used up — retry_after counts to the reset |
key_revoked |
403 | the key is revoked, or the API subscription is not active |
idempotency_conflict |
409 | the key was used for a different request — use a new key |
too_large |
413 | an input cap was exceeded (parts, rows, body size) |
busy |
429 | at capacity right now — retried automatically |
service_unavailable |
503 | an engine is not available on this deployment |
solve_timeout |
504 | the nesting solve ran out of time |
timeout |
— | wait_for_job() gave up waiting (the job keeps running) |
A network failure that outlasts the retries is raised as the underlying urllib.error.URLError. The full list is on the errors page.
Configuration
CutOptim(
api_key="co_live_…", # not needed for health() and validate_*()
base_url="https://api.cutoptim.com", # default
max_retries=2, # 429 / 503 / 409 in-progress / network errors
timeout_s=30.0, # per request
)
API coverage
| Method | Endpoint | Quota |
|---|---|---|
optimize_2d(req, idempotency_key=None) |
POST /v1/optimize/2d |
1 call |
optimize_1d(req, idempotency_key=None) |
POST /v1/optimize/1d |
1 call |
optimize_wood(req, idempotency_key=None) |
POST /v1/optimize/wood |
1 call |
optimize_nest(req, idempotency_key=None) |
POST /v1/optimize/nest |
1 call |
validate_2d · validate_1d · validate_wood · validate_nest |
POST /v1/validate/* |
free, no key |
import_nest(req) |
POST /v1/import/nest |
free (key required) |
get_job(id) · wait_for_job(id) |
GET /v1/jobs/{id} |
free |
usage() |
GET /v1/usage |
free |
health() |
GET /v1/health |
free, no key |
Versioning
The SDK follows semver. The API is v1 and only grows within it: a new field, endpoint or option arrives as a minor SDK release with the regenerated types; nothing you use today is removed or narrowed. See versioning and stability and the API changelog (also as RSS).
Support
- Questions and bugs: GitHub issues or support@cutoptim.com
- Limits and quotas: cutoptim.com/engine/reference/limits
This repository is published from the CutOptim monorepo; pull requests are welcome and applied upstream.
License
MIT © CutOptim
Metadata
Release files for cutoptim 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| cutoptim-1.0.0.tar.gz | 18.1 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| cutoptim-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.0 kB
Release files / cutoptim-1.0.0.tar.gz
| Download URL | cutoptim-1.0.0.tar.gz |
|---|---|
| Size | 18.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
2f6ad49e5bbd4b5c3dca002eace83a713682212c4bc9867315a2fb6651b99111
|
|
BLAKE2b-256 checksum How to use checksums |
762b0caf8d71eef294f83bad021051ce8ec6fc6e4597ce1033e3ad32c9082982
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logRelease files / cutoptim-1.0.0-py3-none-any.whl
| Download URL | cutoptim-1.0.0-py3-none-any.whl |
|---|---|
| Size | 16.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
58d06897daaa9afd17eed83192388dba195c4a0d7fdab0f6283c67da36161c5a
|
|
BLAKE2b-256 checksum How to use checksums |
9b3d2acfa951b00f7767ab3336ad3c11af1c4a9d21c98047494f92ef5f30031e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logSecurity for cutoptim 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for cutoptim-1.0.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 20, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.