Printy3D

Working with the API

Jobs & polling

A job runs one recipe on one ready file. You create it, follow it until it finishes, then read its result and artifacts.

Creating a job#

POST/v1/jobsjobs:write
input_file_idstringrequiredpattern ^file_[0-9A-HJKMNP-TV-Z]{26}$

A file of yours with status ready, in the same mode (test or live) as the key.

recipe"inspect" | "repair" | "tool" | "cad_check"default "inspect"
tool"measure" | "convert" | "cut" | "lattice" | "label" | "mirror" | "remove_small_parts" | "thickness" | "analyze" | "medial" | "structural.components" | "structural.fea" | "structural.heatmap" | "structural.fragility" | "structural.measure_all" | "structural.relief" | null

Required with recipe: "tool", refused otherwise.

paramsRepairParams | object | null

Repair settings for repair, the tool's parameters for tool, an optional max_credits for cad_check. inspect takes none. Unknown keys and values out of range answer 422 validation_failed with the failing fields in errors.

notifyobject

{"webhook_endpoint": "whe_…"}: the webhook to call when the job ends. cad_check only.

Send an Idempotency-Key with every create. Within 24 hours, a retry with the same key returns the job it already created instead of starting a second one, so a timeout on your side never runs or charges a job twice. The answer is 202 with the Job, usually queued.

Lifecycle#

statusMeaning
queuedWaiting for a GPU. Live jobs carry their queue position and an estimated start.
runningA worker is running the engine; stage and progress move.
verifyingThe worker has handed over its outputs; the API is verifying them.
succeededFinal. The result and the artifacts are available.
failedFinal. error_code and error_detail say why.
cancelledFinal. Cancelled before it finished; no result is published.
  • progress (0–100) and stage move while the job runs. Repairs report the engine's stages; inspect and tool jobs report a coarser progress.
  • attempt counts how often a worker took the job. If a worker is lost the job is queued again (up to three attempts) and the lost attempt is never charged.
  • analysis (inspect) and result (repair, tool) appear once the job has succeeded and its outputs passed verification.
  • usage (live jobs): held_credits while it runs, then charged_credits and charge_basis. Credits & billing

Repair stages, in order:

import transform preprocess smooth extract_decals close_holes remove_degenerate clearance thickness volume smoothing non_manifold construct reduce texture_projection clean hollow cuber export analyze

Polling#

Poll the job every two seconds or so until it reaches succeeded, failed or cancelled. Inspect jobs usually take seconds to minutes, repairs minutes; the hard limits per attempt are 3600 s for inspect, 7200 s for repair and 1800 s for a tool. An Exact Check's checker gets 290 s at most and is stopped by 300 s. Give up waiting on your side if you must, but the job keeps running: poll it again later. Exact Check jobs can call a webhook instead.

curl -sS "$API/v1/jobs/$JOB_ID" -H "Authorization: Bearer $PRINTY3D_API_KEY" \
  | jq '{status, stage, progress, queue, error_code, error_detail}'

GET /v1/jobs lists the jobs of your account and key mode, cursor-paginated (limit, cursor), when you track many at once.

Events#

Every change of status, stage or progress is a durable, ordered event {sequence, status, stage, progress, created_at}. Pass after (0 the first time, then the page's next_after) to get only the events you have not seen. Use events to draw a progress bar or a timeline; use the job itself to decide when it is done.

Queue and capacity#

Live jobs run on a fleet of rented GPUs. While a live job is queued, its queue field tells you where it stands (refreshed every 30 seconds):

queue.positioninteger | null

1 is the next job to start.

queue.estimated_start_atstring (date-time) | null

When the fleet expects to start it; absent when no GPU will take it at present.

queue.capacity.state"available" | "busy" | "exhausted"required

available: a new job should start soon. busy: jobs wait longer but will be served. exhausted: nothing starts until the reason clears.

queue.capacity.reason"paused" | "spend_limit" | "no_gpu_available" | "controller_offline" | null

Set only when exhausted.

queue.capacity.retry_after_sinteger | null

When to ask again, set only when exhausted.

When no GPU can start a live job, POST /v1/jobs answers 503 capacity_exhausted with Retry-After and a reason; nothing is queued or held. A live job that is already queued fails free, with error_code: capacity_exhausted, after 10 minutes without capacity or 24 hours in the queue.

Exact Check jobs run on their own CPU workers, not on the GPU fleet, so GPU capacity never refuses them and their queue.capacity is null. When no Exact Check worker is running, a live check gets 503 capacity_exhausted with reason: "cad_unavailable" instead, and a queued one fails free after 10 minutes without a worker.

Cancelling#

A queued job is cancelled at once; a running one is stopped and its outputs are never published. Cancelling a cancelled job is harmless; a succeeded or failed job answers 409 job_not_cancellable. A job cancelled before it started is free; one cancelled while running is charged the time it ran.

When a job fails#

error_codeMeaningCharge (live)
invalid_inputThe engine could not use the file.Minimum (10 credits)
engine_failedThe engine failed on this input (Exact Check: the checker crashed or was stopped).Run time (Exact Check: free)
engine_timeoutThe attempt ran past its time limit.Run time
invalid_resultThe outputs did not pass verification, or (Exact Check) the checker stopped on a fault of its own.Free
transfer_failedMoving files to or from the GPU failed.Free
verification_timeoutVerifying the outputs took too long.Free
lease_expiredThe worker was lost on every attempt.Free
capacity_exhaustedNo GPU (for an Exact Check, no Exact Check worker) could start the job in time.Free
time_limitExact Check only: the checker reached 290 s, or max_credits seconds when that is less.Free
memory_limitExact Check only: the file needs more memory than a check is allowed.Free

error_detail carries the worker's own explanation, for example the last lines of the engine's output. It helps when you report a problem; do not parse it.