API

Quickstart

Create a key, find a model, submit a run, poll it and read the result.

This walk-through takes you from no key to a generated image. You need an AITOPIA account with credits.

#1. Create an API key

Sign in at aitopia.ai, open the profile menu > API keys and create a developer key. You get two secrets, each shown only once:

  • the API key (aitk_live_...), sent with every request;
  • the webhook secret, used to verify webhooks.

Store both on your server (for example in environment variables). Never put the key in a browser or mobile app: anyone holding it spends your credits.

Try it free first. Create a test key (aitk_test_...) instead and use https://api-sandbox.aitopia.ai as the base URL in every step below. The sandbox accepts the same requests, validates them the same way and returns sample files, without spending credits.

Shell
export AITOPIA_KEY="aitk_live_..."

#2. List models

curl
curl "https://api-live.aitopia.ai/v1/models?type=image&limit=5" \
  -H "Authorization: Key $AITOPIA_KEY"
Node.js
const BASE = "https://api-live.aitopia.ai/v1";
const headers = { Authorization: `Key ${process.env.AITOPIA_KEY}` };

const res = await fetch(`${BASE}/models?type=image&limit=5`, { headers });
const { models, has_more, next_offset } = await res.json();
for (const m of models) console.log(m.id, m.price?.credits);
Python
import os, requests

BASE = "https://api-live.aitopia.ai/v1"
HEADERS = {"Authorization": f"Key {os.environ['AITOPIA_KEY']}"}

r = requests.get(f"{BASE}/models", params={"type": "image", "limit": 5}, headers=HEADERS)
for m in r.json()["models"]:
    print(m["id"], (m["price"] or {}).get("credits"))

Each model has an id, a type (image, video, audio or text), a price and a recommended flag. Page through the list with limit and offset while has_more is true.

#3. Read the model's input schema

Every model has its own input fields. Fetch them before the first run:

Shell
curl "https://api-live.aitopia.ai/v1/models/google/nano-banana-2" \
  -H "Authorization: Key $AITOPIA_KEY"

The answer has id, input_schema (a JSON Schema of the request body) and price. The examples below send a prompt; use the fields your model's input_schema lists.

#4. Check the price (optional)

Add ?dry_run=true to any submit call to get an estimate. Nothing runs and nothing is charged:

Shell
curl -X POST "https://api-live.aitopia.ai/v1/models/google/nano-banana-2?dry_run=true" \
  -H "Authorization: Key $AITOPIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "a lighthouse at dusk, watercolor"}'
JSON
{
  "dry_run": true,
  "endpoint": "google/nano-banana-2",
  "credits_estimated": 4,
  "affordable": true,
  "credits_available": 100
}

#5. Submit a run

The request body is the model input. The answer is 202 Accepted with the request id and the URLs to follow it:

curl
curl -X POST "https://api-live.aitopia.ai/v1/models/google/nano-banana-2" \
  -H "Authorization: Key $AITOPIA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"prompt": "a lighthouse at dusk, watercolor"}'
Node.js
const submit = await fetch(`${BASE}/models/google/nano-banana-2`, {
  method: "POST",
  headers: { ...headers, "Content-Type": "application/json" },
  body: JSON.stringify({ prompt: "a lighthouse at dusk, watercolor" }),
});
const request = await submit.json();
if (!submit.ok) throw new Error(`${request.error.code}: ${request.error.message}`);
console.log(request.request_id, request.status);
Python
r = requests.post(
    f"{BASE}/models/google/nano-banana-2",
    headers=HEADERS,
    json={"prompt": "a lighthouse at dusk, watercolor"},
)
request = r.json()
print(request["request_id"], request["status"])
JSON
{
  "request_id": "0d6f3c1e-5b7a-4c2e-9d1f-2a8b6e4c9f10",
  "status": "queued",
  "endpoint": "google/nano-banana-2",
  "created_at": "2026-10-23T10:00:00.000Z",
  "completed_at": null,
  "credits_estimated": 4,
  "output": { "images": [], "videos": [], "audio": [], "files": [] },
  "status_url": "https://api-live.aitopia.ai/v1/requests/0d6f3c1e-5b7a-4c2e-9d1f-2a8b6e4c9f10",
  "result_url": "https://api-live.aitopia.ai/v1/requests/0d6f3c1e-5b7a-4c2e-9d1f-2a8b6e4c9f10",
  "cancel_url": "https://api-live.aitopia.ai/v1/requests/0d6f3c1e-5b7a-4c2e-9d1f-2a8b6e4c9f10/cancel"
}

#6. Poll until it finishes

GET the status_url. With ?wait=30 the call holds for up to 30 seconds and returns as soon as the request is finished:

curl
curl "https://api-live.aitopia.ai/v1/requests/$REQUEST_ID?wait=30" \
  -H "Authorization: Key $AITOPIA_KEY"
Node.js
const TERMINAL = ["completed", "partial", "failed", "cancelled"];
let result = request;
while (!TERMINAL.includes(result.status)) {
  const r = await fetch(`${result.status_url}?wait=30`, { headers });
  result = await r.json();
}
console.log(result.status, result.output.images.map((i) => i.url));
Python
TERMINAL = {"completed", "partial", "failed", "cancelled"}
result = request
while result["status"] not in TERMINAL:
    result = requests.get(result["status_url"], params={"wait": 30}, headers=HEADERS).json()
print(result["status"], [i["url"] for i in result["output"]["images"]])

#7. Use the result

A finished request lists its files under output, grouped by kind:

JSON
{
  "request_id": "0d6f3c1e-5b7a-4c2e-9d1f-2a8b6e4c9f10",
  "status": "completed",
  "endpoint": "google/nano-banana-2",
  "created_at": "2026-10-23T10:00:00.000Z",
  "completed_at": "2026-10-23T10:00:14.000Z",
  "credits_estimated": 4,
  "output": {
    "images": [{ "url": "https://cdn.aitopia.ai/...", "content_type": "image/png" }],
    "videos": [],
    "audio": [],
    "files": []
  },
  "status_url": "...",
  "result_url": "...",
  "cancel_url": "..."
}

If the status is failed, the error object says why (see Errors & retries).

#Next steps

AITOPIA REST API · Live https://api-live.aitopia.ai/v1 · Sandbox https://api-sandbox.aitopia.ai/v1