Skip to content
Docs menu

API reference

Create a task

On this page

POST /api/v1/generate

Submit a generation. We validate your key, debit credits for the model, forward the request upstream, and return either an inline result (chat) or a task id to poll (media).

FieldTypeDescription
modelIdstringModel id from the Market (e.g. veo-3-1, nano-banana-pro). The alias `model` is also accepted.
inputobjectModel-specific parameters — see each model's page for its fields.
callbackUrlstring?Optional https webhook; we POST the finished task to it (see Webhooks).
bash
curl -X POST https://you.bot/api/v1/generate \
  -H "Authorization: Bearer $YOUBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "modelId": "veo-3-1", "input": { "prompt": "a city at dusk", "aspectRatio": "16:9" } }'

Media model response (poll the task id):

json
{ "taskId": "a1b2c3d4e5f6g7h8", "model": "veo-3-1", "creditsCharged": 312 }

Chat/LLM model response (inline, no polling):

json
{ "model": "claude-fable-5", "text": "…", "creditsCharged": 24 }

**`model` is the answer to "which model served this request?"** On a chat call it is read from the model's own reply, not from our request — so you can assert it per call in your own code instead of asking the assistant, which cannot report its own identity. It is always the same model id you requested; if it were ever anything else, the request would have failed instead. (On a media create call it echoes the model the task was accepted for; media task APIs return no model of their own.)

**Conversations (chat models).** Send `messages` inside `input` instead of `prompt` to pass a whole conversation, exactly as the model receives it: an array of `{ "role", "content" }` objects with `role` one of `user`, `assistant` or `system`. Up to 20 messages per request. `content` is a string, or an array of content blocks for a model that accepts images; a message may also carry `images`. Send `prompt` **or** `messages`, not both — a request with both is refused (400 `prompt_and_messages`) rather than one of them being ignored. Anything malformed is refused too, naming the message: a role we do not accept, a `content` that is not a string or array, an empty message, or more than 20 messages. Nothing is dropped silently, and nothing is charged for a refused request.

bash
curl -X POST https://you.bot/api/v1/generate \
  -H "Authorization: Bearer $YOUBOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "modelId": "claude-opus-5",
    "input": {
      "messages": [
        { "role": "user", "content": "I want to build an AI reseller site" },
        { "role": "assistant", "content": "Happy to help — a few technical decisions first." },
        { "role": "user", "content": "List the Claude input and output prices." }
      ]
    }
  }'

**Web search (chat models that list `web_search`).** Send `web_search: true` inside `input` and the model searches the web before answering. On the model+route pairs where we have measured that the search can be required, we require it — so a request that asks for search gets a search, instead of the model deciding to answer from memory. Two things follow, and both are visible on your bill and your clock: the search results are fed back to the model as **input tokens**, so a searched turn can cost many times a plain one (measured on one model: 39 input tokens becomes 16,221), and a searched turn is slower — a few seconds becomes up to ten. Send `web_search: true` on the turns that need fresh facts, not on every turn. If the reply comes back as raw search results instead of an answer, the request is refused with 422 `search_not_performed` and **nothing is charged**.

Do not flatten a conversation into one `prompt` with `User:` / `Assistant:` labels. The model reads that as a single message, and a model using web search will search for the whole block of text instead of your question. Send `messages` and each turn keeps its role.

**Streaming (chat models).** Send `stream: true` inside `input` and the response is an event stream (`Content-Type: text/event-stream`) instead of a JSON object. **It is opt-in on this endpoint:** omit it and you get the single JSON object described above, so existing integrations are unaffected. The frames are:

text
event: start
data: {"model":"claude-fable-5","taskId":"a1b2c3…"}

data: {"text":"Once upon"}

data: {"text":" a time"}

event: done
data: {"taskId":"a1b2c3…"}

`start` carries the same `model` value the JSON response would, and a `taskId`. Each middle frame carries the text added since the previous frame — concatenate them for the full reply. `done` closes the stream. The exact amount charged is settled from the model's own token counts after the stream ends, so it is not in the stream; look the `taskId` up in your usage log for the final figure. The same `taskId` is also returned in the `X-Task-Id` response header, and the model in `X-Model`. Streaming applies to chat models that list `stream` as an input parameter; media models are unaffected.