Image to GLB with curl
A small Bash workflow is enough to submit an image, follow the same task until it settles, and download the resulting GLB. The API does not require a TO3D CLI and generation and rigging are separate tasks.
Verified 27 September 2026
Before you start
Create an API key in the dashboard and keep it in an environment variable. The image URL must be reachable by the service; image-to-3d accepts a URL or base64 image and supports PNG, JPEG, and GIF input up to 128 MB.
- Use the x-api-key header for every request.
- A generation task returns 202 and moves from pending to processing to succeeded, failed, or canceled.
- A failed or canceled task is refunded automatically when the provider does not complete it.
export TO3D_KEY='your_api_key'
export TO3D_IMAGE_URL='https://example.com/photo.png'
BASE='https://api.to3d.app'Submit one image
Post the image URL to Trellis. Save the returned id before doing anything else; polling and downloading use that id and do not create another paid task.
- Send task_type image-to-3d.
- Put the image URL under input.image.
- Treat the response id as opaque and keep it exactly as returned.
TASK_JSON=$(curl -sS --fail-with-body "$BASE/api/trellis/tasks" \
-H "x-api-key: $TO3D_KEY" -H 'Content-Type: application/json' \
-d "$(jq -cn --arg image "$TO3D_IMAGE_URL" '{task_type: "image-to-3d", input: {image: $image}}')")
TASK_ID=$(jq -er '.id' <<<"$TASK_JSON")
printf 'Task: %s\n' "$TASK_ID"Poll and download the GLB
Poll the task endpoint with the same task id. Stop on succeeded, failed, or canceled. A successful response contains output.model_file, a durable URL that can be downloaded without the API key.
- Wait five seconds between checks and cap the loop at a limit that suits your job.
- Do not submit again when a loop times out; check the original task id later.
- Check the first four bytes of the file for glTF before handing it to a viewer or engine.
MODEL_URL=''
for ((attempt = 0; attempt < 120; attempt++)); do
STATUS_JSON=$(curl -sS --fail-with-body "$BASE/api/trellis/tasks/$TASK_ID" -H "x-api-key: $TO3D_KEY")
STATUS=$(jq -er '.status' <<<"$STATUS_JSON")
case "$STATUS" in
succeeded) MODEL_URL=$(jq -er '.output.model_file' <<<"$STATUS_JSON"); break ;;
failed|canceled) jq '{id,status,error}' <<<"$STATUS_JSON" >&2; exit 1 ;;
pending|processing) sleep 5 ;;
esac
done
[ -n "$MODEL_URL" ] || { echo "Timed out; keep the task id" >&2; exit 1; }
curl -fsSL "$MODEL_URL" -o model.glb
[ "$(head -c 4 model.glb)" = glTF ]Optionally rig the result
Rigging is a second request against the downloaded GLB. Create a Skin Tokens task only when you need a humanoid skeleton, then poll it using the same task pattern.
- Send task_type rig to /api/skin_tokens/tasks.
- Set input.model to the GLB URL or a base64 GLB and choose original, mixamo, or ue5 bone names.
- The rig endpoint returns output.model_file after its own task finishes.
curl -sS --fail-with-body "$BASE/api/skin_tokens/tasks" \
-H "x-api-key: $TO3D_KEY" -H 'Content-Type: application/json' \
-d '{"task_type":"rig","input":{"model":"https://storage.to3d.app/tasks/example/model.glb","bone_names":"mixamo"}}'Failures and timeouts
A transient 503 or a local polling timeout is not a reason to create a second task. Query the original id first; the task record is the source of truth for status and error details.
- Retry transient HTTP requests with backoff while keeping the same task id.
- A 400 means the request shape or input is invalid; a 401 means the key is missing or invalid.
- A 404 task response is scoped to the account that created the task.
curl -sS "$BASE/api/trellis/tasks/$TASK_ID" -H "x-api-key: $TO3D_KEY" | jq '{id,status,output,error}'