Skip to main content

CutOptim

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.

PyPI CI Python 3.9+ Zero dependencies MIT

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, json and hmac from 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 the max engine 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

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)

Source distribution for cutoptim 1.0.0
File Size Uploaded Provenance
cutoptim-1.0.0.tar.gz 18.1 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for cutoptim 1.0.0
File Interpreter ABI Platform
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 log

Release 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 log

Security 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.

Select from 2 files

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 log

A 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.

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page