API reference
REST API reference for low-poly.com: low-poly generation methods, 3D model conversion, polygon reduction, LOD generation, mesh inspection and asset downloads, with examples, auth and rate limits.
On this page
Getting started
Generation
Assets
- GET/assets
- GET/assets/category/{slug}
- GET/assets/categories
- GET/assets/{slug}
- POST/assets/{slug}/download
Tools
- POST/tools
- POST/tools/reduce
- POST/tools/convert
- POST/tools/lod
- POST/tools/pivot
- POST/tools/orient
- POST/tools/batch
Account
Reference
Overview
REST over HTTPS. Tool endpoints are synchronous: upload a model and the response body is the result.
Generation endpoints are asynchronous and direct — one endpoint per output type: send an
input (a file or a URL), start a job, poll its status, then download the returned artifacts.
Chain steps by feeding the output[].url from one call into the next call's
image_url / mesh_url — there's no project or artifact id to manage.
Base URL: https://low-poly.com/api/v1
| Endpoint | Purpose | Returns | Cost |
|---|---|---|---|
POST /image | Generate concept images from a prompt (or a variation of a reference image) | application/json | per candidate |
POST /mesh | Turn an image into a 3D low-poly mesh | application/json | per candidate |
POST /texture | Paint a texture onto a mesh using a reference image | application/json | per candidate |
GET /generations/{id} | Poll generation status | application/json | free |
GET /artifacts/{token}/raw | Download a generated artifact | binary | free |
GET /assets | Search low-poly asset catalogue | application/json | free |
POST /assets/{slug}/download | Download a gallery asset (subscribers) | binary | daily plan quota |
POST /tools | Run any model tool by method | binary or zip | varies by method |
POST /tools/reduce | Decimate a mesh | binary (mesh) | 1 credit |
POST /tools/convert | Format swap | binary (mesh) | 1 credit |
POST /tools/lod | LOD levels bundle | application/zip | 1 / level |
POST /tools/pivot | Recenter pivot | binary (mesh) | 1 credit |
POST /tools/orient | Rotate / re-orient | binary (mesh) | 1 credit |
POST /tools/batch | Process many files | application/zip | 1 / success |
GET /me | Account info | application/json | free |
Endpoint paths on this page are shown relative to the base URL. For example, /tools resolves to https://low-poly.com/api/v1/tools.
Authentication
Send your key as a bearer token in the Authorization header.
Create and rotate keys on your API keys page.
Missing or invalid keys return 401 unauthorized.
Access and billing
Every account can create a key and call every endpoint — there is no plan that unlocks the API and no plan that closes it. What the plan decides is who pays for the generations, and the API is metered separately from the browser:
-
Included allowanceStudio plan
A fixed number of API generations per calendar month, counted separately from anything you do in the browser. Calls keep working past the allowance and fall back to spending credits from the account balance. See pricing for the current figures.
-
Metered in creditsevery other plan
Each generation spends credits from the account balance, exactly as a browser generation used to. A plan that advertises unlimited generations means unlimited in the browser; it grants no API allowance, and its API calls spend credits like any other.
Mesh tools, conversions, status polls, artifact downloads and /me
are unaffected by any of this: tools cost their usual credits on every plan,
and reads are free.
A generation response carries the caller’s remaining allowance, both as
headers and as an api_quota object in the body — emitted only
when the plan actually includes one. Watch
X-Api-Quota-Remaining to back off before you start paying
overage; expect 402 insufficient_credits when the allowance is
gone and the balance cannot cover the call either.
Authorization: Bearer lp_live_xxxxxxxxxxxxxxxxxxxxxxxx
X-Api-Quota-Limit: 1000
X-Api-Quota-Used: 412
X-Api-Quota-Remaining: 588
Supported formats
Maximum upload size is 100 MB per file. The output format defaults to the input extension when omitted.
| Extension | Name | Read | Write |
|---|---|---|---|
glb | GLB | yes | yes |
gltf | glTF | yes | yes |
obj | OBJ | yes | yes |
fbx | FBX | yes | yes |
3ds | 3DS | yes | yes |
stl | STL | yes | yes |
blend | Blender | yes | — |
POST /image
Generates low-poly concept image candidates from a text prompt (txt2img), or a variation of a reference image (img2img).
Content-Type: application/json or multipart/form-data (for img2img's reference image).
Request parameters
- promptstringrequired
What to generate. Max 2000 characters.
- countinteger 1-8optional default:
1Number of candidates.
- modestringoptional default:
txt2imgtxt2imgorimg2img. - image / image_urlbinary or stringoptional
Reference image, required when
mode=img2img. Provide the file as multipartimage, or a public URL asimage_url— not both. - strengthnumber 0.05-1.0optional default:
0.6How much the img2img output may drift from the reference.
Response
201 Created returns the generation: id, status, current progress, and output (empty until the job completes — poll /generations/{id}).
curl -X POST https://low-poly.com/api/v1/image \
-H "Authorization: Bearer lp_live_..." \
-H "Content-Type: application/json" \
-d '{
"prompt": "low-poly modular sci-fi crate, game asset, clean silhouette",
"count": 4
}'
curl -X POST https://low-poly.com/api/v1/image \
-H "Authorization: Bearer lp_live_..." \
-F "prompt=same crate, weathered metal" \
-F "mode=img2img" \
-F "image=@crate-concept.png"
POST /mesh
Turns an image into a 3D low-poly mesh. Bring your own image, or chain the output[0].url from a prior /image call.
Content-Type: application/json or multipart/form-data.
Request parameters
- image / image_urlbinary or stringrequired
The source image — multipart
image, or a public URL asimage_url. Exactly one of the two. - countinteger 1-8optional default:
1Number of mesh candidates.
Response
Same shape as /image — poll /generations/{id} for the resulting mesh(es).
curl -X POST https://low-poly.com/api/v1/mesh \
-H "Authorization: Bearer lp_live_..." \
-F "image=@product-photo.jpg"
curl -X POST https://low-poly.com/api/v1/mesh \
-H "Authorization: Bearer lp_live_..." \
-H "Content-Type: application/json" \
-d '{ "image_url": "https://low-poly.com/api/v1/artifacts/64_hex_artifact_token/raw" }'
POST /texture
Paints a texture onto a mesh, using a reference image for appearance. Both a mesh and an image are required — there's no project lineage to infer one from the other.
Content-Type: application/json or multipart/form-data.
Request parameters
- mesh / mesh_urlbinary or stringrequired
The mesh to texture — multipart
mesh, or a public URL asmesh_url. - image / image_urlbinary or stringrequired
The reference image — multipart
image, or a public URL asimage_url. - countinteger 1-8optional default:
1Number of texture candidates.
Response
Same shape as /image — poll /generations/{id} for the textured mesh.
curl -X POST https://low-poly.com/api/v1/texture \
-H "Authorization: Bearer lp_live_..." \
-F "mesh=@crate.glb" \
-F "image=@crate-concept.png"
curl -X POST https://low-poly.com/api/v1/texture \
-H "Authorization: Bearer lp_live_..." \
-H "Content-Type: application/json" \
-d '{
"mesh_url": "https://low-poly.com/api/v1/artifacts/64_hex_mesh_token/raw",
"image_url": "https://low-poly.com/api/v1/artifacts/64_hex_image_token/raw"
}'
GET /generations/{id}
Polls and refreshes a generation. When status becomes success, download each item in output[].url.
curl https://low-poly.com/api/v1/generations/918 \
-H "Authorization: Bearer lp_live_..."
{
"data": {
"id": 918,
"kind": "mesh",
"status": "success",
"output": [
{
"url": "/api/v1/artifacts/64_hex_artifact_token/raw",
"mime_type": "model/gltf-binary",
"size_bytes": 812433
}
],
"credits_consumed": 10,
"credits_remaining": 4810
}
}
GET /artifacts/{token}/raw
Downloads a generated image, model or texture artifact. The artifact token is non-enumerable and still checked against the bearer key's user.
curl https://low-poly.com/api/v1/artifacts/64_hex_artifact_token/raw \
-H "Authorization: Bearer lp_live_..." \
-o generated-model.glb
GET /assets
Search the public low-poly asset catalogue. Results include price, polycount, preview URL and web URL.
Query parameters
- qstringoptional
Search text.
- tagstringoptional
Tag slug.
- sortstringoptional default:
recentOne of
recent,popular,cheap,name. - pageintegeroptional default:
1Page number.
- per_pageinteger 1-96optional default:
24Items per page.
To filter by category, use the API endpoint /assets/category/{slug} below rather than a query parameter. Public gallery category pages use /assets/browse/category/{slug}.
curl "https://low-poly.com/api/v1/assets?q=crate&sort=popular"
GET /assets/category/{slug}
Same response shape as /assets, scoped to one category. Returns 404 if the category slug doesn't exist.
Query parameters
- qstringoptional
Search text.
- tagstringoptional
Tag slug.
- sortstringoptional default:
recentOne of
recent,popular,cheap,name. - pageintegeroptional default:
1Page number.
- per_pageinteger 1-96optional default:
24Items per page.
curl "https://low-poly.com/api/v1/assets/category/vehicles?sort=popular"
GET /assets/categories
Lists asset categories. No authentication required.
curl https://low-poly.com/api/v1/assets/categories
GET /assets/{slug}
Returns metadata for one published asset: description, category, tags, gallery media, available formats and download endpoint.
curl https://low-poly.com/api/v1/assets/low-poly-crate
POST /assets/{slug}/download
Downloads a gallery asset. Requires an active subscription on the key owner's account; each download counts against the plan's daily allowance (the X-Downloads-Remaining response header tells you what's left). Pass format as a query or form field.
Request parameters
- formatstringoptional default:
glbOne of the formats returned by
/assets/{slug}, usuallyglb,fbxorobj.
curl -X POST "https://low-poly.com/api/v1/assets/low-poly-crate/download?format=glb" \
-H "Authorization: Bearer lp_live_..." \
-o low-poly-crate.glb
POST /tools
Run the low-poly tool suite through a single programmable endpoint. Pass method to choose the operation; the method-specific endpoints below remain available for fixed integrations.
Content-Type: multipart/form-data
Request parameters
- methodstringrequired
One of
reduce,convert,lod,pivot,orient,batch. Aliases likesimplify,decimate,format,recenterandrotateare accepted. - filebinaryoptional
Input mesh for single-file methods. Required for
reduce,convert,lod,pivotandorient. - files[]binary[]optional
Input meshes for
batch. - ratio, ratios, format, axismixedoptional
Use the same method-specific fields documented in the dedicated sections below.
Response
The response is the same as the selected method: a mesh binary, a ZIP archive, or a structured API error.
curl -X POST https://low-poly.com/api/v1/tools \
-H "Authorization: Bearer lp_live_..." \
-F "method=reduce" \
-F "file=@tree.glb" \
-F "ratio=0.25" \
-F "format=glb" \
-o tree-low.glb
POST /tools/reduce
Decimate a mesh while preserving its silhouette.
Content-Type: multipart/form-data
Request parameters
- filebinaryrequired
Input mesh. The extension must match a supported format.
- rationumber 0.05–1.0optional default:
0.25Fraction of triangles to keep.
0.25= keep 25%. - formatstringoptional default:
input formatOutput format. One of
glb,gltf,obj,fbx,3ds.
Response
200 OK — application/octet-stream, body is the processed mesh.
- X-Tris-In
- Input triangle count
- X-Tris-Out
- Output triangle count
- X-Credits-Cost
- Credits charged for this call
- X-Credits-Remaining
- Credits left on the account
- X-RateLimit-*
- Standard rate-limit headers (see Rate limits)
curl -X POST https://low-poly.com/api/v1/tools/reduce \
-H "Authorization: Bearer lp_live_..." \
-F "file=@dragon.glb" \
-F "ratio=0.25" \
-F "format=glb" \
-o dragon-low.glb
Content-Type: application/octet-stream
Content-Disposition: attachment; filename="dragon-low.glb"
X-Tris-In: 184502
X-Tris-Out: 46125
X-Credits-Cost: 1
X-Credits-Remaining: 4819
POST /tools/convert
Format swap, no decimation. Same fields as /tools/reduce; ratio is forced to 1.0.
Content-Type: multipart/form-data
Request parameters
- filebinaryrequired
Input mesh.
- formatstringrequired
Target format. Must differ from the input to be useful.
Response
200 OK — application/octet-stream, body is the converted mesh. Same headers as /tools/reduce.
curl -X POST https://low-poly.com/api/v1/tools/convert \
-H "Authorization: Bearer lp_live_..." \
-F "file=@character.fbx" \
-F "format=glb" \
-o character.glb
POST /tools/lod
Generate several decimated levels from one model in a single call. Returns a ZIP, one file per ratio.
Content-Type: multipart/form-data
Request parameters
- filebinaryrequired
Input mesh.
- ratioscsv (0<r≤1)optional default:
0.6,0.3,0.15,0.05Comma-separated list of ratios, one per level. Clamped to
[0.005, 1.0]. - formatstringoptional default:
input formatOutput format applied to every level.
Response
200 OK — application/zip. Entries are named {stem}-lod{i}-r{ratio}.{ext}.
- X-Lod-Levels
- Number of LOD levels in the archive
- X-Credits-Cost
- Credits charged (1 per level)
- X-Credits-Remaining
- Credits left on the account
curl -X POST https://low-poly.com/api/v1/tools/lod \
-H "Authorization: Bearer lp_live_..." \
-F "file=@tree.glb" \
-F "ratios=0.6,0.3,0.15,0.05" \
-F "format=glb" \
-o tree-lod.zip
tree-lod0-r0.60.glb
tree-lod1-r0.30.glb
tree-lod2-r0.15.glb
tree-lod3-r0.05.glb
POST /tools/pivot
Recenter a model's pivot point.
Content-Type: multipart/form-data
Request parameters
- filebinaryrequired
Input mesh.
- modestringoptional default:
centerOne of
center(bounding-box center),origin(world origin),ground(XZ-centered, Y at minimum). - formatstringoptional default:
input formatOutput format.
Response
200 OK — application/octet-stream. X-Pivot-Mode echoes the applied mode; X-Credits-Cost / X-Credits-Remaining report billing.
curl -X POST https://low-poly.com/api/v1/tools/pivot \
-H "Authorization: Bearer lp_live_..." \
-F "file=@prop.glb" \
-F "mode=ground" \
-o prop-ground.glb
POST /tools/orient
Rotate or re-orient a model. The rotation is baked into the vertex positions — the resulting file imports identity-rotated.
Content-Type: multipart/form-data
Request parameters
- filebinaryrequired
Input mesh.
- presetstringoptional default:
y-up-to-z-upOne of
y-up-to-z-up,z-up-to-y-up,rotate-x-90,rotate-x-180,rotate-x-270,rotate-y-90,rotate-y-180,rotate-y-270,rotate-z-90,rotate-z-180,rotate-z-270,flip-x,flip-y,flip-z,custom. - rxnumber -360..360optional default:
0X Euler angle in degrees. Only used when
preset=custom. - rynumber -360..360optional default:
0Y Euler angle in degrees. Only used when
preset=custom. - rznumber -360..360optional default:
0Z Euler angle in degrees. Only used when
preset=custom. Applied in XYZ order. - formatstringoptional default:
input formatOutput format.
Response
200 OK — application/octet-stream. X-Orient-Preset echoes the applied preset; X-Credits-Cost / X-Credits-Remaining report billing.
curl -X POST https://low-poly.com/api/v1/tools/orient \
-H "Authorization: Bearer lp_live_..." \
-F "file=@asset.glb" \
-F "preset=z-up-to-y-up" \
-o asset-yup.glb
curl -X POST https://low-poly.com/api/v1/tools/orient \
-H "Authorization: Bearer lp_live_..." \
-F "file=@asset.glb" \
-F "preset=custom" \
-F "rx=0" -F "ry=90" -F "rz=0" \
-o asset-rot.glb
POST /tools/batch
Process many files in a single request. Returns a ZIP, one decimated file per input. Files that fail processing are skipped from the ZIP and reported in X-Batch-Error-List; you are billed only for successful files.
Content-Type: multipart/form-data
Request parameters
- files[]binary (repeated)required
Up to 500 files per batch.
- rationumber 0.05–1.0optional default:
0.25Decimation ratio applied to every file.
Response
200 OK — application/zip named batch.zip. Each entry is {originalName}-low.{ext}.
- X-Batch-Count
- Files submitted
- X-Batch-Succeeded
- Files that processed successfully
- X-Batch-Errors
- Files that failed processing
- X-Batch-Error-List
- JSON array of
{name, error}(empty when 0) - X-Credits-Cost
- Credits charged (1 per successful file)
- X-Credits-Remaining
- Credits left on the account
curl -X POST https://low-poly.com/api/v1/tools/batch \
-H "Authorization: Bearer lp_live_..." \
-F "files[]=@rock_01.obj" \
-F "files[]=@rock_02.obj" \
-F "files[]=@rock_03.obj" \
-F "ratio=0.3" \
-o rocks-low.zip
GET /me
Inspect the authenticated account: user, credit balance, key id. Does not consume credits.
Response
200 OK — application/json
curl https://low-poly.com/api/v1/me \
-H "Authorization: Bearer lp_live_..."
{
"user": {
"id": 42,
"email": "you@studio.com",
"name": "You"
},
"credits": {
"balance": 4820
},
"key": {
"id": 17
}
}
Rate limits
Per-key, per-minute caps by endpoint group: generation submits (/image, /mesh, /texture) 20; /generations/{id} and /me 120; /tools/reduce and /tools/convert 60; /tools/lod, /tools/pivot and /tools/orient 30; /tools/batch 10.
The limited subject is the key, not the account: each key gets its own budget, so a runaway script on one integration cannot starve another, and a suspect key can be revoked without throttling everything else you run. Issue one key per integration.
Rate limits are per minute and apply on every plan, including plans with an included allowance — they are a burst ceiling, not a quota. The monthly allowance is a separate thing entirely; see Access and billing.
Hitting a cap returns 429 rate_limited. Some tool endpoints additionally include X-RateLimit-* headers in their responses; treat the 429 body as the source of truth.
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 58
X-RateLimit-Reset: 1763251200
Errors
All errors return JSON with a stable code string. Branch on code; message is for humans.
| Status | Code | When |
|---|---|---|
400 | missing_file | No file field, or upload error |
400 | missing_input | /image, /mesh, /texture: neither the file field nor its *_url counterpart was provided |
400 | invalid_input | /image, /mesh, /texture: both the file field and its *_url counterpart were provided |
400 | invalid_url | An *_url field isn't an http(s) URL |
400 | unsupported_format | Extension not in the supported list |
400 | bad_mode | /tools/pivot: invalid mode value |
400 | missing_files | /tools/batch: no files |
400 | too_many | /tools/batch: more than 500 files |
401 | unauthorized | Missing or invalid bearer token |
402 | insufficient_credits | Nothing can pay for the call: the plan’s included API allowance is used up (or the plan has none) and the credit balance is too low |
402 | subscription_required | /assets/{slug}/download: no active subscription on the account |
429 | download_limit_reached | /assets/{slug}/download: the plan's daily download allowance is used up |
404 | not_found | Generation or artifact not owned by this key |
413 | file_too_large | Upload exceeds the 100 MB limit |
415 | content_mismatch | /image, /mesh, /texture: file contents don't match the declared/detected extension. Tool endpoints report the same situation as 400 unsupported_format |
422 | fetch_failed | An *_url field could not be fetched (unreachable, too large, not public) |
429 | rate_limited | Per-minute cap exceeded |
500 | processing_failed | Mesh pipeline raised an error; message carries the detail |
{
"error": {
"code": "rate_limited",
"message": "Too many requests"
}
}
CORS
By default, cross-origin browser access is restricted to this site's own origin — Access-Control-Allow-Origin is never * unless the operator explicitly opts in. Preflight OPTIONS requests are answered on every /api/v1/* path. Server-side integrations (curl, SDKs, backends) are unaffected by CORS; if you need to call the API from a browser app on another domain, proxy the calls through your own backend so your key stays secret anyway.