Everything is free during the promotional period: sign up and get 200 free credits every month (reset monthly, no rollover). The pricing details below describe the rates that will apply after commercial general availability.
Developer Docs
Integrate the OpCut API in one minute
REST + Bearer Token, with two nesting engines: rectangular and irregular-shape. The rectangular engine offers sync / async modes; the irregular engine offers sync solving. Field and spec differences are listed explicitly below.
1 · Rectangular nesting API · one-minute integration (curl)
The rectangular endpoint follows opcut-io 1.0 · rect profile. Requests may carry spec: "opcut-io" and spec_version: "1.0" (defaults to 1.0 when omitted); responses always echo them. Rectangular responses keep plan[].parts and also provide the equivalent sheets[].placements; the request field parts is a compatibility alias of the legacy order.
Sign in to the console and create an API Token (prefix opcut_live_; the plaintext is shown only once at creation), then call the nesting endpoint with the Authorization: Bearer header:
# 1) Submit a nesting job (sync mode, result within the 3-second budget)
curl -X POST https://opcut.net/api/v1/optimize \\
-H "Authorization: Bearer opcut_live_xxxx" \\
-H "Content-Type: application/json" \\
-d '{
"spec": "opcut-io", "spec_version": "1.0",
"stock": [{ "id": "ST-1", "w": 2440, "h": 1830, "count": 1 }],
"order": [{ "id": "P0", "w": 600, "h": 600, "qty": 2, "grain": "none" }],
"time_budget_s": 3.0
}'
# 2) Response
{
"spec": "opcut-io", "spec_version": "1.0",
"job_id": "job_xxx", "status": "solved",
"plan": [{ "stock_id": "ST-1", "utilization": 0.88,
"parts": [{ "id": "P0", "x": 0, "y": 0, "w": 600, "h": 600, "rotated": false }] }],
"metrics": { },
"billing": { "call_fee": 0.05, "order_fee": 1.00, "total": 1.05 },
"cut_model": null
}
Request fields: stock/order/time_budget_s; response fields: plan/metrics/billing/cut_model. status ∈ solved | unsolvable — unsolvable now means truly unsolvable only (a part cannot fit on any raw sheet; no order fee is charged). Insufficient stock count is no longer unsolvable: the pre-check returns 422 INSUFFICIENT_STOCK_CAPACITY (zero charge, the error carries the estimated required sheet count). Grain parts with grain: "x"|"y" must not be rotated.
2 · Python example
import requests
API, TOKEN = "https://opcut.net/api", "opcut_live_xxxx"
resp = requests.post(
f"{API}/v1/optimize",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"stock": [{"id": "ST-1", "w": 2440, "h": 1830, "count": 1}],
"order": [{"id": "P0", "w": 600, "h": 600, "qty": 2, "grain": "none"}],
"time_budget_s": 3.0,
},
timeout=10,
)
resp.raise_for_status()
plan = resp.json()
print("cut rate:", plan["metrics"]) # metrics contains utilization and other evaluation fields
print("cost of this call:", plan["billing"]["total"])
# Async mode (large batches): submit then poll; Idempotency-Key is required (24h retry window)
job = requests.post(
f"{API}/v1/jobs",
headers={"Authorization": f"Bearer {TOKEN}",
"Idempotency-Key": "order-20261001-001"}, # required
json={"stock": [{"id": "ST-1", "w": 2440, "h": 1830, "count": 30}],
"order": [{"id": "P0", "w": 500, "h": 500, "qty": 40, "grain": "none"}],
"time_budget_s": 3.0},
timeout=10,
).json()
print(job["status_url"]) # → /v1/jobs/{job_id}, free polling
The jobs submit response contains job_id/status/estimate_sheets/freeze_amount/call_fee/status_url; the polling response contains sheets_used/result/error.
3 · Node example
const API = "https://opcut.net/api";
const TOKEN = process.env.OPCUT_TOKEN; // opcut_live_xxxx
const resp = await fetch(\`\${API}/v1/optimize\`, {
method: "POST",
headers: { "Authorization": \`Bearer \${TOKEN}\`, "Content-Type": "application/json" },
body: JSON.stringify({
stock: [{ id: "ST-1", w: 2440, h: 1830, count: 1 }],
order: [{ id: "P0", w: 600, h: 600, qty: 2, grain: "none" }],
time_budget_s: 3.0,
}),
});
if (resp.status === 402) throw new Error("Insufficient balance: top up in the console");
if (!resp.ok) throw new Error(\`HTTP \${resp.status}: \${JSON.stringify(await resp.json())}\`);
const plan = await resp.json();
console.log("billing:", plan.billing); // { call_fee, order_fee, total }
Error envelopes use the unified shape {error:{code,message,details}}; the 402 semantics are in the error-code table.
4 · Bearer Token acquisition and management
- Get one: sign in / register with your phone number and SMS code (new numbers register automatically and receive trial credit) → create a token under Console · API tokens — the plaintext is shown only once at creation, the server stores only a hash, so a lost token can only be revoked and re-created. In dev mode (
OPCUT_DEV_MODE=1, off by default) sign-in skips the SMS code (the response headerx-opcut-dev-mode: 1marks it); production always validates. - Calling: all /v1/* endpoints authenticate with
Authorization: Bearer opcut_live_<40 hex chars>(theopcut_live_prefix). - Managing: list / revoke any time in the console (revocation takes effect immediately; in-flight old tokens get 401 right away); create multiple tokens to isolate environments.
- Rate limit: 60 requests/minute per token (the unified MVP tier); exceeding it returns 429 + a Retry-After header.
5 · Error-code table
Error envelopes are uniformly {"error": {"code", "message", "details"}}. The table lists common business error codes; irregular-shape specifics appear in its endpoint section below.
| HTTP | code | Meaning and handling |
|---|---|---|
| 400 | INVALID_REQUEST | Invalid request body/parameters (missing Idempotency-Key, bad month format, etc.) — fix per message and retry |
| 400 | AMOUNT_OUT_OF_RANGE | Recharge amount out of range (¥100–¥50,000, engineering defaults) |
| 400 | NO_BILLABLE_CONSUMPTION | The invoice month has no billable consumption (invoices follow actual spend) |
| 401 | INVALID_TOKEN | Token missing / invalid / revoked — re-create in the console |
| 401 | INVALID_CODE etc. | SMS code wrong / expired / voided (sign-in flow) |
| 402 | INSUFFICIENT_BALANCE | Insufficient balance (details carries balance and required amount) — recharge then retry; retrying with the same idempotency key is not billed twice |
| 402 | TOKEN_SUSPENDED | Account overdue (balance < 0); all tokens are suspended automatically — they resume once a recharge is credited |
| 403 | SCOPE_DENIED | The token lacks the required scope (details lists required_scope) — re-issue it |
| 403 | FORBIDDEN / ORG_MISMATCH | Unauthorized access to another organization's resources (data is strictly isolated by org) |
| 404 | NOT_FOUND | Resource does not exist (wrong job or order number) |
| 409 | IDEMPOTENCY_KEY_REUSED | The idempotency key was used with a different request body — change the key and retry (resending the same body replays the original result instead of 409) |
| 409 | ORDER_NOT_PENDING / ORDER_NOT_PAYABLE / CHANNEL_MISMATCH | Recharge order status/channel mismatch (already paid cannot be paid again, wrong channel, etc.; re-cancelling a closed order is an idempotent 200 and not this error) |
| 409 | TOKEN_LIMIT_EXCEEDED | Active tokens reached the per-account cap (details.limit is the cap; revoking a token frees the slot immediately for a new one) |
| 422 | OPCUT_SYNC_LIMIT_EXCEEDED / INSUFFICIENT_STOCK_CAPACITY / ENGINE_LAYOUT_UNFIT | Sync workload exceeds the limit (max 1,000 parts per call; use the async endpoint POST /v1/jobs for bigger orders); or stock capacity insufficient (parts fit individually but not enough sheets — the error carries the estimated required / available sheet counts; add raw sheets or switch to async review); or the layout missed the cut-rate process gate (the engine solved normally but the layout failed the process gate — sync rejects with guidance to adjust part sizes or split orders; an async job with the same parameters ends failed with the engine_layout_unfit error prefix; neither path is charged and sync freezes are fully refunded). The first two are rejected before billing pre-check with zero charge and zero ledger entries |
| 422 | SPEC_UNSUPPORTED | Unsupported spec name or major version — use opcut-io / 1.0; this error is not charged |
| 429 | RATE_LIMITED | Above the 60 requests/minute per-token limit (Retry-After header included) — back off per the header |
| 429 | SMS_COOLDOWN | SMS code send cooldown (60s, Retry-After included) |
| 500 | ENGINE_ERROR / INTERNAL | Server-side internal error (engine failures are not charged, freezes are refunded along the original path) — safe to retry (note: layouts that miss the process gate are now routed to 422 ENGINE_LAYOUT_UNFIT and no longer land here) |
501 (channel not opened) returns a placeholder notice and has no separate business code.
6 · Rate limiting and billing
| Item | Rule |
|---|---|
| Rate limit | 60 requests/minute per token; query-type calls (job status, usage) are free but also consume the quota; exceeding it returns 429 — back off per the Retry-After header |
| Call fee | ¥0.05/call, charged only on business success |
| Order fee | Tiered by sheets actually used: ≤20 sheets ¥1 / ≤100 sheets ¥2 / >100 sheets ¥3; charged only on solved, not on unsolvable (truly unsolvable); insufficient stock capacity is rejected at pre-check with 422 and zero charge |
| Balance pre-check | Maximum real charge per call ¥3.05 (call fee + top order-fee tier); a lower balance returns 402 |
| Overdue suspension | When the balance < 0 all tokens are suspended automatically; they resume once a recharge is credited |
| Idempotent retry | The async endpoint requires Idempotency-Key (24h window); optional on sync — same key + same body returns the original result without double charging, a different key is an independent request |
| Big jobs: use async | For extra-large tasks (tens of thousands of parts) solving time will clearly exceed time_budget_s (a single layout computation cannot be interrupted) — use POST /v1/jobs instead: it accepts and returns immediately, freezes and settles the balance, and supports polling and webhook notification; the budget promise of the regular sync endpoint is unaffected |
| Billing and invoices | The console "Billing & Recharge" page offers per-entry ledger, CSV export and monthly invoice requests (invoices follow actual spend; recharges are not invoiced); when a CSV exceeds 10000 rows and is truncated, the response carries the X-Truncated header (the filtered total row count) |
7 · Input/output spec opcut-io 1.0
One JSON input/output specification with two engine profiles: rect (rectangular guillotine cuts) and nest (irregular true shapes). Sizes and coordinates are uniformly in millimetres (mm); optional parameters have defaults. Read / download the spec (Markdown); this page documents how to call the current endpoints.
- Version handshake: requests carry top-level
specandspec_version; when omitted they default toopcut-ioand1.0. Responses always echo them. Optionalengineselects the endpoint profile:rectfor rectangular,nestfor irregular shapes. - Tolerant read, strict write: unknown request fields are ignored and reported in the response
warnings; clients must tolerate new response fields. Read the warnings — do not mistake an ignored parameter for an effective one. - Geometry naming: irregular outer contours use
outline; the legacy request fieldpolyis permanently accepted as an equivalent. Passing both requires exact equality, otherwise 422; during the transition responses outputoutline/polytogether with a deprecation warning.
| Item | Compatibility rule |
|---|---|
| MAJOR version | Removed fields or changed semantics start a new major version; the old one stays available for at least 6 months. Unknown spec names or unsupported majors return 422 SPEC_UNSUPPORTED. |
| MINOR version | Adds optional fields or enum values only; newer fields surface through warnings. Old clients keep reading known fields. |
| PATCH revision | Corrections only; the interface contract is unchanged. |
| Field deprecation | Docs and response warnings stay at least one minor-version cycle; deletion happens only in the next major; the request poly alias is permanently accepted. |
| Engine upgrades | Algorithm or precision upgrades do not change the spec version. The spec carries the algorithm version in engine.version; current endpoint field differences are listed below — spec_version cannot substitute for the algorithm version. |
| DXF inner drawings | Currently supported: POST /io/dxf parsing, parts[].inner.dxf_ref references, polyline / true-arc dual formats and SVG download; DXF layout export remains reserved for later. |
| HTTP | Spec error code | Handling / current endpoint |
|---|---|---|
| 401 | AUTH_REQUIRED | Check authentication. API tokens currently return INVALID_TOKEN; sessions may return UNAUTHORIZED. |
| 403 | SCOPE_DENIED | Use a token that includes the optimize:sync scope. |
| 402 | INSUFFICIENT_BALANCE | Recharge and retry; overdue suspension is separate, see TOKEN_SUSPENDED. |
| 422 | VALIDATION_FAILED | Fix the input per the Chinese explanations in error.details; current parameter errors use finer codes such as INVALID_REQUEST. |
| 422 | SPEC_UNSUPPORTED | Switch to spec: "opcut-io", spec_version: "1.0". |
| 429 | RATE_LIMITED | Back off per Retry-After; irregular-shape busy concurrency separately uses NESTING_BUSY. |
| 504 | SOLVE_TIMEOUT | Reduce parts or adjust the budget; irregular shapes currently return NESTING_TIMEOUT and are not charged. |
Compatibility notes: existing response structures are kept so current clients do not break. Rectangular keeps plan/parts and adds sheets/placements; irregular still returns sheets[].pieces and metrics.poly_util/area_lb. The current irregular response exposes elapsed time and constructor as seconds/constructor and does not yet provide the spec's engine object; the full difference list is recorded in the engineering docs.
DXF inner drawings · inner holes and marks for rectangular parts
Rectangular parts are still nested by width and height, and each part kind may carry an inner drawing; inner holes and mark lines translate and rotate with the part and appear in the online layout and the downloaded SVG. Every part row on the online trial page can upload one. Download the example DXF: outer rectangle + circular hole + slot hole + MARK lines.
Upload and drawing conventions
- Upload:
POST /io/dxfwith the DXF R14 / AutoCAD 2000 file in the multipartfilefield; a raw request body (Content-Type: application/dxf) is also accepted. Parsing is free; one file ≤2 MiB, ≤512 entities after BLOCK / INSERT expansion. - Units and curves: sizes and coordinates are uniformly in mm — convert your drawing to millimetres first. Supported: CIRCLE, ARC, ELLIPSE, LWPOLYLINE, POLYLINE and BLOCK / INSERT expansion. Default
output_curve: "polyline"flattens curves with a sag error ≤0.1 mm (the maximum deviation between the curve and its polyline);"arc"keeps true arcs. Ellipses are still polyline-approximated; arc mode represents ellipses with line segments. - Outer contour and automatic sizing: keep one separate outermost closed contour; its bounding-box lower-left corner is normalized to zero. The upload response's
envelope: {w,h}is that outer contour's bounding box size (mm) — inner mark lines are not counted. On the trial page, uploading fills the part width/height automatically. The outer contour is not redrawn as an inner cut line. - Uniform outward margin:
parts[].inner.margin_mmis the millimetres added on each side, a finite number ≥0, defaulting to 0 when omitted. When provided explicitly, the server recomputes the part size as(envelope.w + 2 × margin_mm) × (envelope.h + 2 × margin_mm). For example, an outline of 120 × 80 mm with a 5 mm margin yields a 130 × 90 mm part. On the trial page, editing the margin updates width/height in real time and the solve uses that size; the inner graphics translate by the same margin and rotate with the whole part. When legacy API requests omit the field, the manually supplied size and the envelope-overflow check are kept. - Layer-name convention: put inner cutting contours on the
CUT_INNERlayer and marks on theMARKlayer. Closed rings are recognized as holes and nested islands by containment, keeping thedepthlevel; open lines and explicit MARKs become mark lines, and unknown layers default to MARK. CUT_INNER renders as thin solid lines, MARK as grey dashed lines. - Reading hints: the parse response contains
dxf_ref / envelope / entities / warnings, whereentitiescounts outer contour, holes and marks; the legacyinner / bounds / entity_countfields are kept for compatibility. Checkwarningsbefore solving — unsupported entities are clearly rejected or flagged. Unknown or expireddxf_refreturns 404; parse failures, envelope overflow, invalid output formats or bad margins return 422 with Chinese messages.
Two-step integration · solve with an uploaded drawing
Per spec §8.1, pass {"dxf_ref":"uploaded-id","layers":["CUT_INNER","MARK"],"output_curve":"polyline","margin_mm":5} inside parts[].inner (the legacy order[].inner is accepted too). layers may be omitted (default: all); as an array it outputs only the selected layers. output_curve defaults to polyline and can switch to arc; margin_mm defaults to 0. Parts without a drawing need no inner. The inline snapshot in the upload response is kept only for compatibility; new integrations use dxf_ref.
curl --fail-with-body -X POST https://opcut.net/api/io/dxf \\ -F "file=@inner-golden.dxf;type=application/dxf"
import os
import time
import requests
api = "https://opcut.net/api"
headers = {"Authorization": "Bearer " + os.environ["OPCUT_TOKEN"]}
with open("inner-golden.dxf", "rb") as drawing:
uploaded = requests.post(api + "/io/dxf",
files={"file": ("inner-golden.dxf", drawing, "application/dxf")}, timeout=20)
uploaded.raise_for_status()
snapshot = uploaded.json()
print("drawing notes:", snapshot["warnings"])
margin_mm = 5 # extend 5 mm on every side; set to 0 when no margin is needed
response = requests.post(api + "/v1/optimize", headers=headers, json={
"stock": [{"id": "S1", "w": 2440, "h": 1830, "count": 1}],
"parts": [{"id": "P1", "w": snapshot["envelope"]["w"] + 2 * margin_mm,
"h": snapshot["envelope"]["h"] + 2 * margin_mm, "qty": 2,
"inner": {"dxf_ref": snapshot["dxf_ref"],
"layers": ["CUT_INNER", "MARK"],
"output_curve": "polyline", "margin_mm": margin_mm}}],
"time_budget_s": 3
}, timeout=30)
response.raise_for_status()
result = response.json()
job_id = result["job_id"]
if response.status_code == 202:
for _ in range(180):
status = requests.get(api + result["status_url"], headers=headers, timeout=20)
status.raise_for_status()
job = status.json()
if job["status"] == "succeeded":
break
if job["status"] == "failed":
raise RuntimeError(job["error"])
time.sleep(1)
else:
raise TimeoutError("Query the result later with the job id")
svg = requests.get(api + "/v1/jobs/" + job_id + "/svg?sheet=0",
headers=headers, timeout=20)
svg.raise_for_status()
with open("sheet-1.svg", "wb") as output:
output.write(svg.content)
For parts with drawings, both plan[].parts[].inner_graphics and sheets[].placements[].inner_graphics return layers and geometry. Polyline mode provides points; arc mode provides segments — lines as {type:"line",p1:[x,y],p2:[x,y]}, arcs as {type:"arc",cx,cy,r,a1_deg,a2_deg,ccw}. A full circle is one arc (start and end angles equal or 360° apart), drawn in SVG as two half-circle path A segments.
In both formats the points and circle centres are already transformed to absolute millimetre coordinates on the raw sheet; draw scaled along the same axes without re-applying rotation, translation or y-axis flipping. In polyline mode, close each shape by joining the last point to the first. Inner graphics are for display and exchange only — they take no part in nesting, utilization or kerf computation.
Free download: GET /v1/jobs/{job_id}/svg?sheet=0, sheet starts at 0. It follows the job's read permission — pass the API Token used for the solve (or a token / console session with read rights for that organization's jobs). The SVG always contains the inner graphics; online layer toggles do not change the downloaded content.
Third-party file import · supported formats
Import layout files exported by supported third-party nesting software. The two currently verified formats are .EDI and .OPT exports; sizes and coordinates are normalized to millimetres (mm); inch-based and further vendor formats are not implemented yet. Open the file import page →
Use POST /v1/interop/import, uploading via the multipart file field, one file ≤2 MB. The server detects the format from content and validates the extension, with a cap of 500 raw-sheet records and 5000 parts. Import and preview are free; file errors return a Chinese error.message plus error.code — fix the file per the hint and retry.
curl --fail-with-body -X POST https://opcut.net/api/v1/interop/import \\ -F "file=@my-order.EDI"
The response contains format / filename / units / records. Each raw-sheet record provides id / stock / parts / payload; stock is the raw-sheet width/height, parts[].poly is the closed part polygon, and source arcs are sampled into polylines at 24 steps per full circle. The preview shows the raw-sheet size and each part's local contour separately and switches between multi-sheet records; do not treat local coordinates as positions in the source layout.
Clicking "Convert into my nesting order" prefills the selected sheet's payload into the irregular-shape trial page. Orders above 60 kinds or 300 parts per run are split automatically with all quantities kept, ready for per-order verification and submission. Conversion never solves on its own; submitted solves follow the existing billing rules with trial credit used first.
The input spec gains the optional meta.import_source (order level and part level), storing format / filename / record_id / part_id provenance; solve audits tag the import source (source=interop:<format>). Keep the original file so contours, units and process parameters can be cross-checked.
8 · Irregular-shape nesting engine API
POST https://opcut.net/api/nesting/optimize submits true-polygon nesting synchronously. Use a console API Token with the Authorization: Bearer $OPCUT_TOKEN header; the token needs the optimize:sync scope, and the per-token 60 requests/minute limit applies. Signed-in console sessions are accepted too.
Request fields
| Field | Requirement / default | Notes |
|---|---|---|
spec / spec_version | optional, opcut-io / 1.0 | Echoed in the response; engine: "nest" optional. |
stock | required, {w,h} | Positive; the single raw-sheet size in mm; the server computes the required sheet count. |
parts | required, 1–60 kinds, ≤300 parts total | Each id is a unique part number (1–80 chars); qty is an integer 1–999. |
parts[].outline | required outer contour; legacy name poly also accepted | [[x,y],…], 3–512 distinct vertices; the closing point does not count toward the cap. |
constraints | optional, default spacing 0, default rotation step 90° | kerf_mm kerf, trim_mm edge margin and break_margin_mm breaking allowance are all non-negative; allowed_angles_step must be a positive integer divisor of 360. |
options | optional, budget default / cap 30 seconds | time_budget_s greater than 0 and ≤30; route defaults to wheel_score_break (wheel scoring then breaking). seed is an integer random seed, default 1, useful for reruns. |
Legacy flat fields remain accepted: kerf / trim / break_margin / step / budget / route map to the constraint and option fields in the table above. New integrations should use the spec's nested form and avoid mixing both styles.
Geometry rules
- All sizes / coordinates are in mm; up to three decimal places (0.001 mm) are currently supported; the last point may skip repeating the first — the server closes it automatically.
- Contours must have positive area and must not self-intersect or have duplicate / degenerate vertices; v1 accepts outer-contour polylines only, without arc segments. Upstream must discretize arcs with a sag error ≤0.1 mm (the maximum deviation between arc and polyline not exceeding 0.1 mm).
- Response contours are absolute millimetre coordinates on the raw sheet; draw
outlinedirectly without re-applying rotation or offsets.
Full curl example · 12 parts
Set the OPCUT_TOKEN environment variable locally first, then submit 12 identical pentagons; the token never goes into the request file or source code. Leave network headroom in your timeout settings.
curl --fail-with-body --max-time 40 \\
-X POST https://opcut.net/api/nesting/optimize \\
-H "Authorization: Bearer $OPCUT_TOKEN" \\
-H "Content-Type: application/json" \\
-d '{
"spec": "opcut-io",
"spec_version": "1.0",
"engine": "nest",
"stock": {"w": 2250, "h": 3210},
"parts": [{
"id": "P1", "qty": 12,
"outline": [[0,0],[520,0],[520,180],[260,300],[0,180]]
}],
"constraints": {
"kerf_mm": 0, "trim_mm": 0,
"break_margin_mm": 0, "allowed_angles_step": 90
},
"options": {"time_budget_s": 30, "seed": 1, "route": "wheel_score_break"}
}'
Response fields · current endpoint
| Field | Meaning |
|---|---|
spec / spec_version / warnings | Spec echo and compatibility warnings; log and check notes on unknown fields, alias deprecations, etc. |
sheets[] | Per raw sheet: sheet_id/w/h plus nested parts in pieces[]. The spec's placements is not used yet. |
sheets[].pieces[] | id is the input part number; angle the rotation (degrees); outline and transition field poly both return absolute contour coordinates. |
sheets[].util_poly / cut_tier | The sheet's true area utilization as a 0–1 ratio; guillotine separability is feasible / infeasible / unknown, a machine hint only. |
metrics | sheets_used actual sheets; poly_util true area utilization (0–1); bbox_util bounding-box reference utilization (may exceed 1); area_lb the area lower bound sheet count. Multiply by 100 for a percentage. |
verify / route / seconds / constructor / trim | Geometry re-check status/issues, the process route, engine elapsed seconds, the constructor and the edge margin (mm). A successful re-check status is passed. |
svg_url | Same-organization authenticated SVG download; kept for 15 minutes, unavailable after restarts / cache eviction (404). Save the JSON result promptly. |
billing | call_fee / order_fee / total in yuan; the server's settlement for this call is authoritative. |
Billing and failure handling
Rectangular pricing applies: a successful business call costs ¥0.05, and the order fee follows the actual raw-sheet count — ¥1 / ¥2 / ¥3 (≤20 / ≤100 / >100 sheets), e.g. a success on ≤20 sheets totals ¥1.05. The balance pre-check threshold is ¥3.05; the freeze starts from the area lower bound and settles on actual sheets; failures unfreeze in full with no call fee. Queries and SVG downloads are free; final prices follow the billing spec and your bill.
Invalid input returns 422 INVALID_REQUEST; raw sheets that cannot hold the parts return 422 NESTING_UNFIT; busy concurrency returns 429 NESTING_BUSY; timeouts are 504 NESTING_TIMEOUT; engine unavailable / re-check failure returns 503 NESTING_UNAVAILABLE / NESTING_VERIFY_FAILED. Read error.message/details to fix input or back off and retry. The irregular sync endpoint currently has no idempotent replay — repeated successful submissions are billed independently.