Suzanne
Browse docs

Recipes

End-to-end Python and Node.js examples for text-to-3D and multi-view photo-to-3D.

End-to-end snippets you can drop into a project. Both examples submit a job, poll until done, and return the mesh bytes.

Generate from text: Python

import requests, time, os
 
BASE = "https://api.suzanne3d.com"
KEY  = os.environ["SUZANNE_API_KEY"]
 
def generate_from_text(prompt: str, faces: int = 100_000) -> bytes:
    s = requests.Session()
    s.headers["Authorization"] = f"Bearer {KEY}"
 
    r = s.post(f"{BASE}/v1/generations/text-to-3d", json={
        "model": "sculptor",
        "prompt": prompt,
        "params": {"faces": faces, "pbr": True},
        "outputs": ["glb"],
    })
    r.raise_for_status()
    job_id = r.json()["job_id"]
 
    while True:
        time.sleep(5)
        job = s.get(f"{BASE}/v1/jobs/{job_id}").json()
        if job["status"] == "done":
            return s.get(job["outputs"][0]["download_url"]).content
        if job["status"] in ("failed", "cancelled"):
            raise RuntimeError(job.get("error") or "job did not complete")

Generate from 4 photos: Node.js

import fs from "node:fs/promises";
 
const BASE = "https://api.suzanne3d.com";
const KEY  = process.env.SUZANNE_API_KEY;
const auth = { Authorization: `Bearer ${KEY}` };
 
async function uploadView(path) {
  const { upload_id, upload_url } = await fetch(`${BASE}/v1/uploads`, {
    method: "POST", headers: auth,
  }).then(r => r.json());
 
  // Crucial: do NOT set Content-Type. The presigned URL is signed without it.
  await fetch(upload_url, { method: "PUT", body: await fs.readFile(path) });
  return upload_id;
}
 
export async function generateFrom4Views({ front, back, left, right }) {
  const ids = {
    front: await uploadView(front),
    back:  await uploadView(back),
    left:  await uploadView(left),
    right: await uploadView(right),
  };
 
  const { job_id } = await fetch(`${BASE}/v1/generations/photo-to-3d`, {
    method: "POST",
    headers: { ...auth, "content-type": "application/json" },
    body: JSON.stringify({
      model: "sculptor",
      images_upload_ids: ids,
      params: { faces: 100000, pbr: true },
      outputs: ["glb"],
    }),
  }).then(r => r.json());
 
  while (true) {
    await new Promise(r => setTimeout(r, 5000));
    const job = await fetch(`${BASE}/v1/jobs/${job_id}`, { headers: auth }).then(r => r.json());
    if (job.status === "done") {
      const r = await fetch(job.outputs[0].download_url, { headers: auth, redirect: "follow" });
      return new Uint8Array(await r.arrayBuffer());
    }
    if (["failed", "cancelled"].includes(job.status)) throw new Error(JSON.stringify(job.error));
  }
}

Want an LLM coding agent to wire this into your own stack? See Integrate with Claude Code.