Base URL: https://polyvia-ai.com/api/v1. Generations run asynchronously: create one, then poll it or stream its progress until it lands on a terminal status.
Authentication
Generate a key in Settings → API keys. Keys have the form pv_live_… and you'll see each one only once — we keep just a hash. Pass it in the X-API-Key header with every request. Revoked keys, and keys belonging to suspended accounts, are refused.
curl https://polyvia-ai.com/api/v1/me \
-H "X-API-Key: $POLYVIA_API_KEY"Create a generation
POST/api/v1/generations
Reserves the quoted credits right away and queues the job. On success you pay only what the generation actually used (never above the quote; any difference is returned automatically); if it fails, you get everything back. Pricing: text 9 (untextured) / 17 (textured); image 25; low-poly 34; +8 for HD (4K) textures, +17 for 8K textures, +17 for ULTRA geometry, +5 for the T-pose pre-step, +5 for each additional export format.
| Field | Type | Description |
|---|---|---|
| type | "text" | "image" | Required. |
| prompt | string | Needed for text. Between 3 and 1024 characters. |
| image_url | string (URL) | Needed for image. A public http(s) link to a JPEG, PNG or WebP of at most 10 MB. We download it ourselves; if that fails, the generation fails and is refunded. |
| options.model | "STANDARD" | "LOWPOLY" | "LOWPOLY_PRO" | STANDARD by default. LOWPOLY = low poly ready for games, LOWPOLY_PRO = low poly that supports quads (preview). |
| options.quality | "FAST" | "STANDARD" | "ULTRA" | STANDARD by default. ULTRA = finely detailed geometry (STANDARD model only); FAST = quicker texturing. |
| options.texture | "NONE" | "STANDARD" | "PBR" | Default STANDARD. |
| options.texture_quality | "STANDARD" | "HD" | "EXTREME" | STANDARD by default. HD = 4K maps, EXTREME = 8K maps. Has no effect when texture is NONE. |
| options.texture_4k | boolean | Legacy alias for texture_quality HD; deprecated. |
| options.polycount | integer | Desired triangle count, 500–2,000,000 (over 1,500,000 requires ULTRA; low-poly models have a lower cap). Leave out for adaptive. |
| options.quad | boolean | false by default. Quad topology; you receive the model as FBX. |
| options.smart_low_poly | boolean | false by default. Low poly with a hand-modelled look (STANDARD model), max 20,000 faces. |
| options.seed | integer | Optional. Reusing a seed with identical input reproduces the same mesh. |
| options.formats | array | Choose any of GLB FBX OBJ USDZ STL. Defaults to ["GLB"]. |
| options.negative_prompt | string | Text generations only, max 255 characters. |
| options.t_pose | boolean | Image generations only. false by default. Puts the subject into a T-pose before modelling (useful for rigging). |
| options.remove_background | boolean | Still accepted for compatibility, but has no effect: the subject is always isolated for you. |
| options.parent_asset_id | string | Remix source: any asset you own, or any public one. |
curl -X POST https://polyvia-ai.com/api/v1/generations \
-H "X-API-Key: $POLYVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"type": "text",
"prompt": "a weathered bronze viking helmet with horns",
"options": { "texture": "PBR", "formats": ["GLB", "USDZ"] }
}'curl -X POST https://polyvia-ai.com/api/v1/generations \
-H "X-API-Key: $POLYVIA_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "type": "image", "image_url": "https://example.com/chair.png" }'{ "id": "cmf0x8k2p0001l50g7h3d9q2a", "status": "QUEUED", "credits_charged": 15, "credits_remaining": 485 }Get a generation
GET/api/v1/generations/:id
status takes one of QUEUED RUNNING SUCCEEDED FAILED CANCELED. type takes one of text image multiview texture retopology rig animate convert (studio steps are listed here as well). Once it is SUCCEEDED, files contains signed download URLs that last one hour — fetch the generation again to get new ones.
curl https://polyvia-ai.com/api/v1/generations/cmf0x8k2p0001l50g7h3d9q2a \
-H "X-API-Key: $POLYVIA_API_KEY"{
"id": "cmf0x8k2p0001l50g7h3d9q2a",
"type": "text",
"status": "SUCCEEDED",
"progress": 100,
"error": null,
"credits_charged": 15,
"created_at": "2026-09-21T10:00:00.000Z",
"started_at": "2026-09-21T10:00:01.000Z",
"finished_at": "2026-09-21T10:01:32.000Z",
"asset": { "id": "…", "slug": "a-weathered-bronze-viking-helmet-9q2a0b1c", "title": "A weathered bronze viking helmet with horns", "thumbnail_url": "https://…" },
"files": [
{ "format": "GLB", "bytes": 4812201, "url": "https://…", "expires_at": "2026-09-21T11:05:00.000Z" }
]
}Stream progress (SSE)
GET/api/v1/generations/:id/stream
Returns a text/event-stream. You get the current state straight away, then each change as it happens. Every event is one data: line carrying JSON; lines beginning with : are heartbeats, sent every 15 seconds. We close the stream once a terminal status is reached. Each stream you open uses one request of your rate limit.
curl -N https://polyvia-ai.com/api/v1/generations/cmf0x8k2p0001l50g7h3d9q2a/stream \
-H "X-API-Key: $POLYVIA_API_KEY"data: {"jobId":"cmf0x8k2p0001l50g7h3d9q2a","status":"RUNNING","progress":42,"assetId":null,"error":null}
: ping
data: {"jobId":"cmf0x8k2p0001l50g7h3d9q2a","status":"SUCCEEDED","progress":100,"assetId":"cmf0x9…"}| Field | Type | Description |
|---|---|---|
| jobId | string | The generation id. |
| status | string | QUEUED | RUNNING | SUCCEEDED | FAILED | CANCELED |
| progress | integer | 0–100. |
| assetId | string | null | Set on SUCCEEDED. |
| error | string | null | Plain-language reason, present when FAILED. |
Account
GET/api/v1/me
curl https://polyvia-ai.com/api/v1/me -H "X-API-Key: $POLYVIA_API_KEY"{ "id": "…", "email": "you@example.com", "plan": "PRO", "credits": 475, "max_active_jobs": 3, "active_jobs": 1 }Rate limits
Every key gets 60 requests per minute (sliding window). Each response includes X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). Go over and you receive 429 plus a Retry-After header in seconds. On top of that, your plan limits how many generations may be queued or running at the same time (max_active_jobs in /me).
Errors
All errors share one shape:
{ "error": { "code": "insufficient_credits", "message": "This generation costs 25 credits." } }Error codes never change. The message field is in English regardless of the language you view this site in.
| Code | HTTP status | Description |
|---|---|---|
| invalid_request | 400 | Validation failed on the body or parameters. The message tells you which field. |
| unauthorized | 401 | X-API-Key is absent, malformed or revoked. |
| insufficient_credits | 402 | Your balance doesn't cover this generation. |
| forbidden | 403 | The account is suspended, or you don't own this resource. |
| not_found | 404 | That generation (or endpoint) doesn't exist. |
| too_many_active_jobs | 409 | You've hit your plan's limit on simultaneous generations. Let one finish first. |
| rate_limited | 429 | More than 60 requests per minute. Check Retry-After. |
| internal_error | 500 | Something went wrong on our end. You can safely retry. |