venice-api · git:20260805.9863caf · 2026-08-05 · sha256 6a977b40b9178059

venice-api git:20260805.9863cafC

Immutable. This exact content never changes and is served at /api/v1/blob/6a977b40b9178059.

# Venice AI Image & Video Generation Skill

## Description
Generate images and videos via the Venice AI REST API. Image generation uses the Nano Banana Pro model. Video generation uses Veo 3.1 models (image-to-video and text-to-video) via an async queue. This skill covers model names, endpoint behavior, response parsing, resolution limits, and best practices learned from production use.

## Model

- **Model ID**: `nano-banana-pro`
- **Retired models**: `fluently-xl` was the old model ID. It returns 404 "model not found" if used. Other available models include `lustify-sdxl`, `flux-2-max`, `lustify-v7` but we use `nano-banana-pro` for storyboard work.

## Endpoints

### Image Generation
```
POST https://api.venice.ai/api/v1/image/generate
Authorization: Bearer <VENICE_API_KEY>
Content-Type: application/json
```

#### Request Body
```json
{
  "model": "nano-banana-pro",
  "prompt": "scene description...",
  "negative_prompt": "things to avoid...",
  "resolution": "1K",
  "aspect_ratio": "16:9",
  "steps": 30,
  "cfg_scale": 7,
  "seed": 12345,
  "hide_watermark": true,
  "safe_mode": false
}
```

**CRITICAL**: Use `resolution` + `aspect_ratio`, NOT `width`/`height`. The `width`/`height` params are deprecated and silently ignored -- the API defaults to 1024x1024 (1:1) if you use them.

- `resolution`: `"1K"` or `"2K"` (max 1280 on any dimension)
- `aspect_ratio`: `"16:9"`, `"9:16"`, `"4:3"`, `"3:4"`, `"1:1"`
- For storyboard panels: `resolution: "1K"` + `aspect_ratio: "16:9"` produces 1376x768
- For character references: `resolution: "1K"` + `aspect_ratio: "1:1"` produces 1024x1024

`nano-banana-pro` does NOT accept reference image payloads (`image_references`, `image_1`, etc.) -- these cause a 400 error. Character consistency relies on exhaustive text descriptions and seed anchoring.

Optional img2img params (for edit-style generation):
```json
{
  "image": "base64-ref-image",
  "init_image_mode": "IMAGE_STRENGTH",
  "fidelity": 0.7
}
```

#### Response Body
```json
{
  "id": "request-id",
  "images": ["<raw-base64-encoded-image-string>"],
  "request": { ... },
  "timing": { ... }
}
```

**IMPORTANT**: The `images` array contains raw base64 strings, NOT objects with a `b64_json` field. To extract the image:
```javascript
const b64 = data.images[0]; // string, not object
Buffer.from(b64, "base64"); // decode to binary
```

If code checks for `data.images[0].b64_json`, it will get `undefined` because the string gets character-indexed as an object. Always check `typeof images[0] === "string"` first.

### Image Edit
```
POST https://api.venice.ai/api/v1/images/edit
```
Used for face correction and inpainting. Same auth pattern.

## Resolution Limits

- **Maximum dimension**: 1280px on width OR height. Exceeding this returns HTTP 400.
- **Storyboard panels (16:9)**: `1280x720`
- **Character reference locks (1:1)**: `1024x1024`
- **Minimum useful size**: ~512x512

Common 16:9 options within limits:
- `1280x720` (720p -- recommended for panels)
- `1024x576`
- `960x540`

## Multi-Reference Images

Venice supports up to 14 reference images per generation request:
- Pass as comma-separated base64 strings in the `image` field
- First 5 slots conventionally reserved for character face references
- Slots 6-14 for style, environment, or additional references
- Add role assignments in the prompt: `"Image 1: face reference for CHARACTER_NAME (role description)"`
- Use `fidelity: 0.35` for creative freedom with anchored likeness

## Rate Limiting

- Batch 2-3 concurrent requests at a time
- Insert ~500ms delay between batches
- On HTTP 429, back off exponentially (1s, 2s, 4s)
- On HTTP 5xx, retry up to 3 times with backoff

## Prompt Best Practices

### For storyboard panels:
- Start with the scene description (characters, action, environment)
- Append the aesthetic style directives (film stock, lighting, palette, lens)
- Include full character physical descriptions every time -- never abbreviate
- Use negative prompts to exclude unwanted styles

### For character references:
- Generate at 1024x1024 (square)
- Describe the character exhaustively: age, ethnicity, hair, eyes, face shape, build, wardrobe
- Generate 4 angles: front face, 3/4 view, profile, full body
- Track the seed for reproducibility

### For aesthetic samples:
- Use a representative scene from the screenplay as the base prompt
- Append distinct style directives for each aesthetic
- Generate at 1280x720 (16:9) to match final output
- Save as PNGs for comparison

## Writing Scripts That Call Venice Directly

When writing standalone Node.js scripts (not going through the CLI):
- Use `.mjs` extension for ES module support without tsconfig
- Parse `.env` manually (read file, split lines, regex match `KEY=VALUE`) to avoid import resolution issues with `dotenv`
- Use native `fetch` (available in Node 18+)
- Handle the response as `{ images: ["base64-string"] }`, not `{ images: [{ b64_json }] }`

```javascript
import { readFileSync, writeFileSync } from "fs";

// Parse .env manually
const env = readFileSync(".env", "utf-8");
const apiKey = env.match(/VENICE_API_KEY=(.*)/)?.[1]?.trim();

const res = await fetch("https://api.venice.ai/api/v1/image/generate", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${apiKey}`,
  },
  body: JSON.stringify({
    model: "nano-banana-pro",
    prompt: "...",
    width: 1280,
    height: 720,
    steps: 30,
    cfg_scale: 7,
    hide_watermark: true,
    safe_mode: false,
  }),
});

const data = await res.json();
const b64 = data.images[0]; // raw base64 string
writeFileSync("output.png", Buffer.from(b64, "base64"));
```

---

## Video Generation (Veo 3.1)

Venice wraps Google Veo 3.1 for video generation via an async queue-based API.

### Video Models

| Model | Type | Duration | Aspect Ratio |
|-------|------|----------|--------------|
| `veo3.1-fast-image-to-video` | Image-to-Video | `8s` only | Do NOT include (400 error) |
| `veo3.1-fast-text-to-video` | Text-to-Video | `4s`, `6s`, `8s` | **Required** (`16:9` or `9:16`) |
| `veo3.1-full-text-to-video` | Text-to-Video | `4s`, `6s`, `8s` | **Required** (`16:9` or `9:16`) |
| `wan-2.5-preview-image-to-video` | Image-to-Video | `5s`, `10s` | Optional |
| `wan-2.5-preview-text-to-video` | Text-to-Video | `5s`, `10s` | Optional |

### Video Endpoints

#### Queue (start generation)
```
POST https://api.venice.ai/api/v1/video/queue
Authorization: Bearer <VENICE_API_KEY>
Content-Type: application/json
```

**Image-to-Video Request:**
```json
{
  "model": "veo3.1-fast-image-to-video",
  "prompt": "A slow dolly shot pushes forward through a dim corridor...",
  "duration": "8s",
  "image_url": "data:image/png;base64,...",
  "resolution": "720p",
  "audio": true
}
```

**Text-to-Video Request:**
```json
{
  "model": "veo3.1-fast-text-to-video",
  "prompt": "Full scene description...",
  "duration": "8s",
  "aspect_ratio": "16:9",
  "resolution": "720p",
  "audio": true
}
```

**Response:**
```json
{ "model": "veo3.1-fast-image-to-video", "queue_id": "uuid-string" }
```

**CRITICAL model-specific rules:**
- `veo3.1-fast-image-to-video`: Duration is ALWAYS `"8s"`. Do NOT include `aspect_ratio`.
- `veo3.1-fast-text-to-video` / `veo3.1-full-text-to-video`: `aspect_ratio` is REQUIRED.

#### Retrieve (poll status / download)
```
POST https://api.venice.ai/api/v1/video/retrieve
```

**Request:**
```json
{ "model": "veo3.1-fast-image-to-video", "queue_id": "uuid-string" }
```

**While processing (JSON response):**
```json
{
  "status": "PROCESSING",
  "average_execution_time": 115000,
  "execution_duration": 45000
}
```

**When complete:** Returns binary `video/mp4` data. Check `content-type` header to distinguish from JSON.

#### Complete (cleanup)
```
POST https://api.venice.ai/api/v1/video/complete
```

**Request:**
```json
{ "model": "veo3.1-fast-image-to-video", "queue_id": "uuid-string" }
```

Optional. Cleans up server-side storage after download.

### Video Polling Pattern

Poll every 10 seconds. Typical completion: 60-120 seconds. Max wait: ~5 minutes.

```typescript
const contentType = resp.headers.get("content-type") || "";
if (contentType.includes("application/json")) {
  // Still processing -- log status and continue polling
  const status = await resp.json();
  console.log(status.status, status.execution_duration);
} else {
  // Binary video data -- save as MP4
  const buf = Buffer.from(await resp.arrayBuffer());
  writeFileSync("shot-001.mp4", buf);
}
```

### Video Prompt Best Practices (Veo 3.1)

Veo prompts differ significantly from image prompts:

- **Plain prose only**: No `[AESTHETIC]`, `[SHOT]`, `Setting:`, `Mood:` tags. Natural sentences.
- **Camera first**: "A slow dolly shot pushes forward framing a wide shot at eye level."
- **Veo camera vocabulary**: `dolly shot`, `tracking shot`, `crane shot`, `slow pan`, `locked-off static shot`, `tilt up`, `rack focus`, `handheld shot`
- **Concrete visuals**: "Off-white institutional walls glow under flickering fluorescent light." Not "Setting: hallway, day."
- **Style as film terms**: "1970s analog sci-fi, 16mm Ektachrome with faded warm tones and heavy grain."
- **Audio in separate sentences**: "Sound of boots on tile and a distant alarm." (Veo 3.1 generates audio natively.)
- **Dialogue quoted**: `JAX says "We need to move."`
- **Keep under ~150 words** (Veo max: 1,024 tokens / ~2,500 chars)

### Video Error Handling

| Status | Meaning | Action |
|--------|---------|--------|
| 400 | Invalid params (wrong duration, missing aspect_ratio) | Check model-specific rules |
| 401 | Bad API key | Check VENICE_API_KEY |
| 402 | Insufficient balance | Add credits to Venice account |
| 422 | Content policy violation | Adjust prompt |
| 503 | Model at capacity | Retry after delay |

### Writing Video Generation Scripts

```typescript
import { readFileSync, writeFileSync } from "fs";

const env = readFileSync(".env", "utf-8");
const apiKey = env.match(/VENICE_API_KEY=(.*)/)?.[1]?.trim();

// 1. Queue
const queueResp = await fetch("https://api.venice.ai/api/v1/video/queue", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${apiKey}`,
  },
  body: JSON.stringify({
    model: "veo3.1-fast-image-to-video",
    prompt: "A slow dolly shot...",
    duration: "8s",
    image_url: `data:image/png;base64,${readFileSync("shot-001.png", "base64")}`,
    resolution: "720p",
    audio: true,
  }),
});
const { queue_id, model } = await queueResp.json();

// 2. Poll
while (true) {
  await new Promise(r => setTimeout(r, 10000));
  const resp = await fetch("https://api.venice.ai/api/v1/video/retrieve", {
    method: "POST",
    headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
    body: JSON.stringify({ model, queue_id }),
  });
  const ct = resp.headers.get("content-type") || "";
  if (!ct.includes("application/json")) {
    writeFileSync("shot-001.mp4", Buffer.from(await resp.arrayBuffer()));
    break;
  }
  // Still processing, continue polling
}

// 3. Cleanup
await fetch("https://api.venice.ai/api/v1/video/complete", {
  method: "POST",
  headers: { "Content-Type": "application/json", Authorization: `Bearer ${apiKey}` },
  body: JSON.stringify({ model, queue_id }),
});
```