Skip to content

Generate a model

POST/v1/models

Turns a reference image into a 3D model. Returns a job to poll, like every other long-running operation.

Request body

FieldTypeRequiredDescription
image_urlstringone ofPublicly reachable URL of the reference image
image_base64stringone ofThe image inline, base64-encoded
qualitystringnostandard (default), rapid, or pro
face_countintegernoTarget polygon budget for the result
pbrbooleannoGenerate PBR material maps

Image in, not text in

This endpoint takes an image, not a prompt. Text-to-3D internally means generating a reference image first and choosing between candidates, which is a separate operation with its own cost and its own judgement call about which candidate is right. Folding that silently into one call would spend your credits on a decision you did not get to make.

If you are starting from words, generate the reference image with whatever you already use, then pass it here. Sending a prompt returns invalid_request saying exactly this rather than failing somewhere downstream.

Response

202 Accepted

json
{
  "id": "caf57cd7-ca65-430e-9cea-9c2deba9a723",
  "object": "job",
  "operation": "generate_model",
  "status": "queued",
  "output": null,
  "error": null,
  "poll_url": "/v1/jobs/caf57cd7-ca65-430e-9cea-9c2deba9a723",
  "estimated_seconds": 60,
  "created_at": "2026-08-30T20:56:06.251Z"
}

Poll GET /v1/jobs/{id} until succeeded. Generation typically finishes in under a minute.

Output format

A successful generation returns output.model_url pointing at a ZIP archive containing the mesh and its textures, rather than a single file. That is the generator's native output and unpacking it server-side would lose the texture set.

You do not need to unpack it to keep going: POST /v1/rigs accepts ZIP directly, so chaining with from_job works without you touching the archive.

Example: image to rigged character

python
import time, requests

API = "https://api.cinevva.com/v1"
H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}

def wait(job_id, interval=6, timeout=300):
    deadline = time.time() + timeout
    while time.time() < deadline:
        job = requests.get(f"{API}/jobs/{job_id}", headers=H, timeout=30).json()
        if job["status"] == "succeeded":
            return job
        if job["status"] == "failed":
            raise RuntimeError(job["error"])
        time.sleep(interval)
    raise TimeoutError(job_id)

# 1. Reference image to 3D model
gen = requests.post(f"{API}/models", headers=H,
                    json={"image_url": "https://example.com/knight.png"}).json()
wait(gen["id"])

# 2. Rig it straight from the job — no download, no re-upload
rig = requests.post(f"{API}/rigs", headers=H,
                    json={"from_job": gen["id"], "rig_type": "biped"}).json()
print(wait(rig["id"])["output"]["model_url"])

Errors

StatusCodeCause
400invalid_requestNo image supplied, or an unknown quality
401unauthorizedMissing or invalid API key
402subscription_requiredNo active Standard or Pro subscription
402insufficient_creditsBalance will not cover the generation
429rate_limitedOver 60 requests per minute
503backend_unavailableGenerator at capacity. Retry with backoff