Thrixel API

Download

Unified download endpoint and async format conversion.

GLB is produced when a submission completes. Other formats are converted on-demand: either inline via the unified /download endpoint (which waits for the result), or asynchronously via the /convert job API.


Download a submission asset

GET /api/v1/{submission_id}/download

Public.

Unified endpoint for all downloadable artifacts. GLB is always ready; FBX/OBJ/STL/USDZ trigger an on-demand conversion if not yet cached.

Path Parameters

NameTypeDescription
submission_idstring (required)UUID of the submission.

Query Parameters

NameTypeDescription
formatstringAsset format. Values: glb, fbx, obj, stl, usdz, thumbnail, reference_image. Default: glb.
texturesstringGLB only. png (default) or webp.
dracobooleanGLB only. Draco geometry compression. Default: false.
qualitystringGLB only. auto (default), full or lite. See Lite and Full.
targetstringThe engine the file is for. With format=fbx: generic (default), roblox, unity or ue5, see FBX for an engine. With format=glb: roblox, see GLB for Roblox.
texture_sizeinteger512, 1024 or 2048: cap every texture's longest side at that many pixels. Any format with textures; ignored for stl. See Texture size.
skeletonbooleanFBX only. false drops a rigged model's skeleton and skin weights, keeping its rest pose. Default: true.
indexintegerIndex for reference_image format (0–5). Default: 0.

Example Request

GLB is served straight from storage:

GET /a1b2c3d4-.../download?format=glb
curl -L -o model.glb https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=glb

Any other format triggers a conversion, so the first request for it may take a few seconds:

GET /a1b2c3d4-.../download?format=fbx
curl -L -o model.fbx https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=fbx

Lite and Full

Some models are ready to look at before their full surface detail is: the job completes with a lightweight build first, and the full one follows a few minutes later. While it is on its way, GET /{submission_id}/info reports full_build_status: "pending"; once it is ready (or same, when the two builds are identical) the full model is what every download gets.

qualityWhat you get
auto (default)The full model when it is ready, the lightweight one until then.
fullThe full model. While it is still being prepared the response is 202 with a Retry-After header; retry after that many seconds.
liteA lightweight file with simplified surface colors, available as soon as the job completes.

Formats other than GLB, and any texture_size, are always made from the full model: while it is still being prepared they answer 202 with a Retry-After header too, whatever quality says. To be sure you save the final file, ask for quality=full and retry on 202: right after a job completes, auto can still hand you the lightweight file.

Treat the lightweight file as a placeholder. Swap it for the full download once that is ready, and do not change its geometry on your side: the full build cannot be laid over those edits. Make shape changes through the API instead (Edit, Transform, Decimate); each result gets its own full build.

You do not have to wait before building on the model. Edits, Detail, Texture, Decimate and Transform start straight away. Rig and Group Parts keep the model's surface as it is, so when submitted while its full build is on the way they are accepted as usual and start by themselves once it is ready: their info shows waiting_for_full_build: true until then. Keep polling; do not resubmit. To start them at once instead, send wait_for_full_build: false: they run on the lightweight build, and their result then has a full build of its own on the way (full_build_status: "pending"), which lands by itself shortly after the model's.

Texture size

Pick the texture resolution when you download: texture_size=512, 1024 or 2048 caps the longest side of every texture at that many pixels, in GLB, FBX, OBJ and USDZ. Geometry, rigs and animations are unchanged. It only ever shrinks, so asking for a size at or above the model's own returns the model as it is. STL carries no textures and ignores it.

GET /a1b2c3d4-.../download?format=fbx&texture_size=1024
curl -L -o model.fbx https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=fbx&texture_size=1024

The first request for a size builds it and answers 202 with the conversion job; retry and the file is served from then on. It is free.

This replaces Rebake (POST /api/v1/texture/rebake), which is retired.

Shipping a GLB to a browser

If the model is going into a web page or a browser game, ask for WebP textures:

GET /a1b2c3d4-.../download?format=glb&textures=webp
curl -L -o model.glb https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=glb&textures=webp

Measured at export on real submissions:

ModelDefault (png)textures=webp+ draco=true
Geometry-heavy (15% textures)7.00 MB6.28 MB1.18 MB
Texture-heavy (100% textures)18.33 MB0.95 MB0.91 MB

Textures are where most of the weight is on most models, and WebP is a plain image format the browser decodes natively - nothing extra to install, and a reader that cannot handle it fails visibly. This is the setting to reach for first. Thrixel's own web viewer serves exactly this: textures=webp, no Draco.

When to add Draco

draco=true compresses the geometry rather than the textures, so it only pays off on models that are mostly geometry - the first row above, where it turns 6.28 MB into 1.18 MB. On a texture-heavy model it buys about 4%.

Draco needs a decoder on the other end

KHR_draco_mesh_compression is read by three.js (with DRACOLoader), Babylon, model-viewer and Blender 4.x, but three.js fetches a ~200KB WASM decoder at runtime and you have to wire it up. A reader without a Draco decoder usually does not raise an error - it returns the mesh with every vertex at the origin. Do not hand a Draco file to an unknown tool, and do not feed one back into a pipeline of your own.

Both flags are independent, and neither changes the model: same parts, same triangle count, same bounds.

GLB for Roblox

Roblox gives a MeshPart one mesh and one texture and ignores a material's flat colour, so a model made of many single-colour parts imports grey. Add target=roblox to a GLB download for a file Roblox imports in colour:

  • Each top-level part group (a door, a wheel, a rotor) becomes one mesh, so moving parts stay separate; loose parts merge into the body.
  • Every part's colour, and any texture, is painted into one colour texture per mesh, at most 1024 px.
  • See-through glass is removed: Roblox would draw it as a solid panel.
  • No mesh is over 20,000 triangles; a bigger one is split into pieces. If the whole model is over your budget, reduce its triangles first (Reduce in the app, thrixel_reduce_triangles over MCP).

It carries colour only, with no normal, roughness or glow maps. Import the .glb with Studio's 3D Importer as-is. For a humanoid that needs an R15 rig, use the FBX instead.

GET /a1b2c3d4-.../download?format=glb&target=roblox
curl -L -o model-roblox.glb https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=glb&target=roblox

FBX for an engine

Every FBX embeds its textures, carries metallic and roughness maps, and writes no extra end bones. Any mesh over 20,000 triangles is split into pieces under that, so a high-detail model still imports into engines with a per-mesh limit. Add target to shape the file for one engine:

targetWhat changes
genericNothing beyond the above.
robloxA .zip: the FBX as a Roblox R15 rig, textures at 1024 px, and a textures/ folder with each material's ColorMap, NormalMap, RoughnessMap and MetalnessMap PNGs for a SurfaceAppearance. See Roblox R15.
unityBones renamed for Unity's Humanoid avatar (Hips, LeftUpperLeg...).
ue5Bones renamed to the UE5 Mannequin (pelvis, thigh_l...), with a root bone above pelvis.

Bone names only change on a humanoid Rig; any other model gets the same file as generic, except that roblox still applies its texture size and maps.

GET /a1b2c3d4-.../download?format=fbx&target=roblox
curl -L -o model-roblox.zip https://api.thrixel.com/api/v1/a1b2c3d4-.../download?format=fbx&target=roblox

Roblox R15

On a humanoid Rig, target=roblox rebuilds the skeleton to Roblox's R15 layout:

  • 15 bones (LowerTorso, UpperTorso, Head, and Upper/Lower/Hand and Upper/Lower/Foot on each side) under Root > HumanoidRootNode.
  • Joints R15 does not have fold into the bone they sit on, weights included: the spine and clavicles into UpperTorso, the neck into Head, fingers into the hand, toes into the foot.
  • LowerTorso and Root at the origin, at most 4 bones per vertex, none on Root.
  • One mesh per body part, named after its bone (LeftUpperArm_Geo...). A part over 20,000 triangles is still split, so it arrives as two meshes.

The advanced R15 bones (fingers, extra spine joints) and the avatar attachments are not included.

Importing into Roblox Studio. Roblox does not build materials from an FBX, so a model imported on its own shows grey. For a model without a rig, the GLB for Roblox imports in colour with no setup. Import the .fbx with the 3D Importer, then for each MeshPart add a SurfaceAppearance and set its ColorMap, NormalMap, RoughnessMap and MetalnessMap from the matching PNGs in the zip's textures/ folder (each file is named after its material). SurfaceAppearance.txt in the zip lists, per material, the MeshParts that use it and the files to set.

Glowing parts. A part that glows in the model (a lamp, a headlight, a neon sign) comes with an EmissiveMask PNG, a flat ColorMap, and an EmissiveStrength and EmissiveTint in SurfaceAppearance.txt: set EmissiveMaskContent, EmissiveStrength and EmissiveTint on that part's SurfaceAppearance. This covers models made with Create and Edit; Sculptor and Detailer results do not carry a glow yet.

On this page