> ## Documentation Index
> Fetch the complete documentation index at: https://vetta.sh/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Images

> Generate images over REST — one call, and it waits; every rendered image comes back as a file — and search a licensed stock photo library, free.

`POST /v1/media/images` renders synchronously: the reply carries the finished [files](/docs/api/files) and what the render cost. There is no job to poll — an image is done before the request returns, so it is never a [`media_job`](/docs/api/video#the-media-job-object). The same render is what an agent's `generate_image` tool does; the [catalogue](/docs/api/media) is shared.

## The image generation object

<ResponseField name="object" type="string">Always `image_generation`.</ResponseField>
<ResponseField name="model" type="string">The model that rendered, which is the one you named or the default it picked.</ResponseField>
<ResponseField name="files" type="File[]">One [file](/docs/api/files#the-file-object) per rendered image, `n` of them when the model produced that many. Each is `published` and carries `source.type = image_generation`.</ResponseField>
<ResponseField name="cost_micro_usd" type="integer">What was debited for the render, in integer micro-USD, on the media tier. See [Pricing](/docs/platform/pricing#media-generation--per-finished-job-media-component).</ResponseField>

## Generate images

`POST /v1/media/images` → `200 OK` — scope `agents:write`, [`Idempotency-Key`](/docs/api/overview#idempotency) **required**.

<ParamField body="prompt" type="string" required>What to render.</ParamField>
<ParamField body="model" type="string">A model id from [`GET /v1/media/models?kind=image`](/docs/api/media#search-models). Omitted, the cheapest priced image model is used; an id the catalogue does not know is `validation_failed`.</ParamField>
<ParamField body="image_urls" type="string[]">Up to 4 public `http(s)` reference images for an edit or an image-to-image render. A private or loopback host is `validation_failed`.</ParamField>
<ParamField body="n" type="integer">How many images, 1–4. Default 1. Every one is filed — none is dropped.</ParamField>
<ParamField body="aspect_ratio" type="string">Such as `16:9` or `1:1`. Passed to the model as-is.</ParamField>
<ParamField body="seed" type="integer">Repeat an earlier result.</ParamField>

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -fsSL https://api.vetta.sh/v1/media/images \
    -H "authorization: Bearer sk_live_..." \
    -H "idempotency-key: $(uuidgen)" \
    -H "content-type: application/json" \
    -d '{ "prompt": "a paper kite over a grey harbour", "n": 2, "aspect_ratio": "16:9" }'
  ```

  ```ts SDK theme={"system"}
  const out = await client.media.images.generate({
    prompt: "a paper kite over a grey harbour",
    n: 2,
    aspect_ratio: "16:9",
  });
  for (const file of out.files) console.log(file.id, file.content_type);
  ```

  ```bash CLI theme={"system"}
  vetta media generate --kind image --prompt "a paper kite over a grey harbour" --n 2 --aspect-ratio 16:9
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={"system"}
  {
    "object": "image_generation",
    "model": "example/thrifty-image",
    "files": [
      {
        "id": "fil_01J...",
        "object": "file",
        "name": "image-fil_01J....png",
        "content_type": "image/png",
        "size_bytes": 184201,
        "scope": "published",
        "session_id": null,
        "sha256": "…",
        "created_at": "2026-09-06T12:00:00.000Z"
      }
    ],
    "cost_micro_usd": 50340
  }
  ```
</ResponseExample>

Read the bytes with [`GET /v1/files/{id}?download=true`](/docs/api/files#retrieve-a-file).

## Billing

A render is billed **what it actually cost**, once it has happened, on the media tier; nothing is quoted beforehand because the catalogue publishes no price for most models. Before rendering, the organization's prepaid balance is checked against a small floor — `insufficient_credits` if it is below it — and the debit settles under the request's idempotency key, so a replayed request returns the same files and charges once. A render the model refuses or fails is `provider_error` and is not billed.

## Errors

| Code                     | When                                                                                               |
| ------------------------ | -------------------------------------------------------------------------------------------------- |
| `validation_failed`      | Missing `Idempotency-Key`, an unknown `model`, a private-host `image_urls` entry, `n` outside 1–4. |
| `insufficient_credits`   | The prepaid balance is below the admission floor.                                                  |
| `provider_error`         | The model failed or refused; nothing is filed and nothing is billed.                               |
| `feature_not_configured` | This deploy has no generation provider configured.                                                 |

## Stock photos

`GET /v1/media/stock` — scope `agents:read`

Searches a stock photo library and returns up to `count` photos, each with a direct image URL at the size you asked for, a small preview, and the photographer credit the licence asks you to show. Free: a search is booked to the [ledger](/docs/platform/billing) as a `search` line at `$0`, so it shows up in a spend breakdown as a call that happened, not as one that was never priced.

<ParamField query="query" type="string" required>What the photo should show. 1–200 characters.</ParamField>
<ParamField query="count" type="number" default="10">How many photos to return, 1–30. There is no cursor: `count` is the whole page.</ParamField>
<ParamField query="orientation" type="string">`landscape`, `portrait` or `square`.</ParamField>
<ParamField query="color" type="string">A dominant colour — a name such as `red`, `blue`, `brown`, `black`, `white`, or a hex value such as `#978E82`.</ParamField>
<ParamField query="size" type="string">The image size `url` points at: `small`, `medium` or `large`. Default `large`. Picks the rendition only — it does not narrow the results.</ParamField>

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -fsSL "https://api.vetta.sh/v1/media/stock?query=office+workspace&count=3&orientation=landscape" \
    -H "authorization: Bearer sk_live_..."
  ```

  ```ts SDK theme={"system"}
  const { data } = await client.media.stock.search({ query: "office workspace", count: 3, orientation: "landscape" });
  ```

  ```bash CLI theme={"system"}
  vetta media stock "office workspace" --count 3 --orientation landscape
  ```
</CodeGroup>

<ResponseExample>
  ```json Response theme={"system"}
  {
    "data": [
      {
        "object": "stock_photo",
        "id": "2014422",
        "width": 3024,
        "height": 2016,
        "url": "https://images.example/2014422.jpeg?w=940",
        "preview_url": "https://images.example/2014422.jpeg?w=280",
        "alt": "Brown rocks during golden hour",
        "photographer": "Joey Farina",
        "attribution_url": "https://photos.example/photo/2014422/",
        "dominant_color": "#978E82"
      }
    ]
  }
  ```
</ResponseExample>

### The stock photo object

<ResponseField name="object" type="string">Always `stock_photo`.</ResponseField>
<ResponseField name="id" type="string">The library's own id for the photo. Opaque and unprefixed — it is not a Vetta resource id and cannot be fetched on its own; keep the photo by importing its `url` as a file.</ResponseField>
<ResponseField name="width" type="number">Pixel width of the original.</ResponseField>
<ResponseField name="height" type="number">Pixel height of the original. `width > height` is a landscape photo.</ResponseField>
<ResponseField name="url" type="string">A direct image URL at the requested `size`. Fetchable without a key.</ResponseField>
<ResponseField name="preview_url" type="string">A small thumbnail, for picking before downloading.</ResponseField>
<ResponseField name="alt" type="string | null">A one-line description of the photo, when the library has one.</ResponseField>
<ResponseField name="photographer" type="string | null">Who took it. The licence asks that this credit be shown wherever the photo is used.</ResponseField>
<ResponseField name="attribution_url" type="string | null">The page to link the credit to. This is the one field that may point at the library's own domain.</ResponseField>
<ResponseField name="dominant_color" type="string | null">The photo's average colour as a hex value.</ResponseField>

### Keeping a photo

A result is a URL, not a file. To hold the bytes as a `fil_` — so a session can attach it, or a page can serve it from your own storage — import `url` through [Files](/docs/api/files); the imported file records where it came from as a URL import.

### In a session

Agents reach the same search as the [`find_stock_photo`](/docs/capabilities/tools) tool, which takes `query`, `count` and `orientation`. It is on by default with permission `allow`.

### Errors

| Status | Code                     | When                                                                      |
| ------ | ------------------------ | ------------------------------------------------------------------------- |
| `400`  | `validation_failed`      | `query` missing, `count` outside 1–30, or a filter value outside its set. |
| `429`  | `rate_limited`           | The library's request allowance for this deploy is spent; retry shortly.  |
| `501`  | `feature_not_configured` | This deploy has no stock photo library configured.                        |
| `502`  | `provider_error`         | The library answered with an error.                                       |
