` |
| `No available channel` | Run `flatkey models --json`, retry with a listed id. Do not retry the same model |
| Unknown voice | `flatkey audio voices --json`, retry with a listed id |
| Insufficient credits | `flatkey credits --json`, then top up |
| `unsupported resolution` | Fall back to `720p` |
Retry only after changing the request. These errors are deterministic, so repeating identical arguments burns time without changing the outcome.
# Generate and Edit Images with GPT and Gemini Models via Flatkey
Source: https://docs.flatkey.ai/guides/image-generation
Generate and edit images through Flatkey with gpt-image-2 and Gemini image models, using OpenAI Images-compatible or native Gemini APIs.
Flatkey provides two image API families. Use the OpenAI-compatible `/v1/images/generations` and `/v1/images/edits` endpoints for `gpt-image-2`. Use the native Gemini `generateContent` endpoint for Gemini image models.
You cannot call Gemini image models with the OpenAI Images SDK, `/v1/images/generations`, or `/v1/images/edits`. Use the native Gemini API examples on this page. Otherwise, the request may return `get_channel_failed`.
## Supported image models
| Model | API | Official price | After-bonus price |
| - | - | - | - |
| `gpt-image-2` | OpenAI Images API | \$6.65 / 1M tokens | \~\$3.99 / 1M tokens |
| `gemini-3-pro-image` | Native Gemini API | \$2.00 / 1M tokens | \~\$1.20 / 1M tokens |
| `gemini-2.5-flash-image` | Native Gemini API | \$0.30 / 1M tokens | \~\$0.18 / 1M tokens |
| `gemini-3.1-flash-image` | Native Gemini API | See the dashboard | See the dashboard |
| `gemini-3.1-flash-lite-image` | Native Gemini API | See the dashboard | See the dashboard |
| `nano-banana-pro-preview` | Native Gemini API | See the dashboard | See the dashboard |
See the [Model Directory](https://flatkey.ai/models) for the complete model list and current pricing.
## Use gpt-image-2
### Use the OpenAI SDK
```python python theme={"dark"}
import base64
import os
from pathlib import Path
from openai import OpenAI
client = OpenAI(
api_key=os.environ["FLATKEY_API_KEY"],
base_url="https://router.flatkey.ai/v1",
)
response = client.images.generate(
model="gpt-image-2",
prompt="A serene mountain lake at sunset, photorealistic",
size="1024x1024",
)
Path("generated.png").write_bytes(
base64.b64decode(response.data[0].b64_json)
)
```
```javascript node theme={"dark"}
import { writeFileSync } from "node:fs";
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.FLATKEY_API_KEY,
baseURL: "https://router.flatkey.ai/v1",
});
const response = await client.images.generate({
model: "gpt-image-2",
prompt: "A serene mountain lake at sunset, photorealistic",
size: "1024x1024",
});
writeFileSync(
"generated.png",
Buffer.from(response.data[0].b64_json, "base64"),
);
```
### Generate an image with curl
```bash theme={"dark"}
curl https://router.flatkey.ai/v1/images/generations \
-H "Authorization: Bearer $FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "A futuristic cityscape at night, neon lights, wide angle",
"size": "1024x1024"
}'
```
This endpoint returns the generated image in `data[0].b64_json`; `gpt-image-2` does not provide a hosted image URL. See the [Images API reference](/api-reference/image-generation#generate-an-image) for all fields.
### Edit an image with curl
Use `/v1/images/edits` to upload an input image together with editing instructions. This endpoint uses `multipart/form-data`; `image` is required and `mask` is optional.
```bash theme={"dark"}
curl https://router.flatkey.ai/v1/images/edits \
-H "Authorization: Bearer $FLATKEY_API_KEY" \
-F "model=gpt-image-2" \
-F "prompt=add a small yellow star in the center" \
-F "image=@input.png" \
-F "mask=@mask.png"
```
`image` supports PNG, JPEG, and WebP. Omit `mask` to edit the whole image based on the prompt. When a mask is provided, its transparent areas identify the regions to redraw. The edited image is returned in `data[0].b64_json`. See the [edit API reference](/api-reference/image-generation#edit-an-image) for all fields.
## Use Gemini image models
Gemini image models use the following endpoint. Replace `{model}` with a model ID:
```text theme={"dark"}
POST https://router.flatkey.ai/v1beta/models/{model}:generateContent
```
The request must include `"IMAGE"` in `generationConfig.responseModalities`. Generated images are returned in `candidates[].content.parts[].inlineData`.
### Use curl
```bash theme={"dark"}
curl https://router.flatkey.ai/v1beta/models/gemini-2.5-flash-image:generateContent \
-H "Authorization: Bearer $FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [{
"parts": [{
"text": "A product photo of a translucent purple glass bottle on a white background"
}]
}],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"]
}
}'
```
### Use Python and save the image
Install an HTTP client:
```bash theme={"dark"}
pip install requests
```
```python theme={"dark"}
import base64
import os
from pathlib import Path
import requests
model = "gemini-2.5-flash-image"
response = requests.post(
f"https://router.flatkey.ai/v1beta/models/{model}:generateContent",
headers={
"Authorization": f"Bearer {os.environ['FLATKEY_API_KEY']}",
"Content-Type": "application/json",
},
json={
"contents": [{
"parts": [{
"text": "A product photo of a translucent purple glass bottle on a white background"
}]
}],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"]
},
},
timeout=180,
)
response.raise_for_status()
parts = response.json()["candidates"][0]["content"]["parts"]
image = next(part["inlineData"] for part in parts if "inlineData" in part)
suffix = ".jpg" if image["mimeType"] == "image/jpeg" else ".png"
output = Path(f"generated{suffix}")
output.write_bytes(base64.b64decode(image["data"]))
print(output)
```
### Use Node.js and save the image
```javascript theme={"dark"}
import { writeFileSync } from "node:fs";
const model = "gemini-2.5-flash-image";
const response = await fetch(
`https://router.flatkey.ai/v1beta/models/${model}:generateContent`,
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.FLATKEY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
contents: [{
parts: [{
text: "A product photo of a translucent purple glass bottle on a white background",
}],
}],
generationConfig: {
responseModalities: ["TEXT", "IMAGE"],
},
}),
},
);
if (!response.ok) {
throw new Error(`${response.status}: ${await response.text()}`);
}
const result = await response.json();
const image = result.candidates[0].content.parts.find(
(part) => part.inlineData,
).inlineData;
const suffix = image.mimeType === "image/jpeg" ? "jpg" : "png";
writeFileSync(`generated.${suffix}`, Buffer.from(image.data, "base64"));
```
## Gemini request fields
Conversation content. For text-to-image generation, put the image description in `parts[].text`.
Response modalities. Use `["TEXT", "IMAGE"]` for text and images, or `["IMAGE"]` to request images only.
The model ID appears in the URL path, not in the request body. For example, `gemini-3-pro-image`.
## Gemini response format
```json theme={"dark"}
{
"candidates": [
{
"content": {
"parts": [
{
"text": "Here is the generated image."
},
{
"inlineData": {
"mimeType": "image/png",
"data": "iVBORw0KGgoAAA..."
}
}
]
}
}
]
}
```
`inlineData.data` contains the base64-encoded image. A response can contain text and multiple image parts. Iterate through `parts` instead of assuming that the image is always the first item.
## Troubleshooting
### The API returns get\_channel\_failed
Make sure the endpoint matches the model:
* Use `/v1/images/generations` to generate with `gpt-image-2`.
* Use `/v1/images/edits` to edit with `gpt-image-2`.
* Use `/v1beta/models/{model}:generateContent` for Gemini image models.
### The Gemini response does not contain an image
Make sure `generationConfig.responseModalities` contains `"IMAGE"`. Also check the response for an error or safety block.
## Write effective prompts
* Specify the style: *photorealistic, oil painting, watercolor, 3D render*
* Include lighting details: *golden hour, studio lighting, dramatic shadows*
* Specify the composition: *close-up, wide angle, bird's-eye view, portrait orientation*
* Add quality modifiers: *highly detailed, 4K, professional photography*
# Generate MiniMax H3 videos with Flatkey
Source: https://docs.flatkey.ai/guides/minimax-h3
Create MiniMax H3 video tasks from text or reference media, poll their status, and download the finished MP4 through Flatkey.
Base URL: `https://router.flatkey.ai`
Use `MiniMax-H3` when you need text-to-video, first- and last-frame control, or image, video, and audio references. Every request follows the same asynchronous workflow: create a task, poll its status, then download the finished MP4.
Generate from text, frame images, or reference images, videos, and audio.
Create with `POST /v1/videos`, poll with `GET /v1/videos/{task_id}`, and download from `metadata.url`.
Use this header for task creation and status requests:
```http theme={"dark"}
Authorization: Bearer YOUR_FLATKEY_API_KEY
```
Keep your API key private. Do not paste it into public logs, pages, or support tickets.
## Create your first H3 video
### 1. Submit a text-to-video task
Send `POST /v1/videos` with the `MiniMax-H3` model and a text prompt. Text-only requests require an explicit aspect ratio other than `adaptive`.
```bash Bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MiniMax-H3",
"content": [
{
"type": "text",
"text": "A paper boat crosses a rain puddle in a cinematic macro shot"
}
],
"resolution": "768P",
"duration": 6,
"ratio": "16:9",
"aigc_watermark": false
}'
```
```powershell Windows PowerShell theme={"dark"}
curl.exe --fail-with-body -sS https://router.flatkey.ai/v1/videos `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Content-Type: application/json" `
-d '{
"model": "MiniMax-H3",
"content": [
{
"type": "text",
"text": "A paper boat crosses a rain puddle in a cinematic macro shot"
}
],
"resolution": "768P",
"duration": 6,
"ratio": "16:9",
"aigc_watermark": false
}'
```
The response contains the public task ID in both `id` and `task_id`:
```json theme={"dark"}
{
"id": "task_3f9a00000000000000000000000000e2",
"task_id": "task_3f9a00000000000000000000000000e2",
"object": "video",
"model": "MiniMax-H3",
"status": "queued",
"progress": 0
}
```
Save the returned `task_...` value. You need it to check the task later.
### 2. Poll and download the result
Poll `GET /v1/videos/{task_id}` until `status` becomes `completed` or `failed`. When it is `completed`, read the temporary download URL from `metadata.url`.
```bash Bash theme={"dark"}
TASK_ID="task_3f9a00000000000000000000000000e2"
curl --fail-with-body -sS \
"https://router.flatkey.ai/v1/videos/$TASK_ID" \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
# Run the status request again until status is completed.
# Then copy metadata.url from the response and paste it below.
VIDEO_URL="PASTE_METADATA_URL_HERE"
curl --fail --location "$VIDEO_URL" --output minimax-h3.mp4
```
```powershell Windows PowerShell theme={"dark"}
$taskId = "task_3f9a00000000000000000000000000e2"
while ($true) {
$result = curl.exe --fail-with-body -sS `
"https://router.flatkey.ai/v1/videos/$taskId" `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" | ConvertFrom-Json
Write-Host "status: $($result.status)"
if ($result.status -eq "completed") { break }
if ($result.status -eq "failed") {
$result | ConvertTo-Json -Depth 10
exit 1
}
Start-Sleep -Seconds 5
}
curl.exe --fail --location $result.metadata.url --output minimax-h3.mp4
```
`metadata.url` is a temporary download URL. It works without a Flatkey authorization header, so anyone who has it can access the generated video until it expires. Treat it as private, and do not log, publish, or share it. Download the video promptly and store it in your own storage. Repeating the task query does not refresh an expired URL.
## Choose your inputs
Every request must contain a non-empty text item. You can add frame images or reference media to control the result.
| Input | `type` | Required `role` | Limit |
| - | - | - | - |
| Prompt | `text` | Do not set a role | 7,000 Unicode characters across all text items |
| First frame | `image_url` | `first_frame`, or omit the role | 1 image |
| Last frame | `image_url` | `last_frame` | 1 image |
| Reference image | `image_url` | `reference_image` | 9 images |
| Reference video | `video_url` | `reference_video` | 3 videos |
| Reference audio | `audio_url` | `reference_audio` | 3 audio files; also include a reference image or video |
Do not mix first- or last-frame inputs with reference inputs in the same request. Each `content` item must contain exactly one payload that matches its `type`.
### Control the first and last frames
Use `first_frame` and `last_frame` when you want the video to interpolate between two images.
```bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MiniMax-H3",
"content": [
{
"type": "text",
"text": "The camera slowly pulls back as morning fog crosses the valley"
},
{
"type": "image_url",
"image_url": { "url": "https://example.com/first.jpg" },
"role": "first_frame"
},
{
"type": "image_url",
"image_url": { "url": "https://example.com/last.jpg" },
"role": "last_frame"
}
],
"resolution": "2K",
"duration": 8,
"ratio": "adaptive"
}'
```
### Generate with reference media
Reference inputs help preserve a subject, motion style, or sound. Reference audio cannot be the only media input.
```bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "MiniMax-H3",
"content": [
{
"type": "text",
"text": "Keep the character and movement style consistent while crossing a snowy street"
},
{
"type": "image_url",
"image_url": { "url": "https://example.com/character.jpg" },
"role": "reference_image"
},
{
"type": "video_url",
"video_url": { "url": "https://example.com/motion.mp4" },
"role": "reference_video"
},
{
"type": "audio_url",
"audio_url": { "url": "https://example.com/ambience.mp3" },
"role": "reference_audio"
}
],
"resolution": "768P",
"duration": 10,
"ratio": "adaptive"
}'
```
## Request settings
| Field | Required | Supported values |
| - | - | - |
| `model` | Yes | `MiniMax-H3` |
| `content` | Yes | Text plus optional frame or reference media |
| `resolution` | Yes | `768P` or `2K`; values are case-sensitive |
| `duration` | Yes | An integer from `4` through `15` seconds |
| `ratio` | For text-only requests | `21:9`, `16:9`, `4:3`, `1:1`, `3:4`, `9:16`, or `adaptive` with media |
| `aigc_watermark` | No | `true` or `false` |
When a request contains media and omits `ratio`, Flatkey uses `adaptive`. A text-only request must explicitly use a non-`adaptive` ratio.
## Understand task results
| `status` | Meaning |
| - | - |
| `queued` | The task is waiting to run. |
| `in_progress` | The video is being generated. Read `progress` for a value from 0 to 100. |
| `completed` | The video is ready at `metadata.url`. |
| `failed` | Generation failed. Read `error.code` and `error.message`. |
For H3 video responses, the shared usage field names represent seconds:
* `usage.completion_tokens` is the generated output duration.
* `usage.total_tokens` is the total billable duration, including reference-video input time.
These values are seconds, not language-model tokens. Check **Usage Logs** in the [Flatkey Console](https://console.flatkey.ai/usage-logs/common) for the final charge.
## Current limitations
* `callback_url` is not supported. Poll the task status instead.
* Task listing, cancellation, deletion, regeneration, and remix are not supported.
* `MiniMax-H3-Context-IR` is not supported.
* Remote media URLs must remain reachable while the task is being processed.
| Error code | What to check |
| - | - |
| `invalid_duration` | Set `duration` from `4` through `15`. |
| `invalid_resolution` | Use exactly `768P` or `2K`. |
| `invalid_ratio` | Use a supported ratio. Text-only requests cannot omit it or use `adaptive`. |
| `invalid_content` | Check the prompt, roles, media limits, and incompatible input combinations. |
| `unsupported_callback_url` | Remove `callback_url` and poll the task. |
# Use Flatkey with the OpenAI Python and Node.js SDK
Source: https://docs.flatkey.ai/guides/openai-sdk
Drop in Flatkey as the base URL for the OpenAI Python or Node.js SDK to access 300+ models at least 20% below the same model's official API price — no code rewrite required.
Access 300+ AI models at least 20% below the same model's official API price by routing the OpenAI Python or Node.js SDK through Flatkey. Switching requires one line of code: set `base_url` to `https://router.flatkey.ai/v1` and use your Flatkey API key. Every other SDK feature — streaming, function calling, async clients, retry logic — works exactly as before.
## Installation
```bash pip theme={"dark"}
pip install openai
```
```bash npm theme={"dark"}
npm install openai
```
```bash yarn theme={"dark"}
yarn add openai
```
```bash pnpm theme={"dark"}
pnpm add openai
```
## Basic setup
```python python theme={"dark"}
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["FLATKEY_API_KEY"],
base_url="https://router.flatkey.ai/v1",
)
```
```javascript node theme={"dark"}
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.FLATKEY_API_KEY,
baseURL: "https://router.flatkey.ai/v1",
});
```
## Chat completions
```python python theme={"dark"}
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Explain quantum entanglement simply."},
],
max_tokens=512,
temperature=0.7,
)
print(response.choices[0].message.content)
```
```javascript node theme={"dark"}
const response = await client.chat.completions.create({
model: "gpt-4o",
messages: [
{ role: "system", content: "You are a helpful assistant." },
{ role: "user", content: "Explain quantum entanglement simply." },
],
max_tokens: 512,
temperature: 0.7,
});
console.log(response.choices[0].message.content);
```
## Streaming
```python python theme={"dark"}
stream = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Write a haiku about the ocean."}],
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="", flush=True)
print() # newline at end
```
```javascript node theme={"dark"}
const stream = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: "Write a haiku about the ocean." }],
stream: true,
});
for await (const chunk of stream) {
const delta = chunk.choices[0]?.delta?.content || "";
process.stdout.write(delta);
}
console.log();
```
## Function calling / tool use
```python python theme={"dark"}
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"},
},
"required": ["location"],
},
},
}
]
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "What's the weather in Tokyo?"}],
tools=tools,
tool_choice="auto",
)
print(response.choices[0].message.tool_calls)
```
## Using non-OpenAI models
You can call any Flatkey-supported model using the same client — just change the `model` parameter:
```python python theme={"dark"}
# Claude via Flatkey
response = client.chat.completions.create(
model="claude-sonnet-4-5",
messages=[{"role": "user", "content": "Review this code for bugs."}],
)
# DeepSeek via Flatkey
response = client.chat.completions.create(
model="deepseek-v3",
messages=[{"role": "user", "content": "Solve this math problem step by step."}],
)
# Gemini via Flatkey
response = client.chat.completions.create(
model="gemini-2.5-flash",
messages=[{"role": "user", "content": "Summarize this article."}],
)
```
## Async client
```python python theme={"dark"}
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI(
api_key=os.environ["FLATKEY_API_KEY"],
base_url="https://router.flatkey.ai/v1",
)
async def main():
response = await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
asyncio.run(main())
```
Any model ID in the [Model Directory](https://flatkey.ai/models) works with the OpenAI SDK through Flatkey — including Claude, Gemini, DeepSeek, and Qwen models.
# Generate Seedance videos with Flatkey
Source: https://docs.flatkey.ai/guides/seedance
Create Seedance video tasks, poll results, download the final MP4, and reuse virtual or real-person assets through Flatkey.
Base URL: `https://router.flatkey.ai`
This guide gives you a copy-ready path for using Seedance through Flatkey. Start with a text-to-video task, poll the task until it completes, then download the generated MP4. If you need reusable product, background, audio, or real-person references, continue to the asset library sections.
Use the copy icon on any command block, then replace `YOUR_FLATKEY_API_KEY`, `task_...`, `ast_...`, and `rph_...` with your own values.
Create the task with `POST /v1/videos`, poll it with `GET /v1/videos/{task_id}`, and download the MP4 from `metadata.url`.
Use this authorization header on every API request in this guide. The video download URL returned in `metadata.url` is the only exception.
```http theme={"dark"}
Authorization: Bearer YOUR_FLATKEY_API_KEY
```
Keep your API key private. Do not paste it into public logs, pages, or support tickets.
## Call Seedance
Start here if you only want to create a text-to-video task and download the finished video.
### 1. Create a text-to-video task
Send `POST /v1/videos` with the `seedance-2.0` model and a text prompt.
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"content": [
{
"type": "text",
"text": "Create a calm 5 second video of a ceramic mug on a kitchen table in morning light"
}
],
"resolution": "1080p",
"ratio": "16:9",
"duration": 5
}'
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/videos `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Content-Type: application/json" `
-d '{
"model": "seedance-2.0",
"content": [
{
"type": "text",
"text": "Create a calm 5 second video of a ceramic mug on a kitchen table in morning light"
}
],
"resolution": "1080p",
"ratio": "16:9",
"duration": 5
}'
```
Copy the returned `task_...` value. Store it in your database or notes because you need it to check the video.
### 2. Poll the task
Use this endpoint with the saved task ID:
```http theme={"dark"}
GET /v1/videos/{task_id}
```
Poll until `status` is `completed`, then read the download URL from `metadata.url`. If the status is `failed`, read `error.message`, fix the request, and create a new task.
```bash Bash theme={"dark"}
TASK_ID="task_3f9a00000000000000000000000000e2"
curl -sS "https://router.flatkey.ai/v1/videos/$TASK_ID" \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
# Run the command above again until status is completed.
# Then copy metadata.url from the response and paste it below.
VIDEO_URL="PASTE_METADATA_URL_HERE"
curl -L "$VIDEO_URL" -o output.mp4
```
```powershell Windows PowerShell theme={"dark"}
$taskId = "task_3f9a00000000000000000000000000e2"
while ($true) {
$result = curl.exe -sS "https://router.flatkey.ai/v1/videos/$taskId" `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" | ConvertFrom-Json
Write-Host "status: $($result.status)"
if ($result.status -eq "completed") { break }
if ($result.status -eq "failed") {
$result | ConvertTo-Json -Depth 10
exit 1
}
Start-Sleep -Seconds 5
}
$videoUrl = $result.metadata.url
curl.exe -L "$videoUrl" -o output.mp4
```
After the task is complete, `metadata.url` downloads the MP4 file without an authorization header. Treat this URL as private and do not publish it.
## Generate from first and last frames
Give Seedance the opening image and the closing image, and it generates the motion between them. Add one `image_url` item with `role: "first_frame"` and one with `role: "last_frame"`. The text prompt describes the transition.
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"content": [
{
"type": "text",
"text": "The red ball rolls smoothly across the table from left to right and turns blue"
},
{
"type": "image_url",
"image_url": { "url": "https://example.com/first.png" },
"role": "first_frame"
},
{
"type": "image_url",
"image_url": { "url": "https://example.com/last.png" },
"role": "last_frame"
}
],
"resolution": "720p",
"ratio": "16:9",
"duration": 5,
"generate_audio": false
}'
```
```python Python theme={"dark"}
import base64
import requests
def data_uri(path):
with open(path, "rb") as f:
return "data:image/png;base64," + base64.b64encode(f.read()).decode()
response = requests.post(
"https://router.flatkey.ai/v1/videos",
headers={"Authorization": "Bearer YOUR_FLATKEY_API_KEY"},
json={
"model": "seedance-2.0",
"content": [
{"type": "text", "text": "The red ball rolls smoothly across the table from left to right and turns blue"},
{"type": "image_url", "image_url": {"url": data_uri("first.png")}, "role": "first_frame"},
{"type": "image_url", "image_url": {"url": data_uri("last.png")}, "role": "last_frame"},
],
"resolution": "720p",
"ratio": "16:9",
"duration": 5,
"generate_audio": False,
},
)
print(response.json()["id"])
```
Each image can be a public HTTPS URL or a `data:image/...;base64,` data URI. Use images with the same aspect ratio as `ratio`. Poll and download the result the same way as in step 2 of [Call Seedance](#call-seedance).
To chain clips, add `"return_last_frame": true`. The completed task then includes `metadata.last_frame_url`; download that image and pass it as the `first_frame` of the next task.
## More options
These request fields are optional. See the [API reference](/api-reference/seedance-video-generation) for details.
| Goal | Field |
| - | - |
| Get notified instead of polling | `callback_url`: Flatkey `POST`s the task to your HTTPS endpoint when its status changes. See [Task callbacks](/api-reference/seedance-video-generation#task-callbacks). |
| Run a task ahead of others in the queue | `priority`: `0`–`9`, higher runs first. |
| Get a MOV file for post-production | `output_format: "mov"` (`seedance-2.5` only). |
| Edit or extend an existing video | `seedance-2.5` with a `reference_video` and `omni_reference_task_type`. See [Video editing](/api-reference/seedance-video-generation#video-editing). |
| Drop a task that waits too long | `execution_expires_after`: seconds before the task expires. Default: 48 hours. |
## Use the asset library
Use the asset library when you want to register a reference once, wait until it is ready, and reuse it in Seedance requests. There are two asset types:
| Asset type | What it is for |
| - | - |
| Virtual assets | Product images, background videos, audio, or other non-person references. |
| Real-person assets | A specific person's face, voice, or video. This capability is limited access and must be enabled for your account. |
Use the `asset://ast_...` URI returned by Flatkey in your Seedance requests.
### Virtual assets
Virtual assets do not require real-person verification. Create them from a public URL or upload a local file, then reuse the returned Flatkey asset URI.
#### 1. Create a virtual asset from a public HTTPS URL
Send a public `https://` URL.
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/assets \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://cdn.example.com/reference/product.png",
"asset_type": "Image",
"moderation": {
"strategy": "Default"
}
}'
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/assets `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Content-Type: application/json" `
-d '{
"url": "https://cdn.example.com/reference/product.png",
"asset_type": "Image",
"moderation": {
"strategy": "Default"
}
}'
```
The response includes an `id`, for example `ast_1234567890abcdef1234567890abcdef`, and a status such as `Processing`. Build the reusable URI by adding `asset://` before the ID:
```text theme={"dark"}
asset://ast_1234567890abcdef1234567890abcdef
```
Do not send `model` when you create, upload, or query an asset. Flatkey derives the relevant Seedance models from the API key used for the request.
#### 2. Upload a local virtual asset
For a local image, video, or audio file, send `multipart/form-data` to `POST /v1/assets/upload`. Use one `file` field. `asset_type` can be `Image`, `Video`, or `Audio`.
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/assets/upload \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-F "file=@./reference-a.png" \
-F "asset_type=Image"
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/assets/upload `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-F "file=@./reference-a.png" `
-F "asset_type=Image"
```
The response uses the same asset shape as URL creation. Save its `id` or `asset_url` for polling and later video requests.
#### 3. Poll until the selected model is available
Query every asset with the same API key that will create the video task. For example, check two image assets separately:
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/assets/ast_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
curl -sS https://router.flatkey.ai/v1/assets/ast_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/assets/ast_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
curl.exe -sS https://router.flatkey.ai/v1/assets/ast_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
Every asset response includes `available_models`. `available_models` is always an array and lists the models that can use this asset now. A partially ready asset can look like this:
```json theme={"dark"}
{
"id": "ast_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"object": "asset",
"asset_type": "Image",
"status": "Processing",
"available_models": ["seedance-2.0-fast"],
"asset_url": "asset://ast_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"created_at": 1785292000
}
```
`status` is the aggregate state across the relevant Seedance models for this API key:
| Status | Meaning |
| - | - |
| `Creating` | The source upload has not finished. |
| `Processing` | At least one relevant model is still being prepared. Check `available_models`; another model may already be usable. |
| `Active` | Every relevant model for this API key is ready. |
| `Failed` | The source failed, or readiness could not be established for at least one relevant model. |
| `Expired` | The source can no longer be used. Create a new asset. |
You do not need to wait for aggregate `Active` when your chosen model is already listed. Before creating a task, make sure the selected model appears in every asset's `available_models`. For fast use `seedance-2.0-fast`; for pro use `seedance-2.0`. The task creation request checks readiness again, so retry polling if routing changed between the GET and POST requests.
The `asset://` URI identifies the asset but does not prove readiness by itself. `Deleting` means deletion is in progress and the asset cannot be used for a new task. After deletion completes, `GET /v1/assets/{asset_id}` returns `404 asset_not_found`; do not wait for a pollable `Deleted` status.
#### 4. Call Seedance with two virtual assets
Put each `asset://ast_...` URI in the media field that matches the asset type: `image_url.url` for `Image`, `video_url.url` for `Video`, or `audio_url.url` for `Audio`. A Seedance request needs text or at least one image/video; audio alone is not enough. This example starts only after both assets list `seedance-2.0-fast` in `available_models`.
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0-fast",
"content": [
{
"type": "image_url",
"image_url": {
"url": "asset://ast_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
"role": "reference_image"
},
{
"type": "image_url",
"image_url": {
"url": "asset://ast_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"role": "reference_image"
},
{
"type": "text",
"text": "Create a clean studio product video while keeping the product consistent"
}
],
"resolution": "1080p",
"ratio": "16:9",
"duration": 5
}'
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/videos `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Content-Type: application/json" `
-d '{
"model": "seedance-2.0-fast",
"content": [
{
"type": "image_url",
"image_url": {
"url": "asset://ast_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
},
"role": "reference_image"
},
{
"type": "image_url",
"image_url": {
"url": "asset://ast_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
},
"role": "reference_image"
},
{
"type": "text",
"text": "Create a clean studio product video while keeping the product consistent"
}
],
"resolution": "1080p",
"ratio": "16:9",
"duration": 5
}'
```
Copy the returned `task_...` and use `GET /v1/videos/{task_id}` from the first section to download the finished video from `metadata.url`.
### Real-person assets
Real-person assets are for a specific person's face, voice, or video. This capability is limited access. Use it only after Flatkey enables it for your account.
You will create a profile, send `verification_url` to the person, wait until the profile is `active`, create an asset, wait until the asset `status` is `Active`, then call Seedance with `asset://ast_...`.
| Endpoint | When to use it |
| - | - |
| `POST /v1/real-persons` | Create a real-person profile and first verification link. |
| `GET /v1/real-persons` | List your real-person profiles. |
| `POST /v1/real-persons/{person_id}/verification-sessions` | Create a new verification link if needed. |
| `GET /v1/real-persons/{person_id}` | Poll the profile status. |
| `POST /v1/real-persons/{person_id}/assets` | Create an asset from a public URL or a local file. |
| `GET /v1/real-persons/{person_id}/assets` | List assets under one profile. |
File size limits:
| File type | Limit |
| - | - |
| Image | \< 30 MiB |
| Video | \<= 50 MiB |
| Audio | \<= 15 MiB |
Write requests in this section require `Idempotency-Key`. Replace each `YOUR_UNIQUE_KEY_...` placeholder with a new UUID for every new write request. Reuse a key only when retrying the exact same request.
#### 1. Create a real-person profile
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/real-persons \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_PROFILE" \
-H "Content-Type: application/json" \
-d '{
"name": "My first real-person profile"
}'
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/real-persons `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_PROFILE" `
-H "Content-Type: application/json" `
-d '{
"name": "My first real-person profile"
}'
```
The response includes a profile `id`, a `status`, and a one-time `verification_url`. Send `verification_url` to the real person and ask them to open it themselves. Do not publish this link.
If the link expires or the person needs another link, create a new verification session:
```bash Bash theme={"dark"}
curl -sS -X POST https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/verification-sessions \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_VERIFICATION_SESSION"
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS -X POST https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/verification-sessions `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_VERIFICATION_SESSION"
```
After the person finishes the verification page, continue with the next step.
#### 2. Poll until the profile is active
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
Wait until the profile status is `active` before creating assets. If it is `pending_verification` or `verifying`, wait and poll again. If it is `failed` or `expired`, create a new verification session.
#### 3. Create a real-person asset from a public URL
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/assets \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_URL_ASSET" \
-H "Content-Type: application/json" \
-d '{
"url": "https://cdn.example.com/reference/person.png",
"asset_type": "Image",
"name": "Front-facing reference"
}'
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/assets `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_URL_ASSET" `
-H "Content-Type: application/json" `
-d '{
"url": "https://cdn.example.com/reference/person.png",
"asset_type": "Image",
"name": "Front-facing reference"
}'
```
#### 4. Upload a local file
Local upload uses `multipart/form-data` and is available only for real-person assets. Use one `file` field and let curl set the multipart boundary.
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/assets \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_FILE_ASSET" \
-F "asset_type=Image" \
-F "name=Front-facing reference" \
-F "file=@./person-reference.png"
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/assets `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Idempotency-Key: YOUR_UNIQUE_KEY_FOR_THIS_FILE_ASSET" `
-F "asset_type=Image" `
-F "name=Front-facing reference" `
-F "file=@./person-reference.png"
```
The create response includes an asset ID or `asset_uri`, for example `asset://ast_1234567890abcdef1234567890abcdef`.
#### 5. Poll readiness and call Seedance
Real-person asset responses do not include `available_models`. List the assets under the profile until the asset `status` is `Active`:
```bash Bash theme={"dark"}
curl -sS "https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/assets?limit=20" \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS "https://router.flatkey.ai/v1/real-persons/rph_1234567890abcdef/assets?limit=20" `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
If the listed asset is still `Processing`, wait and poll again. If it is `Failed`, create a new asset from a better reference.
After the asset is `Active`, call Seedance with the `asset://ast_...` URI.
```bash Bash theme={"dark"}
curl -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0",
"content": [
{
"type": "text",
"text": "Create a short greeting video with natural movement"
},
{
"type": "image_url",
"image_url": {
"url": "asset://ast_1234567890abcdef1234567890abcdef"
},
"role": "reference_image"
}
],
"resolution": "1080p",
"ratio": "16:9",
"duration": 5
}'
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS https://router.flatkey.ai/v1/videos `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" `
-H "Content-Type: application/json" `
-d '{
"model": "seedance-2.0",
"content": [
{
"type": "text",
"text": "Create a short greeting video with natural movement"
},
{
"type": "image_url",
"image_url": {
"url": "asset://ast_1234567890abcdef1234567890abcdef"
},
"role": "reference_image"
}
],
"resolution": "1080p",
"ratio": "16:9",
"duration": 5
}'
```
Copy the returned `task_...` and use `GET /v1/videos/{task_id}` from the first section to download the finished video from `metadata.url`.
#### 6. Delete an asset when you no longer need it
```bash Bash theme={"dark"}
curl -sS -X DELETE https://router.flatkey.ai/v1/assets/ast_1234567890abcdef1234567890abcdef \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
```powershell Windows PowerShell theme={"dark"}
curl.exe -sS -X DELETE https://router.flatkey.ai/v1/assets/ast_1234567890abcdef1234567890abcdef `
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
The delete request returns `204 No Content`. After deletion completes, `GET /v1/assets/{asset_id}` returns `404 asset_not_found`.
# Generate text with Flatkey
Source: https://docs.flatkey.ai/guides/text-generation
Call any chat model through one endpoint. Covers model choice, streaming, tool calling, structured outputs, and reading token usage.
Base URL: `https://router.flatkey.ai`
Text generation runs through `POST /v1/chat/completions`, which matches the OpenAI Chat Completions API exactly. Any OpenAI-compatible client works after you change the base URL.
Keep your existing request code and point `base_url` at `https://router.flatkey.ai/v1`.
Switch models by changing the `model` field. Nothing else changes.
## Make your first call
```python Python theme={"dark"}
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["FLATKEY_API_KEY"],
base_url="https://router.flatkey.ai/v1",
)
response = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "Explain vector databases in two sentences."}],
)
print(response.choices[0].message.content)
```
```typescript TypeScript theme={"dark"}
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.FLATKEY_API_KEY,
baseURL: "https://router.flatkey.ai/v1",
});
const response = await client.chat.completions.create({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "Explain vector databases in two sentences." }],
});
console.log(response.choices[0].message.content);
```
```bash cURL theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"messages": [
{ "role": "user", "content": "Explain vector databases in two sentences." }
]
}'
```
## Choose a model
List everything your account can reach and keep the text models:
```bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/models \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
| jq '.data[] | select(.type == "text") | .id'
```
Browse the same list with pricing, context length, and latency on the [model directory](https://flatkey.ai/models).
A practical starting point:
| Need | Try |
| - | - |
| Everyday coding and reasoning | `claude-sonnet-5`, `gpt-5.6-sol` |
| Long documents or whole repositories | `deepseek-v4-pro`, `kimi-k3` |
| High volume at low cost | `deepseek-v4-flash`, `gemini-2.5-flash-lite` |
| Agentic coding | `glm-5.3`, `claude-opus-5` |
## Stream the response
Set `stream: true` to receive tokens as they are produced:
```python theme={"dark"}
stream = client.chat.completions.create(
model="gemini-2.5-flash",
messages=[{"role": "user", "content": "Write a haiku about routing."}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="", flush=True)
```
Each chunk carries a partial `delta` instead of a complete message.
## Call your own functions
Pass tool definitions and the model decides when to invoke them:
```python theme={"dark"}
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]
response = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "What is the weather in Paris?"}],
tools=tools,
)
print(response.choices[0].message.tool_calls)
```
To finish the loop, append the assistant message, then one message per tool result keyed by `tool_call_id`, and send the whole thread back.
When a tool fires, `finish_reason` is `tool_calls`, not `stop`. Code that only checks for `stop` will drop the call silently.
Tool calling is a capability of the model, not of Flatkey. The GPT, Claude, Gemini, Qwen, DeepSeek, and GLM families support it. Image, video, and speech models ignore a `tools` array.
## Force a JSON shape
Use `response_format` when you need to parse the answer:
```python theme={"dark"}
response = client.chat.completions.create(
model="gpt-5.6-sol",
messages=[{"role": "user", "content": "Extract the city and country."}],
response_format={
"type": "json_schema",
"json_schema": {
"name": "location",
"strict": True,
"schema": {
"type": "object",
"properties": {"city": {"type": "string"}, "country": {"type": "string"}},
"required": ["city", "country"],
"additionalProperties": False,
},
},
},
)
```
`{"type": "json_object"}` also works when you only need valid JSON without a fixed schema.
## Read token usage
Every response carries the counts you are billed on:
```json theme={"dark"}
"usage": {
"prompt_tokens": 28,
"completion_tokens": 22,
"total_tokens": 50
}
```
Different model families tokenize differently, so the same text costs a different number of tokens on different models. Use `usage` rather than estimating from character counts. Per-request cost also appears in [Usage logs](/dashboard/usage).
## Troubleshooting
**`No available channel for model ...`**
That model is not routable right now. Pick another id from `/v1/models`. Retrying the same model does not clear this.
**The response stops mid-sentence**
`finish_reason` is `length`. Raise `max_tokens`.
**Tool calls never fire**
Confirm the model supports tools, and check that `tool_choice` is not set to `none`.
## Next steps
Every parameter and response field.
Drop Flatkey into an existing OpenAI project.
# Generate video with Flatkey
Source: https://docs.flatkey.ai/guides/video-generation
Submit a video task, poll it, and download the MP4. Covers the two request formats, per-model resolutions, and the parameters that do and do not apply.
Base URL: `https://router.flatkey.ai`
Flatkey generates video through one asynchronous endpoint. You submit a task, poll it until it finishes, then download the MP4. This page covers what is shared across every video model. For a model's own options, continue to [Seedance](/guides/seedance) or [MiniMax H3](/guides/minimax-h3).
Create the task with `POST /v1/videos`, poll it with `GET /v1/videos/{task_id}`, and download the MP4 from `metadata.url`.
Some models take a `content` array, others take a `prompt` string. Sending the wrong one returns `400`.
Use this authorization header on every request on this page, including the download:
```http theme={"dark"}
Authorization: Bearer YOUR_FLATKEY_API_KEY
```
## Find a video model
List every model and keep the ones whose `type` is `video`:
```bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/models \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
| jq '.data[] | select(.type == "video") | .id'
```
```json theme={"dark"}
"grok-imagine-video"
"grok-imagine-video-1.5"
"MiniMax-H3"
"seedance-2.5"
"veo-3.1-fast-generate-preview"
"veo-3.1-generate-preview"
```
`/v1/models` ignores query parameters. Filtering happens in your own code, as in the `jq` example above.
You can also browse the same list with pricing on the [model directory](https://flatkey.ai/models).
## Pick the right request shape
The endpoint accepts two different shapes, and each model takes exactly one of them.
| Model | Prompt field |
| - | - |
| `seedance-2.5` | `content` array |
| `seedance-2.0-pro` | `content` array |
| `MiniMax-H3` | `content` array |
| `grok-imagine-video` | `prompt` string |
| `grok-imagine-video-1.5` | `prompt` string |
| `veo-3.1-generate-preview` | `prompt` string |
| `veo-3.1-fast-generate-preview` | `prompt` string |
Sending a `content` array to a `prompt` model returns:
```json theme={"dark"}
{ "code": "invalid_request", "message": "prompt is required" }
```
### The `content` shape
```bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5",
"content": [
{ "type": "text", "text": "a quiet neon-lit street at dusk, slow camera drift" }
],
"duration": 5,
"resolution": "720p"
}'
```
The array also carries images. Add an `image_url` entry to interpolate between frames or supply a reference. Field names differ by model, so follow the model's own guide.
### The `prompt` shape
```bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/videos \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-video",
"prompt": "a quiet neon-lit street at dusk, slow camera drift",
"duration": 10
}'
```
## Resolutions differ by model
There is no shared set of resolution values. Sending an unsupported one returns `400` before the task is created.
| Model | Accepted values | Notes |
| - | - | - |
| `seedance-2.5` | `480p`, `720p` | `1080p` returns `unsupported resolution` |
| `MiniMax-H3` | `768P`, `2K` | Uppercase `P`. Other values return `resolution must be 768P or 2K` |
| `grok-imagine-video` | `720p` | Output is `848x480` regardless |
| `veo-3.1-*` | `720p` | |
Check the model's own guide before choosing a resolution. `1080p` is rejected by `seedance-2.5`, and `720p` is rejected by `MiniMax-H3`.
## Request parameters
| Parameter | Type | Required | Description |
| - | - | - | - |
| `model` | string | Yes | Model id from `/v1/models` |
| `content` | array | Conditional | Prompt and media for `content`-shape models |
| `prompt` | string | Conditional | Prompt for `prompt`-shape models |
| `duration` | integer | No | Length in seconds. `MiniMax-H3` accepts `4` through `15` |
| `resolution` | string | No | See the table above |
`aspect_ratio` and `seed` are accepted without an error but do not change the output on every model. On `grok-imagine-video`, requesting `9:16` still returns an `848x480` landscape clip, and the same `seed` produces a different file each time. Where a model supports orientation, it is documented on that model's own page. Do not rely on these two fields for cross-model behavior.
## Poll the task
A successful submission returns a task immediately:
```json theme={"dark"}
{
"id": "task_aaaaaaaaaaaaaaaa",
"task_id": "task_aaaaaaaaaaaaaaaa",
"object": "video",
"model": "seedance-2.5",
"status": "queued",
"progress": 0,
"created_at": 1787110914
}
```
Poll with the task id until the status is terminal:
```bash theme={"dark"}
curl --fail-with-body -sS https://router.flatkey.ai/v1/videos/task_aaaaaaaaaaaaaaaa \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY"
```
```json theme={"dark"}
{
"id": "task_aaaaaaaaaaaaaaaa",
"status": "completed",
"progress": 100,
"completed_at": 1787111135,
"metadata": {
"url": "https://router.flatkey.ai/v1/videos/task_aaaaaaaaaaaaaaaa/content"
}
}
```
| Status | Meaning |
| - | - |
| `queued` | Accepted and waiting |
| `in_progress` | Generating |
| `completed` | Ready to download from `metadata.url` |
| `failed` | Generation failed. Read the error field |
Poll about every 15 seconds. A five-second clip usually finishes in two to four minutes.
## Download the MP4
The download URL arrives in `metadata.url`, and it needs the same authorization header:
```bash theme={"dark"}
curl -L "https://router.flatkey.ai/v1/videos/task_aaaaaaaaaaaaaaaa/content" \
-H "Authorization: Bearer YOUR_FLATKEY_API_KEY" \
-o output.mp4
```
## End-to-end example
```python theme={"dark"}
import os, time, requests
BASE = "https://router.flatkey.ai/v1"
HEAD = {"Authorization": f"Bearer {os.environ['FLATKEY_API_KEY']}",
"Content-Type": "application/json"}
task = requests.post(f"{BASE}/videos", headers=HEAD, json={
"model": "seedance-2.5",
"content": [{"type": "text", "text": "a quiet neon-lit street at dusk"}],
"duration": 5,
"resolution": "720p",
}).json()
task_id = task["task_id"]
while True:
time.sleep(15)
state = requests.get(f"{BASE}/videos/{task_id}", headers=HEAD).json()
if state["status"] == "completed":
mp4 = requests.get(state["metadata"]["url"], headers=HEAD).content
open("output.mp4", "wb").write(mp4)
break
if state["status"] == "failed":
raise RuntimeError(state)
```
## Troubleshooting
**`prompt is required`**
You sent a `content` array to a model that expects `prompt`. Check the table in [Pick the right request shape](#pick-the-right-request-shape).
**`unsupported resolution` or `resolution must be 768P or 2K`**
The value is valid for a different model. See [Resolutions differ by model](#resolutions-differ-by-model).
**`No available channel for model ...`**
The model is not routable right now. Pick another video model rather than retrying — this error does not clear on its own. Check the [model directory](https://flatkey.ai/models) for current availability.
**`duration must be between 4 and 15`**
`MiniMax-H3` requires a `duration` in that range. Send one explicitly.
**The task stays `queued`**
Video generation takes minutes, not seconds. Keep polling at a 15-second interval before treating it as stuck.
## Model guides
Text-to-video plus the virtual and real-person asset library.
First and last frame control, reference media, and task settings.
# Flatkey documentation
Source: https://docs.flatkey.ai/index
Guides, SDK examples, and API reference for building with Flatkey.
Connect GPT, Claude, Gemini, DeepSeek, and more with one OpenAI-compatible API key. Set your client’s `base_url` to `https://router.flatkey.ai/v1` to get started.
Make your first API call in under 5 minutes
Full endpoint reference with request and response examples
Browse all 300+ models, providers, and live pricing
Understand subscription plans, usage billing, and account credits
## How Flatkey works
Sign up at [console.flatkey.ai](https://console.flatkey.ai/sign-up) and choose the subscription plan that fits your usage.
Open the **Keys** section in the dashboard and create a new API key. Keep it secret — treat it like a password.
Set `base_url` to `https://router.flatkey.ai/v1` in your OpenAI-compatible SDK. No other code changes required.
Track every request, token count, and dollar spent in the **Usage** dashboard. Set quotas to cap spend.
## Why teams use Flatkey
Access GPT, Claude, Gemini, DeepSeek, Qwen, GLM, and more with a single API key and base URL.
API prices are at least 20% below the official API list price for the same model.
Automatic failover across upstream providers. Live health dashboards for every model.
Review the current privacy terms of Flatkey and your selected provider. Contact support to confirm retention periods.
OpenAI-compatible API. Change one line of code — no SDK rewrites, no migration overhead.
Choose a plan that fits your workload and monitor usage in real time.
## Explore the docs
Learn how to create and use your Flatkey API key
Use Flatkey with the official OpenAI Python or Node.js SDK
Route Claude requests through Flatkey with the Anthropic SDK
View real-time success rates and latency for every model
# Introduction to Flatkey
Source: https://docs.flatkey.ai/introduction
Guides, SDK examples, and API reference for building with Flatkey.
Connect GPT, Claude, Gemini, DeepSeek, and more with one OpenAI-compatible API key. Set your client’s `base_url` to `https://router.flatkey.ai/v1` to get started.
## What Flatkey gives you
A single API key and base URL routes to GPT, Claude, Gemini, DeepSeek, Qwen, GLM, and more. No per-provider keys to manage.
API prices are at least 20% below the official API list price for the same model. Top up \$200 and get \$100 in bonus credit.
Automatic failover across upstream providers. Live health dashboards show real success rates from production traffic.
Review the current privacy terms of Flatkey and your selected provider. Contact support to confirm retention periods.
Top up once, consume as you go. No subscriptions, no surprise invoices.
OpenAI-compatible endpoints: chat completions, responses, embeddings, image generation, and models list.
## Supported providers
Flatkey routes to official APIs from every major AI vendor:
| Provider | Example models |
| - | - |
| OpenAI | gpt-4o, gpt-4o-mini, gpt-5, gpt-image-2 |
| Anthropic | claude-opus-4, claude-sonnet-4, claude-haiku-4 |
| Google | gemini-2.5-pro, gemini-2.5-flash, gemini-flash-latest |
| DeepSeek | deepseek-v3, deepseek-v4-pro, deepseek-v4-flash |
| Qwen | qwen3.5-plus, qwen3.7-max, qwen3.5-flash |
| Z.ai / GLM | glm-4.7, glm-5-turbo, glm-5.2 |
| ByteDance | seedance-2.0, seedance-2.5 |
See the full [Model Directory](https://flatkey.ai/models) for all 300+ models with live pricing and health data.
## How it works
Flatkey sits between your application and AI providers. When you send a request:
1. Your client calls `https://router.flatkey.ai/v1` with your Flatkey API key.
2. Flatkey authenticates the request and deducts the cost from your prepaid balance.
3. The request is forwarded to the official provider API (OpenAI, Anthropic, etc.).
4. The response is returned directly to your client — no transformation of the content.
Review Flatkey's current privacy terms and those of your selected provider for data handling requirements. Contact [support@flatkey.ai](mailto:support@flatkey.ai) to confirm retention periods or discuss a security review.
## Next steps
Create an account and make your first API call in under 5 minutes
Learn how to create and use your Flatkey API key
Use Flatkey with the official OpenAI SDK — Python or Node.js
Full endpoint reference for all supported API methods
# Quickstart: Choose how you'll use Flatkey
Source: https://docs.flatkey.ai/quickstart
Four ways to use Flatkey — the REST API, the OpenAI SDK, the Flatkey CLI, or your coding agent. One key and one model catalog behind all of them.
Flatkey gives you one key and four ways to spend it. All four share the same account, the same balance, and the same model catalog — so the only question is which one fits how you work.
You do not have to choose once and for all. Start with whichever is closest to what you are doing today, and add the others later against the same key.
## Before you start
Create a key at [console.flatkey.ai/keys](https://console.flatkey.ai/keys). Flatkey keys begin with `sk-`. Every option below uses that one key, so keep it in an environment variable and out of source control.
Each panel in the Console also lets you pick an existing key instead of creating a new one, and drops it straight into the snippet it shows you.
## API for developers
Call any model over plain HTTP with an OpenAI-compatible request. No SDK, no dependencies — if you have an HTTP client, you are ready.
The Console builds the request for you: choose a key, choose a model, and copy the snippet in **cURL**, **Node.js**, or **Python**.
```bash theme={"dark"}
curl https://router.flatkey.ai/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $FLATKEY_API_KEY" \
-d '{"model":"gpt-4o-mini","messages":[{"role":"user","content":"Say hello in one sentence."}]}'
```
Base URL, authentication, available endpoints, and error handling.
## SDKs for developers
Keep the OpenAI SDK you already use and point it at Flatkey. Your request code does not change — only the base URL does.
Same idea as above: pick a key and a model, then copy the setup for your language.
```python theme={"dark"}
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["FLATKEY_API_KEY"],
base_url="https://router.flatkey.ai/v1",
)
```
Python and Node.js setup, streaming, and tool calling.
Route Claude requests through Flatkey the same way.
## Flatkey CLI
Generate images and video from your terminal, or let an AI assistant drive the CLI for you.
```bash theme={"dark"}
npm i -g @flatkey-ai/cli
```
```bash theme={"dark"}
flatkey login
```
Ask your AI assistant for what you want, and let it run the CLI:
```text theme={"dark"}
Use Flatkey CLI to generate a "beautiful sunrise" picture.
```
Commands, JSON output for agents, and error recovery.
## Codex & Claude Code
Connect your coding agent once, then use Flatkey from the projects you already have open.
The Console gives you a single install command for **macOS**, **Linux**, or **Windows**. It installs the agent if you do not have it, asks for your key, and writes the configuration for you.
The installer is interactive. It asks whether you want Claude Code or Codex CLI, and it needs Node.js already installed.
Manual setup or CC Switch.
Route Codex CLI through Flatkey.
Configure the desktop app.
## Check that it worked
Whichever option you picked, open **Usage Logs** in the [Console](https://console.flatkey.ai) after your first request. You should see it listed with the model, token counts, latency, and cost.
If nothing appears, wait a few seconds and search again, then confirm the API key and base URL the request actually used.
## Next steps
Every supported model with live pricing and health.
Status codes, response formats, and what to do about them.
Streaming, tool calling, and structured output.
Submit a task, poll it, and download the MP4.
# Flatkey for Enterprise
Source: https://docs.flatkey.ai/reference/enterprise
Committed-volume discounts, custom routing, invoicing, and procurement for teams running high volume on Flatkey.
Running high volume? Open the [contact page](https://flatkey.ai/contact), find the **Enterprise** plan, and select **Contact sales**. Enterprise contracts cover committed-volume discounts, custom routing, invoicing, and procurement.
Self-serve plans cover most individual and small-team workloads. An Enterprise contract makes sense once your volume, payment process, or procurement requirements outgrow them.
## Contracts and billing
Consider an Enterprise contract when your usage outgrows the self-serve plans, when your team needs invoicing instead of card payments, or when procurement requires a signed agreement. If you are unsure, share your monthly token volume and target models with [support@flatkey.ai](mailto:support@flatkey.ai).
Enterprise pricing is custom. Contracts cover committed-volume discounts, custom routing, invoicing, and procurement support. Exact terms depend on your volume and model mix — see the **Enterprise** plan on the [pricing page](https://flatkey.ai/pricing).
Yes. Enterprise accounts support invoice billing, custom monthly usage, team procurement, and dedicated routing discounts. Contact [support@flatkey.ai](mailto:support@flatkey.ai) to discuss terms.
## Teams and access control
Yes. Issue a separate API key per person or per service, then use **Groups** to apply model access policies and spend quotas to each key. Every request appears in **Usage Logs** with its key, so you can attribute cost per team or per project.
Yes. Use **Groups** in the console to set model access policies and spend quotas on individual keys or groups of keys.
## Security review
Send your questionnaire to [support@flatkey.ai](mailto:support@flatkey.ai) to confirm data retention requirements and available security documentation. See [Privacy and security](/reference/faq#privacy-and-security) in the FAQ for how to request details.
## Next steps
Compare self-serve plans and contact sales about Enterprise.
Answers to common questions about billing, models, and privacy.
# Flatkey FAQ: Common Questions Answered
Source: https://docs.flatkey.ai/reference/faq
Answers to the most common questions about Flatkey — including model compatibility, billing, privacy, API key management, and enterprise options.
This page covers the most common questions about using Flatkey. If you have a question not answered here, contact [support@flatkey.ai](mailto:support@flatkey.ai) or use the live chat in the console.
## Getting started
Flatkey is an AI API gateway that provides access to 300+ official AI models from OpenAI, Anthropic, Google, DeepSeek, Qwen, GLM, and more, plus 1000+ tools — all through a single OpenAI-compatible API key. It is a drop-in replacement for any OpenAI-compatible client: change `base_url` to `https://router.flatkey.ai/v1`, use your Flatkey key, and route to any model at discounted rates.
No. One Flatkey API key routes to all 300+ models across all providers. You do not need separate OpenAI, Anthropic, or Google API keys.
Yes. Flatkey routes your requests to the official vendor APIs — OpenAI, Anthropic, Google, DeepSeek, etc. The model outputs come directly from the original provider infrastructure. Flatkey does not host, fine-tune, or modify any models.
## Billing and pricing
Flatkey is prepaid. You top up your balance (\$10, \$20, \$200, or custom enterprise) and the cost of each API request is deducted from your balance in real time. There are no subscriptions or recurring charges.
Every top-up earns bonus credit: +\$3 on \$10, +\$8 on \$20, +\$100 on \$200. Bonus credit is added to your balance instantly, never expires, and spends just like paid credit. It is not a discount code — it is real credit that appears in your balance.
Yes. You can top up as many times as you like, and you earn bonus credit on each top-up. Three \$200 top-ups earn \$300 in bonus credit total.
API requests return a `401 Insufficient Balance` error. There are no automatic charges. Top up your balance to resume. Enable low-balance alerts in **Settings → Notifications** to avoid interruptions.
## Compatibility and models
Yes. The OpenAI Python and Node.js SDKs support a `base_url` parameter. Set it to `https://router.flatkey.ai/v1` and use your Flatkey API key. No other changes are needed.
Yes. Set `base_url` to `https://router.flatkey.ai` (no `/v1`) in the Anthropic SDK constructor and use your Flatkey API key. See the [Anthropic SDK guide](/guides/anthropic-sdk) for details.
Use the same canonical model IDs you would use with the original provider — for example `gpt-4o`, `claude-sonnet-4-5`, `gemini-2.5-flash`. The full list is in the [Model Directory](https://flatkey.ai/models).
Yes. Set `stream: true` in your request. Flatkey returns server-sent events (SSE) in the same format as the OpenAI API. Streaming works for all chat and text generation models.
## Privacy and security
Review the current privacy terms of Flatkey and your selected provider. To confirm which request data is retained, for how long, and available deletion options, contact [support@flatkey.ai](mailto:support@flatkey.ai).
For a security or compliance review, contact [support@flatkey.ai](mailto:support@flatkey.ai) to request currently available documentation and confirm its scope and validity.
Revoke the key immediately from the **Keys** page in the console. Revocation is instant. Then create a new key and update your application. See [API Key Management](/dashboard/api-keys) for details.
## Enterprise
Committed-volume discounts, custom routing, invoicing, and procurement are covered on the [Enterprise](/reference/enterprise) page.
## Support
* **Email**: [support@flatkey.ai](mailto:support@flatkey.ai)
* **Live chat**: Available at [flatkey.ai](https://flatkey.ai) and in the console
* **X (Twitter)**: [@flatkey101](https://x.com/flatkey101)
# Model Health
Source: https://docs.flatkey.ai/reference/model-health
Model Health lives on flatkey.ai so the numbers stay current. This page points you there.
View live success rates and latency.
This reference moved to [flatkey.ai](https://flatkey.ai/status) so the data updates without a documentation release.
# Model List
Source: https://docs.flatkey.ai/reference/model-list
Model List lives on flatkey.ai so the numbers stay current. This page points you there.
View the full model directory with live pricing.
This reference moved to [flatkey.ai](https://flatkey.ai/models) so the data updates without a documentation release.
# Pricing
Source: https://docs.flatkey.ai/reference/pricing
Pricing lives on flatkey.ai so the numbers stay current. This page points you there.
View current token prices and top-up tiers.
This reference moved to [flatkey.ai](https://flatkey.ai/pricing) so the data updates without a documentation release.