# Flatkey API Authentication: Bearer Tokens and API Keys Source: https://docs.flatkey.ai/api-reference/authentication Authenticate Flatkey API requests by passing your API key as a Bearer token in the Authorization header. Learn key formats, errors, and best practices. Every request to the Flatkey API must include a valid API key in the `Authorization` header as a Bearer token. You create and manage keys in the [Flatkey console](https://console.flatkey.ai/keys). ## Header format ```http theme={"dark"} Authorization: Bearer sk-fk-your-api-key ``` Flatkey API keys begin with `sk-fk-`. Keys are case-sensitive and must be passed exactly as generated. ## Example request ```bash theme={"dark"} curl https://router.flatkey.ai/v1/chat/completions \ -H "Authorization: Bearer $FLATKEY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o-mini", "messages": [{"role": "user", "content": "Hello!"}] }' ``` ## Creating an API key 1. Sign in to [console.flatkey.ai](https://console.flatkey.ai) 2. Click **Keys** in the left sidebar 3. Click **Create Key** and enter a name 4. Copy the key — it is shown **only once** If you lose a key, you must create a new one. Old keys remain active until explicitly revoked. ## Authentication errors | HTTP status | Condition | Resolution | | - | - | - | | `401 Unauthorized` | Key is missing from the request | Add `Authorization: Bearer sk-fk-...` header | | `401 Unauthorized` | Key is invalid or revoked | Generate a new key in the console | | `401 Unauthorized` | Balance is \$0.00 | Top up at [console.flatkey.ai](https://console.flatkey.ai) | | `403 Forbidden` | Key lacks model access | Check key permissions in the console | ### Error response format ```json theme={"dark"} { "error": { "message": "Invalid API key provided.", "type": "invalid_request_error", "code": "invalid_api_key" } } ``` ## Security best practices * Store keys in environment variables, not in source code * Use one key per deployment environment * Revoke keys immediately if compromised * Rotate keys periodically for production workloads Never include API keys in client-side JavaScript, mobile app bundles, or public repositories. Keys can be misused to drain your balance. # Chat Completions — POST /v1/chat/completions Source: https://docs.flatkey.ai/api-reference/chat-completions Generate chat and text completions through Flatkey by sending a messages array to any supported model. Returns choices with generated text and token usage. The `/v1/chat/completions` endpoint generates a text completion based on a list of messages. It is the primary endpoint for chat, instruction-following, and text generation tasks. The request format is identical to the OpenAI Chat Completions API, so any OpenAI-compatible SDK works without modification. ## Endpoint ``` POST https://router.flatkey.ai/v1/chat/completions ``` ## Request ### Headers | Header | Value | | - | - | | `Authorization` | `Bearer $FLATKEY_API_KEY` | | `Content-Type` | `application/json` | ### Body parameters The model ID to use. See the [Model Directory](https://flatkey.ai/models) for all valid values. Example: `"gpt-4o"`, `"claude-sonnet-4-5"`, `"gemini-2.5-flash"`. An array of message objects representing the conversation. Each object must have `role` (`"system"`, `"user"`, or `"assistant"`) and `content` (string). Maximum number of tokens to generate. Defaults vary by model. Sampling temperature between 0 and 2. Higher values produce more random output. Default: 1. If `true`, the response is returned as a stream of server-sent events (SSE). Default: `false`. Nucleus sampling — only tokens in the top `top_p` probability mass are considered. Default: 1. One or more sequences where the model stops generating. Can be a string or an array of strings. A list of tool definitions for function calling. Each tool must have `type: "function"` and a `function` object with `name`, `description`, and `parameters`. Controls tool selection: `"none"`, `"auto"`, `"required"`, or a specific tool `{"type": "function", "function": {"name": "..."}}`. ## Example request ```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="gpt-4o", messages=[ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the speed of light?"}, ], max_tokens=256, temperature=0.7, ) print(response.choices[0].message.content) ``` ```bash curl theme={"dark"} curl https://router.flatkey.ai/v1/chat/completions \ -H "Authorization: Bearer $FLATKEY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "messages": [ {"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What is the speed of light?"} ], "max_tokens": 256 }' ``` ## Response ```json theme={"dark"} { "id": "chatcmpl-abc123", "object": "chat.completion", "created": 1710000000, "model": "gpt-4o", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "The speed of light in a vacuum is approximately 299,792,458 meters per second." }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 28, "completion_tokens": 22, "total_tokens": 50 } } ``` ### Response fields Unique identifier for the completion. Array of generated completion choices. The generated text. Always `"assistant"` for generated messages. Why generation stopped: `"stop"` (natural end), `"length"` (max\_tokens reached), `"tool_calls"` (tool invoked). Token counts for billing. Input token count. Output token count. Sum of prompt and completion tokens. ## Streaming Set `stream: true` to receive a stream of SSE chunks. Each chunk has the same structure but with partial `delta` content instead of a full message: ```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", ) stream = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Tell me a story."}], stream=True, ) for chunk in stream: print(chunk.choices[0].delta.content or "", end="", flush=True) ``` # Embeddings — POST /v1/embeddings Source: https://docs.flatkey.ai/api-reference/embeddings Generate vector embeddings for text using POST /v1/embeddings. Supports batch input and multiple embedding models for semantic search, clustering, and RAG. The `/v1/embeddings` endpoint generates vector embeddings for text input. Embeddings represent the semantic meaning of text as a high-dimensional float array, enabling similarity search, clustering, and retrieval-augmented generation (RAG). The endpoint is compatible with the OpenAI Embeddings API. ## Endpoint ``` POST https://router.flatkey.ai/v1/embeddings ``` ## Supported embedding models | Model | Dimensions | Provider | | - | - | - | | `gemini-embedding-001` | 3072 | Google | | `text-embedding-3-small` | 1536 | OpenAI | | `text-embedding-3-large` | 3072 | OpenAI | See the [Model Directory](https://flatkey.ai/models) for current availability and pricing. ## Request ### Headers | Header | Value | | - | - | | `Authorization` | `Bearer $FLATKEY_API_KEY` | | `Content-Type` | `application/json` | ### Body parameters Embedding model ID. Example: `"gemini-embedding-001"`, `"text-embedding-3-small"`. Text to embed. Can be a single string or an array of strings for batch embedding. `"float"` (default) returns a float array. `"base64"` returns a base64-encoded string. Number of dimensions for the output embedding. Not supported by all models. ## Example requests ```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", ) # Single text response = client.embeddings.create( model="gemini-embedding-001", input="The quick brown fox jumps over the lazy dog", ) embedding = response.data[0].embedding print(f"Embedding dimensions: {len(embedding)}") print(f"First 5 values: {embedding[:5]}") ``` ```python python batch theme={"dark"} import os from openai import OpenAI client = OpenAI( api_key=os.environ["FLATKEY_API_KEY"], base_url="https://router.flatkey.ai/v1", ) # Batch embedding texts = [ "What is machine learning?", "How does neural network training work?", "Explain gradient descent", ] response = client.embeddings.create( model="gemini-embedding-001", input=texts, ) for i, item in enumerate(response.data): print(f"{texts[i]}: {len(item.embedding)} dimensions") ``` ```bash curl theme={"dark"} curl https://router.flatkey.ai/v1/embeddings \ -H "Authorization: Bearer $FLATKEY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gemini-embedding-001", "input": "The quick brown fox jumps over the lazy dog" }' ``` ## Response ```json theme={"dark"} { "object": "list", "data": [ { "object": "embedding", "index": 0, "embedding": [0.0023064255, -0.009327292, ...] } ], "model": "gemini-embedding-001", "usage": { "prompt_tokens": 9, "total_tokens": 9 } } ``` ### Response fields Array of embedding objects, one per input string. Position in the input array. Float array of the embedding vector. Number of tokens processed. ## Cosine similarity example ```python python theme={"dark"} import os import numpy as np from openai import OpenAI client = OpenAI( api_key=os.environ["FLATKEY_API_KEY"], base_url="https://router.flatkey.ai/v1", ) def embed(text): return client.embeddings.create( model="gemini-embedding-001", input=text, ).data[0].embedding def cosine_similarity(a, b): a, b = np.array(a), np.array(b) return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) q = embed("capital city of France") doc1 = embed("Paris is the capital and most populous city of France.") doc2 = embed("The Eiffel Tower is a famous landmark.") print(cosine_similarity(q, doc1)) # higher — more relevant print(cosine_similarity(q, doc2)) # lower ``` # Flatkey API Errors: HTTP Status Codes and Troubleshooting Source: https://docs.flatkey.ai/api-reference/errors Reference for all Flatkey API HTTP error codes, error response formats, and recommended fixes for 400, 401, 403, 429, 500, and 503 responses. Flatkey returns standard HTTP status codes alongside a JSON error body that describes what went wrong. Error responses follow the same format as the OpenAI API, so existing error-handling code works without modification. ## Error response format All errors return a JSON object with an `error` field: ```json theme={"dark"} { "error": { "message": "A human-readable description of the error.", "type": "invalid_request_error", "code": "invalid_api_key" } } ``` ## HTTP status codes ### 400 Bad Request The request body is malformed or missing a required field. ```json theme={"dark"} { "error": { "message": "Missing required field: model", "type": "invalid_request_error", "code": "missing_required_field" } } ``` **Common causes:** * Missing `model` or `messages` field in chat completions * Invalid JSON in the request body * `messages` array is empty *** ### 401 Unauthorized Authentication failed. Either the key is invalid, missing, or the account balance is zero. ```json theme={"dark"} { "error": { "message": "Invalid API key provided.", "type": "authentication_error", "code": "invalid_api_key" } } ``` **Common causes and fixes:** | `code` value | Cause | Fix | | - | - | - | | `invalid_api_key` | Key is missing, malformed, or revoked | Check and regenerate your key | | `insufficient_balance` | Account balance is \$0.00 | Top up at [console.flatkey.ai](https://console.flatkey.ai) | *** ### 403 Forbidden The API key does not have permission to access the requested model. ```json theme={"dark"} { "error": { "message": "This key does not have access to model claude-opus-4-5.", "type": "permission_error", "code": "model_not_allowed" } } ``` **Fix:** Check model access settings for this key in the [console](https://console.flatkey.ai/keys). *** ### 404 Not Found The model ID is not recognized by Flatkey. ```json theme={"dark"} { "error": { "message": "Model 'gpt-unknown' not found.", "type": "invalid_request_error", "code": "model_not_found" } } ``` **Fix:** Verify the model ID in the [Model Directory](https://flatkey.ai/models). IDs are case-sensitive. *** ### 429 Too Many Requests Your key has exceeded the rate limit. ```json theme={"dark"} { "error": { "message": "Rate limit exceeded. Please retry after 1 second.", "type": "rate_limit_error", "code": "rate_limit_exceeded" } } ``` **Fix:** Implement exponential backoff and retry. Contact [support@flatkey.ai](mailto:support@flatkey.ai) if you need higher rate limits. *** ### 500 Internal Server Error An unexpected error occurred while processing your request. ```json theme={"dark"} { "error": { "message": "The model encountered an error processing your request.", "type": "api_error", "code": "internal_error" } } ``` **Fix:** Retry the request. If the error persists, contact [support@flatkey.ai](mailto:support@flatkey.ai). *** ### 503 Service Unavailable The model provider is temporarily unavailable. ```json theme={"dark"} { "error": { "message": "Upstream provider temporarily unavailable. Please retry.", "type": "service_error", "code": "upstream_unavailable" } } ``` **Fix:** Retry with exponential backoff. The issue typically resolves within seconds. If it persists, contact [support@flatkey.ai](mailto:support@flatkey.ai). *** ## Retry strategy For `429`, `500`, and `503` errors, implement exponential backoff: ```python python theme={"dark"} import time import random from openai import OpenAI, RateLimitError, APIStatusError client = OpenAI( api_key="sk-fk-your-key", base_url="https://router.flatkey.ai/v1", ) def call_with_retry(model, messages, max_retries=5): for attempt in range(max_retries): try: return client.chat.completions.create( model=model, messages=messages, ) except RateLimitError: wait = (2 ** attempt) + random.uniform(0, 1) time.sleep(wait) except APIStatusError as e: if e.status_code in (500, 503): wait = (2 ** attempt) + random.uniform(0, 1) time.sleep(wait) else: raise raise RuntimeError("Max retries exceeded") ``` The OpenAI SDK has built-in retry logic via the `max_retries` parameter. Set `max_retries=3` in the OpenAI client constructor to automatically retry on transient errors. # Image Generation and Editing API Source: https://docs.flatkey.ai/api-reference/image-generation Generate or edit images through the OpenAI Images-compatible /v1/images/generations and /v1/images/edits endpoints. Use `/v1/images/generations` to generate an image from a text prompt and `/v1/images/edits` to edit an input image. Both endpoints use OpenAI Images-compatible request shapes. Available operations depend on the model and channel exposed by your Flatkey service. Gemini image models use the native Gemini `generateContent` endpoint and cannot be called through these OpenAI Images endpoints. See the [Gemini image model guide](/guides/image-generation#use-gemini-image-models). ## Generate an image ### Endpoint ``` POST https://router.flatkey.ai/v1/images/generations ``` ### Headers | Header | Value | | - | - | | `Authorization` | `Bearer $FLATKEY_API_KEY` | | `Content-Type` | `application/json` | ### JSON body parameters Image model ID exposed by your Flatkey service, such as `"gpt-image-2"`. Text description of the image to generate. Output dimensions, such as `"1024x1024"`. Supported values depend on the model. Output quality: `"low"`, `"medium"`, or `"high"`. If omitted, the upstream default is used. Higher quality increases latency and usage. Background handling, such as `"transparent"` or `"opaque"`, when supported by the model. Output format, such as `"png"`, `"jpeg"`, or `"webp"`, when supported by the model. Output compression level, when supported by the model. Content moderation level, when supported by the model. `gpt-image-2` results are returned in `data[].b64_json`. Hosted image URLs are not provided, and `response_format: "url"` is unsupported. Requested image count. `gpt-image-2` currently returns one image per request because its upstream image tool does not accept `n`. Send multiple requests if you need multiple images. Set to `true` to receive an SSE response. Omit it for a synchronous response. ### Example requests ```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 futuristic city skyline at dusk, cyberpunk style", size="1024x1024", quality="high", ) 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 futuristic city skyline at dusk, cyberpunk style", size: "1024x1024", quality: "high", }); writeFileSync( "generated.png", Buffer.from(response.data[0].b64_json, "base64"), ); ``` ```bash curl 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 serene forest clearing with morning mist and sunbeams", "size": "1024x1024", "quality": "high" }' ``` ## Edit an image `/v1/images/edits` accepts an input image and editing instructions as `multipart/form-data`. ### Endpoint ``` POST https://router.flatkey.ai/v1/images/edits ``` ### Headers | Header | Value | | - | - | | `Authorization` | `Bearer $FLATKEY_API_KEY` | | `Content-Type` | `multipart/form-data`; curl adds the boundary automatically when you use `-F` | ### Form fields | Field | Type | Required | Description | | - | - | - | - | | `model` | string | Yes | Image model ID exposed by your Flatkey service, such as `gpt-image-2`. | | `prompt` | string | Yes | Instructions describing how to edit the image. | | `image` | file | Yes | Input image in PNG, JPEG, or WebP format. For multiple images, use `image[]` or indexed fields such as `image[0]` and `image[1]`. | | `mask` | file | No | Mask for inpainting. Transparent areas identify the regions to redraw. | | `size` | string | No | Output dimensions. Supported values depend on the model. | | `quality` | string | No | Output quality: `low`, `medium`, or `high`. | | `stream` | boolean | No | Set to `true` to receive an SSE response. | ### Example request ```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" ``` The `mask` field is optional. Omit it to edit the whole image based on the prompt. Do not set the `Content-Type` header manually; curl derives the correct multipart boundary from the `-F` fields. If a supplied mask is damaged, unreadable, or exceeds the service limit, the request fails instead of falling back to a whole-image edit. ## Response Synchronous generation and editing requests return the same response shape. Flatkey returns the image as base64 data; it does not provide a hosted image URL for `gpt-image-2`. ```json theme={"dark"} { "created": 1710000000, "data": [ { "b64_json": "iVBORw0KGgoAAA...", "revised_prompt": "Add a small yellow five-pointed star in the center...", "url": "" } ] } ``` ### Response fields Unix timestamp of when the request completed. Array of image objects. Base64-encoded image data. The prompt actually used by the model after rewriting. Empty for `gpt-image-2`; hosted URLs are not provided. For edits, the input image also counts toward model usage, so an edit typically consumes more usage than text-only image generation. ## Streaming Set `stream` to `true` in a generation JSON body or add `-F "stream=true"` to an edit request to receive `text/event-stream` output. The final event contains the image result, followed by `data: [DONE]`. | Event | Meaning | | - | - | | `image_generation.completed` | Image generation completed. | | `image_edit.completed` | Image editing completed. | | `[DONE]` | The stream has ended. | Streaming support can depend on the selected model and channel. For synchronous high-quality requests, configure a client timeout of at least 150 seconds. # List Models — GET /v1/models Source: https://docs.flatkey.ai/api-reference/models Retrieve all AI models available through your Flatkey account with GET /v1/models. Returns model IDs, providers, and timestamps in OpenAI-compatible format. The `/v1/models` endpoint returns a list of all models available through your Flatkey account. The response follows the OpenAI API format — the same model IDs returned here can be used in `/v1/chat/completions`, `/v1/embeddings`, and other endpoints. ## Endpoint ``` GET https://router.flatkey.ai/v1/models ``` ## Request ### Headers | Header | Value | | - | - | | `Authorization` | `Bearer $FLATKEY_API_KEY` | No request body is needed. ## Example request ```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", ) models = client.models.list() for model in models.data: print(model.id) ``` ```javascript node theme={"dark"} import OpenAI from "openai"; const client = new OpenAI({ apiKey: process.env.FLATKEY_API_KEY, baseURL: "https://router.flatkey.ai/v1", }); const models = await client.models.list(); for (const model of models.data) { console.log(model.id); } ``` ```bash curl theme={"dark"} curl https://router.flatkey.ai/v1/models \ -H "Authorization: Bearer $FLATKEY_API_KEY" ``` ## Response ```json theme={"dark"} { "object": "list", "data": [ { "id": "gpt-4o", "object": "model", "created": 1710000000, "owned_by": "openai" }, { "id": "claude-sonnet-4-5", "object": "model", "created": 1710000000, "owned_by": "anthropic" }, { "id": "gemini-2.5-flash", "object": "model", "created": 1710000000, "owned_by": "google" } ] } ``` ### Response fields Array of model objects. The model identifier to use in requests. Always `"model"`. Unix timestamp when the model was added. The provider: `"openai"`, `"anthropic"`, `"google"`, etc. For pricing, health data, and detailed model specifications, visit [flatkey.ai/models](https://flatkey.ai/models) or the [Model Directory](https://flatkey.ai/models). # Flatkey REST API: Endpoints, Auth, and Request Format Source: https://docs.flatkey.ai/api-reference/overview Flatkey exposes an OpenAI-compatible REST API at https://router.flatkey.ai/v1. API requests use Bearer authentication, with anonymous access only for completed video content URLs. The Flatkey API is a REST API that is fully compatible with the OpenAI API specification. API requests go to `https://router.flatkey.ai/v1` — the same base URL for all endpoints, providers, and models. Authentication uses a Bearer token in the `Authorization` header, except for completed video content URLs. ## Base URL ``` https://router.flatkey.ai/v1 ``` ## Authentication API requests must include your Flatkey API key as a Bearer token: ``` Authorization: Bearer sk-fk-your-api-key ``` Generate your API key in the [console](https://console.flatkey.ai/keys). See [Authentication](/api-reference/authentication) for full details. The anonymous `GET /v1/videos/{task_id}/content` URL is an exception. Treat it as sensitive because possession of the task URL grants access to the completed video. ## Request format * All requests use **HTTPS** * Request bodies must be **JSON** (`Content-Type: application/json`) * Responses are JSON, SSE for streaming, or `video/mp4` for completed video content ## Available endpoints | Endpoint | Method | Description | | - | - | - | | `/v1/chat/completions` | POST | Chat and text generation (OpenAI-compatible) | | `/v1/responses` | POST | OpenAI Responses API (stateful multi-turn) | | `/v1/embeddings` | POST | Text embeddings | | `/v1/images/generations` | POST | Image generation | | `/v1/videos` | POST | Create a Seedance video-generation task | | `/v1/videos/{task_id}` | GET | Retrieve Seedance task status and result | | `/v1/videos/{task_id}/content` | GET | Download a completed video without Bearer authentication | | `/v1/models` | GET | List available models | ## OpenAI SDK compatibility Because the API is OpenAI-compatible, you can use the official OpenAI Python or Node.js SDK with no changes to the request code — just set `base_url`: ```python python theme={"dark"} from openai import OpenAI client = OpenAI( api_key="sk-fk-your-api-key", base_url="https://router.flatkey.ai/v1", ) ``` ```javascript node theme={"dark"} import OpenAI from "openai"; const client = new OpenAI({ apiKey: "sk-fk-your-api-key", baseURL: "https://router.flatkey.ai/v1", }); ``` ## Versioning Flatkey uses the same versioning scheme as the OpenAI API. The `/v1` path prefix is stable. When new OpenAI API versions are released, Flatkey adds support while maintaining backward compatibility with `/v1`. ## Rate limits Rate limits are applied per API key. If you exceed the limit, you receive a `429 Too Many Requests` response. Contact [support@flatkey.ai](mailto:support@flatkey.ai) if you need higher rate limits for production workloads. ## Supported content types | Content type | Used for | | - | - | | `application/json` | JSON request bodies and API responses | | `text/event-stream` | Streaming responses (when `stream: true`) | | `video/mp4` | Completed video content | # Responses API — POST /v1/responses Source: https://docs.flatkey.ai/api-reference/responses Use POST /v1/responses to run stateful multi-turn conversations with the OpenAI Responses API format. Flatkey proxies this endpoint for compatible models. The `/v1/responses` endpoint implements the OpenAI Responses API, which supports stateful multi-turn conversations with built-in conversation history management. It is an alternative to `/v1/chat/completions` for workflows that require OpenAI's newer response format. Pass a `previous_response_id` to continue a conversation without resending full message history. ## Endpoint ``` POST https://router.flatkey.ai/v1/responses ``` ## When to use this endpoint Use `/v1/responses` if: * Your application is built on the OpenAI Responses API * You need stateful conversation management * You use the `previous_response_id` parameter for multi-turn conversations For most new integrations, `/v1/chat/completions` is the recommended endpoint and has broader model support. ## Request ### Headers | Header | Value | | - | - | | `Authorization` | `Bearer $FLATKEY_API_KEY` | | `Content-Type` | `application/json` | ### Body parameters Model ID to use. Only models that support the Responses API format are valid here. The user input for this turn. Can be a string or an array of content objects. System-level instructions for the model (equivalent to the `system` message in chat completions). The `id` of a previous response. When provided, the model continues the conversation from that point without the client needing to send history. Maximum number of tokens to generate. If `true`, returns a stream of server-sent events. Default: `false`. ## Example request ```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.responses.create( model="gpt-4o", input="What is the capital of Japan?", instructions="Answer concisely.", ) print(response.output_text) ``` ## Multi-turn with previous\_response\_id ```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", ) # First turn response1 = client.responses.create( model="gpt-4o", input="My name is Alice.", ) # Second turn — continue from the first response2 = client.responses.create( model="gpt-4o", input="What is my name?", previous_response_id=response1.id, ) print(response2.output_text) # "Your name is Alice." ``` ## Response ```json theme={"dark"} { "id": "resp_abc123", "object": "response", "created_at": 1710000000, "model": "gpt-4o", "output": [ { "type": "message", "role": "assistant", "content": [ {"type": "output_text", "text": "The capital of Japan is Tokyo."} ] } ], "usage": { "input_tokens": 15, "output_tokens": 9, "total_tokens": 24 } } ``` ### Response fields Unique identifier for this response. Pass it as `previous_response_id` to continue the conversation. Always `"response"`. Unix timestamp of when the response was created. The model that generated the response. Array of output objects produced by the model. The output type. `"message"` for text responses. Always `"assistant"` for generated output. Array of content blocks. Each block has a `type` (`"output_text"`) and a `text` string. Token counts for billing. Number of input tokens processed. Number of output tokens generated. Sum of input and output tokens. # Seedance Video Generation — POST /v1/videos Source: https://docs.flatkey.ai/api-reference/seedance-video-generation Create Seedance video tasks with text and multimodal inputs, poll their status, and download completed videos through Flatkey. Use `POST /v1/videos` to create an asynchronous Seedance video-generation task. Poll the task until it completes, then download the video from the returned Flatkey URL. Supported models are `seedance-2.0`, `seedance-2.0-fast`, `seedance-2.0-mini`, and `seedance-2.5`. Availability depends on your account; use [`GET /v1/models`](/api-reference/models) to retrieve the current model list. ## Endpoint ```http theme={"dark"} POST https://router.flatkey.ai/v1/videos ``` Flatkey also accepts `POST /v1/video/generations` and `GET /v1/video/generations/{task_id}`. Use `/v1/videos` for the standard response structure and native `usage` data. ## Request ### Headers | Header | Value | | - | - | | `Authorization` | `Bearer $FLATKEY_API_KEY` | | `Content-Type` | `application/json` | ### Body parameters Seedance model ID: `"seedance-2.0"`, `"seedance-2.0-fast"`, `"seedance-2.0-mini"`, or `"seedance-2.5"`. Multimodal input items. Include at least one non-empty text item, image, video, or audio item. See [`content` items](#content-items). Output resolution. Default: `"720p"`. | Model | Supported values | | - | - | | `seedance-2.0` | `"480p"`, `"720p"`, `"1080p"`, `"4k"` | | `seedance-2.0-fast`, `seedance-2.0-mini` | `"480p"`, `"720p"` | | `seedance-2.5` | `"480p"`, `"720p"`, `"1080p"` | Output aspect ratio: `"16:9"`, `"4:3"`, `"1:1"`, `"3:4"`, `"9:16"`, `"21:9"`, or `"adaptive"`. Default: `"adaptive"`. With `"adaptive"`, the model picks the ratio from your input images or videos. For `seedance-2.5`, first-frame and first/last-frame tasks support only `"adaptive"`. Video length in seconds. Default: `5`. Seedance 2.0 models accept 4 through 15; `seedance-2.5` accepts 4 through 30. Always send a positive `duration`. Flatkey bills Seedance per second, so `-1` (let the model choose the length) is rejected with `video_price_not_configured`. The only exception is a [`seedance-2.5` video edit](#video-editing), which requires `-1`. Add an "AI generated" watermark to the lower-right corner. Default: `false`. Generate a synchronized mono audio track. Default: `true`. Set it to `false` if you only need video; generated audio can occasionally fail copyright checks. Return the last frame of the generated video as an image. Default: `false`. When `true`, the completed task includes `metadata.last_frame_url`, which downloads a JPEG with the same dimensions as the video and no watermark. Use it as the `first_frame` of the next task to chain clips. Like `metadata.url`, it needs no authorization header; treat it as private. `seedance-2.5` only. Container of the generated video: `"mp4"` or `"mov"`. Default: `"mp4"`. MOV keeps more color precision for grading and compositing (H.264, YUV 4:4:4, PCM audio), but some players cannot open it. The [content endpoint](#download-the-video) returns `video/quicktime` for MOV results. `seedance-2.5` only, for reference-mode tasks. Tells the model which task you intend: `"auto"` (default), `"reference"`, `"edit"`, or `"extend"`. Setting it explicitly moves the task-type checks to submission, so an invalid combination fails immediately instead of after the task starts. `"reference"`, `"edit"`, and `"extend"` all need reference items in `content`; a text-only request with `"reference"` is rejected. See [video editing and extension](#video-editing). Queue priority from `0` through `9`. Default: `0`. Higher values move the task ahead of lower-priority tasks waiting on the same model. Supported on all Seedance 2.0 models and `seedance-2.5`. Values outside 0–9 are rejected with `400`. Seconds the task may wait and run before it expires. Default: `172800` (48 hours). Use a shorter value when a late result is useless to you. Public HTTPS URL that receives a `POST` whenever the task changes status. See [Task callbacks](#task-callbacks). Must use `https`, contain no user credentials, be at most 2,048 characters, and resolve to a public address; otherwise the request is rejected with `400`. ### `content` items | `type` | Value field | Supported `role` | Limits | | - | - | - | - | | `text` | `text` | — | Prompt text. Keep it under about 1,000 English words. | | `image_url` | `image_url.url` | `first_frame`, `last_frame`, `reference_image` | Up to 9 reference images (`seedance-2.5`: 30). JPEG, PNG, WebP, BMP, TIFF, GIF, HEIC, or HEIF; under 30 MB; 300–6000 px per side; aspect ratio 0.4–2.5. | | `video_url` | `video_url.url` | `reference_video` | Up to 3 videos, 2–15 seconds each and 15 seconds total (`seedance-2.5`: 10 videos, 30 seconds). MP4 or MOV; under 200 MB; at least about 409,600 total pixels (for example 854×480). | | `audio_url` | `audio_url.url` | `reference_audio` | Up to 3 clips, 2–15 seconds each and 15 seconds total (`seedance-2.5`: 10 clips, 30 seconds). WAV or MP3; under 15 MB. Seedance 2.0 models need at least one image or video alongside audio. | Each URL can be a public HTTPS URL, a `data:;base64,` data URI (images and audio only), or an `asset://` reference from the [asset library](/guides/seedance#use-the-asset-library). The whole request body must stay under 64 MB. Choose one input mode per task. These modes cannot be mixed: | Mode | How to build `content` | | - | - | | First frame | One image with `role: "first_frame"`. | | First and last frames | Two images, one with `role: "first_frame"` and one with `role: "last_frame"`. `last_frame` always needs a `first_frame`, and neither can be combined with reference items. | | Reference | Any combination of `reference_image`, `reference_video`, and `reference_audio` items. | ### Video editing `seedance-2.5` can edit or extend an existing video in reference mode. Describe the change in the prompt and refer to the video as `@Video1`. | Task | Required settings | Output length | | - | - | - | | Edit (replace a subject, add or remove objects, change audio) | At least one `reference_video` 4–30 seconds long, `ratio: "adaptive"`, `duration: -1`. Recommended: `omni_reference_task_type: "edit"`. | Same as the edited input video | | Extend (continue a video forward or backward) | At least one `reference_video`, `ratio: "adaptive"`, a positive `duration`. Recommended: `omni_reference_task_type: "extend"`. | `duration` | ```json theme={"dark"} { "model": "seedance-2.5", "resolution": "720p", "ratio": "adaptive", "duration": -1, "omni_reference_task_type": "edit", "content": [ { "type": "text", "text": "Edit @Video1: make the ball bright green and keep everything else unchanged." }, { "type": "video_url", "video_url": { "url": "https://example.com/input.mp4" }, "role": "reference_video" } ] } ``` Like every `seedance-2.5` request with a reference video, an edit is billed at the resolution's with-video rate for a fixed 30 seconds, regardless of the input or output length. ### Draft mode `seedance-2.5` can render a cheap 480p draft first, so you can check the scene, shots, and motion before paying for the final video. It takes two requests: 1. **Create the draft.** Send a normal request with `"draft": true` and `"resolution": "480p"`. It is billed as a regular 480p video. 2. **Render the final video.** After the draft completes, send a request whose only `content` item is a `draft_task` pointing at the draft's task ID, with `"resolution": "1080p"`. ```json theme={"dark"} { "model": "seedance-2.5", "resolution": "1080p", "content": [ { "type": "draft_task", "draft_task": { "id": "task_3f9a00000000000000000000000000e2" } } ] } ``` The final video reuses the draft's prompt, media, duration, ratio, seed, audio setting, and task type. Do not send `duration`, `ratio`, `frames`, `seed`, `generate_audio`, `omni_reference_task_type`, `draft`, or any other `content` item with a `draft_task`; the request is rejected with `400`. You can still set `return_last_frame`, `output_format`, `watermark`, `priority`, `execution_expires_after`, and `callback_url`. | Rule | Detail | | - | - | | Model | `model` must match the draft. | | Draft task ID | Use the `task_…` ID Flatkey returned for the draft. It must be your own task and must have completed. | | Resolution | Draft: `480p` only. Final video: `1080p` only. | | Expiry | A draft can be used for 7 days after it was created. | | Billing | The final video is billed at the 1080p rate for the draft's duration. If the draft used a reference video, the with-video rate applies for a fixed 30 seconds. | Seedance does not accept images or videos that show real human faces directly. Upload them as [real-person assets](/guides/seedance#real-person-assets) and reference them with `asset://`. ## Example requests ```python python theme={"dark"} import os import requests response = requests.post( "https://router.flatkey.ai/v1/videos", headers={ "Authorization": f"Bearer {os.environ['FLATKEY_API_KEY']}", "Content-Type": "application/json", }, json={ "model": "seedance-2.0", "content": [ { "type": "text", "text": "An astronaut walking on the moon, cinematic lighting, slow camera push-in", } ], "resolution": "1080p", "ratio": "16:9", "duration": 5, }, ) response.raise_for_status() print(response.json()) ``` ```javascript node theme={"dark"} const response = await fetch("https://router.flatkey.ai/v1/videos", { method: "POST", headers: { Authorization: `Bearer ${process.env.FLATKEY_API_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ model: "seedance-2.0", content: [ { type: "text", text: "An astronaut walking on the moon, cinematic lighting, slow camera push-in", }, ], resolution: "1080p", ratio: "16:9", duration: 5, }), }); if (!response.ok) throw new Error(await response.text()); console.log(await response.json()); ``` ```bash curl theme={"dark"} curl https://router.flatkey.ai/v1/videos \ -H "Authorization: Bearer $FLATKEY_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "seedance-2.0", "content": [ { "type": "text", "text": "An astronaut walking on the moon, cinematic lighting, slow camera push-in" } ], "resolution": "1080p", "ratio": "16:9", "duration": 5 }' ``` ### First- and last-frame input ```json theme={"dark"} { "model": "seedance-2.0", "content": [ { "type": "text", "text": "The camera slowly moves forward" }, { "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": "720p", "ratio": "16:9", "duration": 5, "generate_audio": false } ``` ### Multimodal reference ```json theme={"dark"} { "model": "seedance-2.0", "content": [ { "type": "text", "text": "A woman smiles at the camera during a beach sunset" }, { "type": "image_url", "image_url": { "url": "https://example.com/reference.jpg" }, "role": "reference_image" } ], "resolution": "1080p", "ratio": "16:9", "duration": 5, "watermark": false } ``` ## Response ```json theme={"dark"} { "id": "task_3f9a00000000000000000000000000e2", "task_id": "task_3f9a00000000000000000000000000e2", "object": "video", "model": "seedance-2.0", "status": "queued", "progress": 0, "created_at": 1780306574 } ``` ### Response fields Public task ID used to poll the task and download the completed video. `task_id` contains the same value. Task status: `queued`, `in_progress`, `completed`, or `failed`. Generation progress from 0 through 100. Unix timestamp for task creation. ## Poll task status ```http theme={"dark"} GET https://router.flatkey.ai/v1/videos/{task_id} ``` ```bash theme={"dark"} curl https://router.flatkey.ai/v1/videos/task_3f9a00000000000000000000000000e2 \ -H "Authorization: Bearer $FLATKEY_API_KEY" ``` While generation is running, the response includes the current progress: ```json theme={"dark"} { "id": "task_3f9a00000000000000000000000000e2", "object": "video", "status": "in_progress", "progress": 50, "model": "seedance-2.0", "created_at": 1780306574 } ``` When the task completes, the response includes the video URL and token usage: ```json theme={"dark"} { "id": "task_3f9a00000000000000000000000000e2", "object": "video", "status": "completed", "progress": 100, "model": "seedance-2.0", "usage": { "completion_tokens": 120, "total_tokens": 120 }, "metadata": { "url": "https://router.flatkey.ai/v1/videos/task_3f9a00000000000000000000000000e2/content", "last_frame_url": "https://router.flatkey.ai/v1/videos/task_3f9a00000000000000000000000000e2/last-frame" }, "created_at": 1780306574, "completed_at": 1780306750 } ``` Failed tasks return the error details with the task response: ```json theme={"dark"} { "id": "task_3f9a00000000000000000000000000e2", "object": "video", "status": "failed", "error": { "message": "The video generation task failed.", "code": "" } } ``` Any pre-consumed balance is refunded automatically. ### Status values | Status | Meaning | | - | - | | `queued` | The task is waiting to start. | | `in_progress` | The video is being generated. `progress` ranges from 0 through 100. | | `completed` | The video is ready at `metadata.url`, and token usage is available in `usage`. | | `failed` | Generation failed. Details are available in `error.message`, and any pre-consumed balance is refunded. | ## Task callbacks Set `callback_url` to receive status changes instead of, or in addition to, polling. Flatkey sends a `POST` with `Content-Type: application/json` when the task moves to `in_progress`, `completed`, or `failed`. The body is exactly what [`GET /v1/videos/{task_id}`](#poll-task-status) returns at that moment, including `metadata.url` and `metadata.last_frame_url` for completed tasks. ```json theme={"dark"} { "id": "task_3f9a00000000000000000000000000e2", "object": "video", "status": "completed", "progress": 100, "model": "seedance-2.0", "usage": { "completion_tokens": 120, "total_tokens": 120 }, "metadata": { "url": "https://router.flatkey.ai/v1/videos/task_3f9a00000000000000000000000000e2/content" }, "created_at": 1780306574, "completed_at": 1780306750 } ``` | Header | Value | | - | - | | `X-Flatkey-Event-Id` | `:`, for example `task_3f9a…e2:completed`. The same event can be delivered more than once; use this header to ignore duplicates. | Respond with any `2xx` status within 5 seconds. Otherwise Flatkey retries twice, after 2 and 8 seconds, and then gives up. Redirects are not followed. Callbacks are not signed, so verify a result by polling the task before acting on it, and keep polling as the source of truth in case a callback is lost. ## Download the video ```http theme={"dark"} GET https://router.flatkey.ai/v1/videos/{task_id}/content ``` ```bash theme={"dark"} curl -L "https://router.flatkey.ai/v1/videos/task_3f9a00000000000000000000000000e2/content" \ -o output.mp4 ``` The content endpoint streams `video/mp4` data, or `video/quicktime` when the task used `output_format: "mov"`. It does not require a Bearer token and can be used directly as a `