# reAPI — Full Documentation
> One API for top AI image, video, chat, and audio models. This file is the complete documentation set in markdown; https://reapi.ai/llms.txt is the link index.
Pages: 69
# ai-essay-writer (https://reapi.ai/docs/ai-essay-writer)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> The AI Essay Writer API generates a complete, structured essay from a
> **topic** plus an **academic level**, **essay type**, and **length**. One
> async call returns a task id; poll until the essay is ready. An optional
> `humanized` pass rewrites the result to read more human. Pricing is per
> essay, scaled by the length tier — see current rates on the
> [model page](https://reapi.ai/models/ai-essay-writer).
## Quick example
```bash
curl https://reapi.ai/api/v1/essay \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ai-essay-writer",
"type": "Descriptive",
"academic_level": "HighSchool",
"length": "Short",
"input": "Write an essay about the ketogenic diet"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/essay",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "ai-essay-writer",
"type": "Descriptive",
"academic_level": "HighSchool",
"length": "Short",
"input": "Write an essay about the ketogenic diet",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/essay", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "ai-essay-writer",
type: "Descriptive",
academic_level: "HighSchool",
length: "Short",
input: "Write an essay about the ketogenic diet",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "ai-essay-writer",
"type": "Descriptive",
"academic_level": "HighSchool",
"length": "Short",
"input": "Write an essay about the ketogenic diet",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/essay", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "ai-essay-writer",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload carries the generated essay text
in the task output.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/essay
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Request body
### `model` — required
`string`. Must be `"ai-essay-writer"`.
### `input` — required
`string`. The essay topic or instructions the model builds the essay around.
### `type` — required
`string`. The rhetorical mode of the essay.
| Value | Notes |
| ---------------------- | -------------------------- |
| `"Descriptive"` | Descriptive essay |
| `"Narrative"` | Narrative essay |
| `"Persuasive"` | Persuasive / argumentative |
| `"Analytical"` | Analytical essay |
| `"CompareAndContrast"` | Compare-and-contrast essay |
### `academic_level` — required
`string`. The writing level the essay targets (vocabulary, structure, depth).
| Value |
| -------------- |
| `"HighSchool"` |
| `"University"` |
| `"Doctorate"` |
### `length` — required
`string`. Output length tier. **Pricing scales with this tier.**
| Value |
| ---------- |
| `"Short"` |
| `"Medium"` |
| `"Long"` |
### `humanized` — boolean, default `false`
Run the finished essay through a de-AI pass so it reads more human.
### `model_version` — string, default `"v2"`
Generation model version.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time. The completed `output` carries the generated essay text.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "ai-essay-writer",
"status": "completed",
"created_at": 1735000000,
"output": {
"text": "The ketogenic diet, commonly referred to as the keto diet, is a high-fat, low-carbohydrate eating plan…"
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | --------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.text` is the essay |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
***
## Pricing
Per essay, scaled by the `length` tier. Each tier is priced at a fixed
estimated output-word count × the per-word rate.
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_word_usd × estimated_words(length) × 1000)
```
`length` (Short / Medium / Long) selects the estimated word count. See the
current per-tier rates in the pricing table on the
[model page](https://reapi.ai/models/ai-essay-writer). Failed jobs refund
automatically.
***
## Validation errors
All cases below return HTTP 400. Pattern-match on `code`, not `message`.
| Trigger | Code | Message (illustrative) |
| ---------------------------------- | ------- | ----------------------------------------------- |
| `input` missing | `20002` | `input is required` |
| `type` missing / invalid | `20003` | `invalid type` |
| `academic_level` missing / invalid | `20003` | `invalid academic_level` |
| `length` missing / invalid | `20003` | `invalid length` |
| Upstream out of credits | `80003` | Upstream provider could not process the request |
The full envelope is `{ "error": { "code", "message", "request_id" } }` — see
[Errors catalog](/docs/api/errors).
***
## Tips
* **Length drives the bill.** A Short essay costs less than a Long one; pick the
smallest tier that fits the assignment.
* **Set the type and level deliberately.** `academic_level` and `type` change
the vocabulary, structure, and depth more than the prompt wording does.
* **Use `humanized` for a natural read.** Turn it on when the essay needs to
read less like AI; leave it off for the fastest result.
* **The topic can be detailed.** `input` accepts full instructions, not just a
title — constraints and angles in the topic carry into the essay.
***
## Related
* [AI Humanizer](/docs/humanize)
* [AI Text Detector](/docs/ai-text-detector)
* [Errors catalog](/docs/api/errors)
* [Quickstart](/docs/api/quickstart)
---
# ai-text-detector (https://reapi.ai/docs/ai-text-detector)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> `ai-text-detector` is a single async endpoint that scores text 0–100 for how likely
> it is AI-generated, aggregating several third-party detection engines into one
> result plus per-engine sub-scores. See current pricing on the
> [model page](https://reapi.ai/models/ai-text-detector).
## Quick example
```bash
curl https://reapi.ai/api/v1/detect \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "ai-text-detector",
"text": "Citizen science involves the public participating in scientific research. This can take many forms, from collecting data on local wildlife populations to analyzing astronomical images."
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/detect",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "ai-text-detector",
"text": "Citizen science involves the public participating in scientific research. This can take many forms, from collecting data on local wildlife populations to analyzing astronomical images.",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/detect", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "ai-text-detector",
text: "Citizen science involves the public participating in scientific research. This can take many forms, from collecting data on local wildlife populations to analyzing astronomical images.",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "ai-text-detector",
"text": "Citizen science involves the public participating in scientific research. This can take many forms, from collecting data on local wildlife populations to analyzing astronomical images.",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/detect", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "ai-text-detector",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` until `status === "completed"`. A check usually
finishes in a few seconds.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/detect
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; polling
the task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Request body
### `model` — required
Always `"ai-text-detector"`.
### `text` — required (string)
The text to check for AI authorship.
* **Maximum:** under **30,000 words** per request.
* **Recommended:** at least \~200 words. Short snippets carry less signal,
though the detector still returns a result.
* Encode line breaks inside the JSON string as `\n`.
***
## Output
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "ai-text-detector",
"status": "completed",
"created_at": 1735000000,
"output": {
"detection": {
"result": 12.0,
"human": 88.0,
"detectors": {
"scoreGptZero": 50.0,
"scoreOpenAI": 0.0,
"scoreWriter": 0.0,
"scoreCrossPlag": 0.0,
"scoreCopyLeaks": 50.0,
"scoreSapling": 0.0,
"scoreContentAtScale": 0.0,
"scoreZeroGPT": 50.0
}
}
},
"error": null
}
```
### Interpreting the score
`result` is the headline **AI-likelihood**, 0–100:
| Range | Reading |
| -------- | --------------------- |
| under 50 | Likely human-written. |
| 50 – 60 | Possible AI. |
| over 60 | Likely AI-generated. |
`human` is the complementary human-likelihood. `detectors` carries each
third-party engine's sub-score so you can see whether the engines agree or
split. The headline `result` is the most accurate signal; treat the sub-scores
as supporting detail.
***
## Pricing
Billed **per word** of the `text` you submit, with a **50-word floor**. AI
detection is priced at one-tenth the per-word rate of text humanization.
Polling does not add any charge. Failed requests are refunded automatically.
`1 credit = $0.001 USD`. See the live banner on the
[model page](https://reapi.ai/models/ai-text-detector) for the current
per-1,000-words rate in credits.
***
## Errors
Standard envelope:
```json
{
"error": {
"code": 20002,
"message": "text is required",
"request_id": "req_..."
}
}
```
Common cases:
| Code | When |
| ------- | -------------------------------------------------------------------------- |
| `20002` | `text` missing. |
| `20003` | `text` empty or at/over the 30,000-word cap. |
| `30001` | Insufficient credits for the submitted word count. |
| `80001` | Provider rejected the submission — includes insufficient upstream credits. |
| `80003` | Provider failed while processing the detection. |
***
## Related
* [AI Humanizer](/docs/humanize) — rewrite AI text to read human. Pair it with
`ai-text-detector` for a detect → rewrite → detect QA loop.
---
# claude-fable-5 (https://reapi.ai/docs/claude-fable-5)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Claude Fable 5 is Anthropic's most capable widely released model — a tier
> above Opus — built for the most demanding reasoning and long-horizon
> agentic work, exposed through api.reapi.ai as a drop-in OpenAI-compatible
> Chat Completions endpoint (the native Anthropic `/v1/messages` surface is
> also available). 1M token context, 128K max output, always-on adaptive
> thinking, vision input, prompt caching, and tool use. Current rates live
> on the [model page](https://reapi.ai/models/claude-fable-5) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-fable-5",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"max_tokens": 4096
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="claude-fable-5",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
max_tokens=4096,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```python
from anthropic import Anthropic
client = Anthropic(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai",
)
with client.messages.stream(
model="claude-fable-5",
max_tokens=4096,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "claude-fable-5",
messages: [{ role: "user", content: "Hello" }],
stream: true,
max_tokens: 4096,
// `group` is an api.reapi.ai-specific extension; pass via extra body.
// @ts-expect-error — not part of the OpenAI types
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "claude-fable-5",
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"max_tokens": 4096,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The Claude Fable 5 chat workspace
lives on the `api.reapi.ai` platform — sign in there to create a key and
top up tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Endpoints
```http
POST https://api.reapi.ai/v1/chat/completions # OpenAI-compatible
POST https://api.reapi.ai/v1/messages # Anthropic-native
```
Both surfaces accept `claude-fable-5`. Pick whichever matches your SDK
of record:
* **`/v1/chat/completions`** — drop-in for the OpenAI SDKs. Same request
shape, same SSE wire format. Set `base_url` to `https://api.reapi.ai/v1`.
* **`/v1/messages`** — native Anthropic Messages format. Set `base_url`
to `https://api.reapi.ai` for the Anthropic Python / TypeScript SDKs.
Required for callers that need Anthropic-specific features
(`cache_control` blocks for prompt caching, the `effort` parameter,
summarized thinking display, native multi-block content, the full
tool-use spec).
***
## Request body — `/v1/chat/completions`
### `model` — string, required
Must be `"claude-fable-5"`. The value is echoed back in the response
envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec, plus content-parts for vision:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string OR content-parts array (text + image_url parts)"
}
```
Multi-turn history is sent in chronological order — the last message is
the one Claude responds to.
### `max_tokens` — integer, default `4096`
Upper bound on output tokens. **Anthropic's API requires `max_tokens`
on every call, including streamed ones** — even though the OpenAI SDKs
treat it as optional. Set it generously (`128000` is the hard cap on the
synchronous API) for long-form outputs; the model still stops at the
natural end of its response.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the
OpenAI format, terminated by a `data: [DONE]` line.
### `tools` / `tool_choice` — optional
Standard OpenAI tool-calling parameters. Claude Fable 5 supports the
full OpenAI tool-use spec via this surface. For Anthropic's native
tool-use schema (with `cache_control`, server-side tools, etc.) call
`/v1/messages` directly.
### `group` — string, default `"default"`
api.reapi.ai-specific extension. Selects a token group on the gateway,
which routes the request to a specific upstream channel pool. Omit if
default routing is fine.
**No sampling parameters.** Claude Fable 5 does not accept
`temperature`, `top_p`, or `top_k` — Anthropic removed them on this
model generation and requests that include them are rejected upstream.
Steer style and variance through prompting instead.
***
## Adaptive thinking — always on
Adaptive thinking is the **only** thinking mode on Claude Fable 5. It
applies on every call — there is no way to disable it — and the model
decides per request how much reasoning the task needs. Two consequences
for integrators:
* **Raw chain-of-thought is never returned.** Thinking blocks arrive
with empty content by default. On the native `/v1/messages` surface,
set `thinking: type adaptive, display summarized` (see Anthropic's
adaptive-thinking docs for the exact field shape) to receive readable
summarized thinking.
* **Depth is tuned with `effort`, not a token budget.** On the native
surface, `output_config.effort` accepts `low` through `max`. Higher
effort means deeper reasoning and more output tokens; lower effort
means faster, cheaper calls.
***
## Vision input (multimodal)
Send images alongside text via OpenAI content-parts:
```json
{
"model": "claude-fable-5",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What does this chart show?" },
{
"type": "image_url",
"image_url": { "url": "https://example.com/chart.png" }
}
]
}
]
}
```
Supported image formats: PNG, JPEG, GIF, WebP. Each image counts toward
the input token budget based on its resolution.
***
## Prompt caching
Anthropic's prompt caching pays off on stable system prompts, recurring
RAG context, and long multi-turn agent histories. The first call pays
the cache-write rate on the cacheable region; subsequent calls within
the cache window pay only the (much lower) cache-read rate on those
tokens.
To enable caching, call `/v1/messages` natively and add a
`cache_control` block. Example:
```json
{
"model": "claude-fable-5",
"max_tokens": 4096,
"system": [
{
"type": "text",
"text": "",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{ "role": "user", "content": "Question for the assistant" }
]
}
```
The cache key is the hash of the cacheable content. See
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) for cache-read and
cache-write rates.
***
## Refusals and fallback
Claude Fable 5 includes safety classifiers that can decline certain
requests. On the native `/v1/messages` surface a refusal is returned as
a **successful response** with `stop_reason: "refusal"` — not an HTTP
error — and the response reports which classifier declined the request.
Two things follow:
* **A request refused before any output is generated is not billed.**
* **A refused request can usually be served by another Claude model.**
Retry it with a different `model` value on the same key — Claude Opus
4.8 covers most workloads the classifier declines on Fable 5.
Handle `stop_reason: "refusal"` (native surface) or an empty completion
with a refusal marker (OpenAI surface) explicitly in your integration
rather than treating it as a transport failure: it is deterministic for
a given prompt, so a verbatim retry on the same model will refuse again.
***
## Response shape — `/v1/chat/completions`
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1749600000,
"model": "claude-fable-5",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21,
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
```
`usage.prompt_tokens_details.cached_tokens` reports how many input
tokens were served from cache — the part billed at the cache-read rate
rather than the standard input rate.
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta in
the OpenAI chunk format; the final event before `[DONE]` carries the
`finish_reason` (`stop` / `length` / `tool_calls` / `content_filter`).
***
## Pricing
Claude Fable 5 is billed **pay-as-you-go in USD** against your
api.reapi.ai token balance. It bills along several dimensions — input
tokens, output tokens, cache-read tokens, and per-request web search.
The full 1M context window is billed at standard per-token rates with
no long-context premium, and requests refused before any output is
generated are not billed. Current rates live on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) and in the pricing
card at the top of the
[model page](https://reapi.ai/models/claude-fable-5).
Per-call bill:
```
billable_input = (prompt_tokens - cached_tokens) × input_rate / 1,000,000
cache_read_bill = cached_tokens × cache_read_rate / 1,000,000
output_bill = completion_tokens × output_rate / 1,000,000
```
Cache-write rate applies on the first call that writes a cache block;
subsequent hits pay only the cache-read rate. Web search, when used, is
billed per request. Failed requests are not charged.
***
## Limits
| Limit | Value |
| ------------------- | ----------- |
| Context window | 1M tokens |
| Max output per call | 128K tokens |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape — HTTP status, plus a JSON
body:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
Common cases:
| Status | When | Notes |
| ------ | ----------------------------------------------------- | --------------------------------------------------------------------------------------------- |
| `400` | Missing `max_tokens`, sampling params sent, bad shape | Anthropic requires `max_tokens`; `temperature` / `top_p` / `top_k` are rejected on this model |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you,
that's a one-for-one wire failure and a retry from your side is safe;
the upstream provider may have already produced output, but the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "claude-fable-5",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "Summarise this in three sentences." }
]
}
```
### Tool use (function calling, OpenAI surface)
```json
{
"model": "claude-fable-5",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo today?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### Vision
```json
{
"model": "claude-fable-5",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Read the error in this screenshot and suggest a fix." },
{
"type": "image_url",
"image_url": { "url": "https://your-cdn.com/screenshot.png" }
}
]
}
]
}
```
### Long context with prompt caching (native Anthropic surface)
```json
{
"model": "claude-fable-5",
"max_tokens": 4096,
"system": [
{
"type": "text",
"text": "<800K-token reference document>",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{ "role": "user", "content": "Find every mention of the constraint X and list them with line numbers." }
]
}
```
***
## When to pick Claude Fable 5
Pick Claude Fable 5 when the task sits past what Opus-tier models handle
cleanly:
* **Frontier long-horizon agentic work** — week-long refactors,
codebase-scale migrations, autonomous runs that must stay coherent
across hundreds of steps.
* **The most demanding reasoning** — deep multi-step problems, ambiguous
specifications, analysis where the answer quality justifies the top
capability tier.
* **Large-context analysis** — full codebases, long research packs,
multi-document review, audit work across the 1M window.
Route everyday premium coding to Claude Opus 4.8 and lighter traffic
(classification, short replies, tight loops) to cheaper Claude or GPT
models on the same key.
***
## Tips
* **Set `max_tokens` generously.** Anthropic enforces it strictly — the
model still stops at the natural end of its response, but a low cap
will truncate before the real ending.
* **Stream by default for chat UX.** Streaming cuts perceived latency
dramatically — especially relevant here, since always-on adaptive
thinking can add a pause before the first visible token.
* **Don't send sampling parameters.** `temperature`, `top_p`, and
`top_k` are rejected on Claude Fable 5. Steer variance and style
through prompting.
* **Cache the stable parts of long prompts.** A 500K-token RAG context
on top of a 1KB user question can pay the cache-read rate on every
subsequent call instead of the standard input rate — a big saving on
multi-turn agents replaying long histories.
* **Handle refusals as a first-class outcome.** Detect the refusal stop
reason and retry on another Claude model instead of retrying verbatim.
* **Use the native `/v1/messages` surface for Anthropic-only features.**
`cache_control`, `effort`, summarized thinking display, native
multi-block content, the full tool-use spec — all of those work
through `/v1/messages` without needing translation.
***
## Related
* [claude-opus-4-8](/docs/claude-opus-4-8)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# claude-opus-4-7 (https://reapi.ai/docs/claude-opus-4-7)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Claude Opus 4.7 is Anthropic's flagship model, exposed through
> api.reapi.ai as a drop-in OpenAI-compatible Chat Completions endpoint
> (the native Anthropic `/v1/messages` surface is also available). 1M
> token context, 128K max output, vision input, prompt caching, and
> tool use. Current rates live on the
> [model page](https://reapi.ai/models/claude-opus-4-7) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-7",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"max_tokens": 4096,
"temperature": 0.7
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="claude-opus-4-7",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
max_tokens=4096,
temperature=0.7,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```python
from anthropic import Anthropic
client = Anthropic(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai",
)
with client.messages.stream(
model="claude-opus-4-7",
max_tokens=4096,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "claude-opus-4-7",
messages: [{ role: "user", content: "Hello" }],
stream: true,
max_tokens: 4096,
temperature: 0.7,
// `group` is an api.reapi.ai-specific extension; pass via extra body.
// @ts-expect-error — not part of the OpenAI types
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "claude-opus-4-7",
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"max_tokens": 4096,
"temperature": 0.7,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The Claude Opus 4.7 chat workspace
lives on the `api.reapi.ai` platform — sign in there to create a key and
top up tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Endpoints
```http
POST https://api.reapi.ai/v1/chat/completions # OpenAI-compatible
POST https://api.reapi.ai/v1/messages # Anthropic-native
```
Both surfaces accept `claude-opus-4-7`. Pick whichever matches your SDK
of record:
* **`/v1/chat/completions`** — drop-in for the OpenAI SDKs. Same request
shape, same SSE wire format. Set `base_url` to `https://api.reapi.ai/v1`.
* **`/v1/messages`** — native Anthropic Messages format. Set `base_url`
to `https://api.reapi.ai` for the Anthropic Python / TypeScript SDKs.
Required for callers that need Anthropic-specific features
(`cache_control` blocks for prompt caching, native multi-block
content, the full tool-use spec).
***
## Request body — `/v1/chat/completions`
### `model` — string, required
Must be `"claude-opus-4-7"`. The value is echoed back in the response
envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec, plus content-parts for vision:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string OR content-parts array (text + image_url parts)"
}
```
Multi-turn history is sent in chronological order — the last message is
the one Claude responds to.
### `max_tokens` — integer, default `4096`
Upper bound on output tokens. **Anthropic's API requires `max_tokens`
on every call, including streamed ones** — even though the OpenAI SDKs
treat it as optional. Set it generously (`128000` is the hard cap) for
long-form outputs; the model still stops at the natural end of its
response.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the
OpenAI format, terminated by a `data: [DONE]` line.
### `temperature` — number, default `1`
Range `0.0` – `1.0`. Sampling temperature. Anthropic recommends
**either** `temperature` or `top_p`, not both. Lower values produce
more deterministic output.
### `top_p` — number, default `1`
Range `0.0` – `1.0`. Nucleus sampling cutoff.
### `tools` / `tool_choice` — optional
Standard OpenAI tool-calling parameters. Claude Opus 4.7 supports the
full OpenAI tool-use spec via this surface; for Anthropic's native
tool-use schema (with `cache_control`, `tool_choice_type`, etc.) call
`/v1/messages` directly.
### `group` — string, default `"default"`
api.reapi.ai-specific extension. Selects a token group on the gateway,
which routes the request to a specific upstream channel pool. Omit if
default routing is fine.
***
## Vision input (multimodal)
Send images alongside text via OpenAI content-parts:
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What does this chart show?" },
{
"type": "image_url",
"image_url": { "url": "https://example.com/chart.png" }
}
]
}
]
}
```
Supported image formats: PNG, JPEG, GIF, WebP. Base64 URLs work too —
prefix `data:image/png;base64,...`. Each image counts toward the input
token budget based on its resolution.
***
## Prompt caching
Anthropic's prompt caching pays off on stable system prompts, recurring
RAG context, and long multi-turn agent histories. The first call pays
the cache-write rate on the cacheable region; subsequent calls within
the cache window pay only the (much lower) cache-read rate on those
tokens.
To enable caching, call `/v1/messages` natively and add a
`cache_control` block. Example:
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"system": [
{
"type": "text",
"text": "",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{ "role": "user", "content": "Question for the assistant" }
]
}
```
The cache key is the hash of the cacheable content; the cache TTL is
5 minutes (default ephemeral) or 1 hour (extended ephemeral) depending
on how the cache\_control block is configured. See
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) for cache-read and
cache-write rates.
***
## Response shape — `/v1/chat/completions`
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1735000000,
"model": "claude-opus-4-7",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21,
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
```
`usage.prompt_tokens_details.cached_tokens` reports how many input
tokens were served from cache — the part billed at the cache-read rate
rather than the standard input rate.
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta in
the OpenAI chunk format; the final event before `[DONE]` carries the
`finish_reason` (`stop` / `length` / `tool_calls` / `content_filter`).
***
## Pricing
Claude Opus 4.7 is billed **pay-as-you-go in USD** against your
api.reapi.ai token balance. Current rates (input, output, cache-read,
cache-write) live on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) and in the pricing
card at the top of the
[model page](https://reapi.ai/models/claude-opus-4-7).
Per-call bill:
```
billable_input = (prompt_tokens - cached_tokens) × input_rate / 1,000,000
cache_read_bill = cached_tokens × cache_read_rate / 1,000,000
output_bill = completion_tokens × output_rate / 1,000,000
```
Cache-write rate applies on the first call that writes a cache block;
subsequent hits within the cache TTL pay only the cache-read rate.
Failed requests are not charged.
***
## Limits
| Limit | Value |
| --------------------- | ----------- |
| Context window | 1M tokens |
| Max output per call | 128K tokens |
| Cache TTL (ephemeral) | 5 minutes |
| Cache TTL (extended) | 1 hour |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape — HTTP status, plus a JSON
body:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
Common cases:
| Status | When | Notes |
| ------ | ------------------------------------- | ----------------------------------------------------------------------- |
| `400` | Missing `max_tokens`, bad shape, etc. | Anthropic requires `max_tokens`; OpenAI SDKs that omit it will 400 here |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you,
that's a one-for-one wire failure and a retry from your side is safe;
the upstream provider may have already produced output, but the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "Summarise this in three sentences." }
]
}
```
### Tool use (function calling, OpenAI surface)
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo today?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### Vision
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Read the error in this screenshot and suggest a fix." },
{
"type": "image_url",
"image_url": { "url": "https://your-cdn.com/screenshot.png" }
}
]
}
]
}
```
### Long context with prompt caching (native Anthropic surface)
```json
{
"model": "claude-opus-4-7",
"max_tokens": 4096,
"system": [
{
"type": "text",
"text": "<800K-token reference document>",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{ "role": "user", "content": "Find every mention of the constraint X and list them with line numbers." }
]
}
```
***
## When to pick Claude Opus 4.7
Pick Claude Opus 4.7 when **output quality** dominates the decision:
* **High-stakes coding and refactors** — architecture, multi-file
refactor, migration planning, long-form engineering deliverables.
* **Agent workflows that must hold state** — multi-step planning,
reliable tool use, long agent runs where context drift kills the
workflow.
* **Large-context analysis** — full codebases, long research packs,
multi-document review, audit work.
Route lighter traffic (classification, short replies, tight loops) to
cheaper Claude or GPT models on the same key.
***
## Tips
* **Set `max_tokens` generously.** Anthropic enforces it strictly — the
model still stops at the natural end of its response, but a low cap
will truncate before the real ending.
* **Stream by default for chat UX.** Streaming cuts perceived latency
dramatically.
* **Cache the stable parts of long prompts.** A 500K-token RAG context
on top of a 1KB user question can pay the cache-read rate on every
subsequent call instead of the standard input rate. Big savings on
multi-turn agents.
* **Tune `temperature` *or* `top_p`, not both.** Mixing them produces
results that are hard to reason about.
* **Use the native `/v1/messages` surface for Anthropic-only features.**
`cache_control`, native multi-block content, full tool-use spec — all
of those work through `/v1/messages` without needing translation.
***
## Related
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# claude-opus-4-8 (https://reapi.ai/docs/claude-opus-4-8)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Claude Opus 4.8 is Anthropic's most capable model for complex reasoning
> and long-horizon agentic coding, exposed through api.reapi.ai as a
> drop-in OpenAI-compatible Chat Completions endpoint (the native
> Anthropic `/v1/messages` surface is also available). 1M token context,
> 128K max output, vision input, prompt caching, and tool use. Current
> rates live on the
> [model page](https://reapi.ai/models/claude-opus-4-8) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-4-8",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"max_tokens": 4096,
"temperature": 0.7
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="claude-opus-4-8",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
max_tokens=4096,
temperature=0.7,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```python
from anthropic import Anthropic
client = Anthropic(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai",
)
with client.messages.stream(
model="claude-opus-4-8",
max_tokens=4096,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "claude-opus-4-8",
messages: [{ role: "user", content: "Hello" }],
stream: true,
max_tokens: 4096,
temperature: 0.7,
// `group` is an api.reapi.ai-specific extension; pass via extra body.
// @ts-expect-error — not part of the OpenAI types
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "claude-opus-4-8",
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"max_tokens": 4096,
"temperature": 0.7,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The Claude Opus 4.8 chat workspace
lives on the `api.reapi.ai` platform — sign in there to create a key and
top up tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Endpoints
```http
POST https://api.reapi.ai/v1/chat/completions # OpenAI-compatible
POST https://api.reapi.ai/v1/messages # Anthropic-native
```
Both surfaces accept `claude-opus-4-8`. Pick whichever matches your SDK
of record:
* **`/v1/chat/completions`** — drop-in for the OpenAI SDKs. Same request
shape, same SSE wire format. Set `base_url` to `https://api.reapi.ai/v1`.
* **`/v1/messages`** — native Anthropic Messages format. Set `base_url`
to `https://api.reapi.ai` for the Anthropic Python / TypeScript SDKs.
Required for callers that need Anthropic-specific features
(`cache_control` blocks for prompt caching, native multi-block
content, the full tool-use spec).
***
## Request body — `/v1/chat/completions`
### `model` — string, required
Must be `"claude-opus-4-8"`. The value is echoed back in the response
envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec, plus content-parts for vision:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string OR content-parts array (text + image_url parts)"
}
```
Multi-turn history is sent in chronological order — the last message is
the one Claude responds to.
### `max_tokens` — integer, default `4096`
Upper bound on output tokens. **Anthropic's API requires `max_tokens`
on every call, including streamed ones** — even though the OpenAI SDKs
treat it as optional. Set it generously (`128000` is the hard cap on the
synchronous API) for long-form outputs; the model still stops at the
natural end of its response.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the
OpenAI format, terminated by a `data: [DONE]` line.
### `temperature` — number, default `1`
Range `0.0` – `1.0`. Sampling temperature. Anthropic recommends
**either** `temperature` or `top_p`, not both. Lower values produce
more deterministic output.
### `top_p` — number, default `1`
Range `0.0` – `1.0`. Nucleus sampling cutoff.
### `tools` / `tool_choice` — optional
Standard OpenAI tool-calling parameters. Claude Opus 4.8 supports the
full OpenAI tool-use spec via this surface and uses tools more
efficiently than prior Opus models — fewer steps for the same result.
For Anthropic's native tool-use schema (with `cache_control`,
`tool_choice_type`, etc.) call `/v1/messages` directly.
### `group` — string, default `"default"`
api.reapi.ai-specific extension. Selects a token group on the gateway,
which routes the request to a specific upstream channel pool. Omit if
default routing is fine.
***
## Vision input (multimodal)
Send images alongside text via OpenAI content-parts:
```json
{
"model": "claude-opus-4-8",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What does this chart show?" },
{
"type": "image_url",
"image_url": { "url": "https://example.com/chart.png" }
}
]
}
]
}
```
Supported image formats: PNG, JPEG, GIF, WebP. Base64 URLs work too —
prefix `data:image/png;base64,...`. Each image counts toward the input
token budget based on its resolution.
***
## Prompt caching
Anthropic's prompt caching pays off on stable system prompts, recurring
RAG context, and long multi-turn agent histories. The first call pays
the cache-write rate on the cacheable region; subsequent calls within
the cache window pay only the (much lower) cache-read rate on those
tokens.
To enable caching, call `/v1/messages` natively and add a
`cache_control` block. Example:
```json
{
"model": "claude-opus-4-8",
"max_tokens": 4096,
"system": [
{
"type": "text",
"text": "",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{ "role": "user", "content": "Question for the assistant" }
]
}
```
The cache key is the hash of the cacheable content. See
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) for cache-read and
cache-write rates.
***
## Response shape — `/v1/chat/completions`
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1735000000,
"model": "claude-opus-4-8",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21,
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
```
`usage.prompt_tokens_details.cached_tokens` reports how many input
tokens were served from cache — the part billed at the cache-read rate
rather than the standard input rate.
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta in
the OpenAI chunk format; the final event before `[DONE]` carries the
`finish_reason` (`stop` / `length` / `tool_calls` / `content_filter`).
***
## Pricing
Claude Opus 4.8 is billed **pay-as-you-go in USD** against your
api.reapi.ai token balance. It bills along several dimensions — input
tokens, output tokens, cache-read tokens, and per-request web search.
Current rates live on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) and in the pricing
card at the top of the
[model page](https://reapi.ai/models/claude-opus-4-8).
Per-call bill:
```
billable_input = (prompt_tokens - cached_tokens) × input_rate / 1,000,000
cache_read_bill = cached_tokens × cache_read_rate / 1,000,000
output_bill = completion_tokens × output_rate / 1,000,000
```
Cache-write rate applies on the first call that writes a cache block;
subsequent hits pay only the cache-read rate. Web search, when used, is
billed per request. Failed requests are not charged.
***
## Limits
| Limit | Value |
| ------------------- | ----------- |
| Context window | 1M tokens |
| Max output per call | 128K tokens |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape — HTTP status, plus a JSON
body:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
Common cases:
| Status | When | Notes |
| ------ | ------------------------------------- | ----------------------------------------------------------------------- |
| `400` | Missing `max_tokens`, bad shape, etc. | Anthropic requires `max_tokens`; OpenAI SDKs that omit it will 400 here |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you,
that's a one-for-one wire failure and a retry from your side is safe;
the upstream provider may have already produced output, but the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "claude-opus-4-8",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "Summarise this in three sentences." }
]
}
```
### Tool use (function calling, OpenAI surface)
```json
{
"model": "claude-opus-4-8",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo today?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### Vision
```json
{
"model": "claude-opus-4-8",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Read the error in this screenshot and suggest a fix." },
{
"type": "image_url",
"image_url": { "url": "https://your-cdn.com/screenshot.png" }
}
]
}
]
}
```
### Long context with prompt caching (native Anthropic surface)
```json
{
"model": "claude-opus-4-8",
"max_tokens": 4096,
"system": [
{
"type": "text",
"text": "<800K-token reference document>",
"cache_control": { "type": "ephemeral" }
}
],
"messages": [
{ "role": "user", "content": "Find every mention of the constraint X and list them with line numbers." }
]
}
```
***
## When to pick Claude Opus 4.8
Pick Claude Opus 4.8 when **output quality and reliability** dominate the
decision:
* **Long-horizon agentic coding** — multi-service refactors, codebase-scale
migrations, and agent runs that must stay on-task across many steps.
* **High-stakes reasoning** — work where a confident-but-wrong answer has
real downstream cost. Opus 4.8 is more likely to flag uncertainty than
to overclaim.
* **Large-context analysis** — full codebases, long research packs,
multi-document review, audit work.
Route lighter traffic (classification, short replies, tight loops) to
cheaper Claude or GPT models on the same key.
***
## Tips
* **Set `max_tokens` generously.** Anthropic enforces it strictly — the
model still stops at the natural end of its response, but a low cap
will truncate before the real ending.
* **Stream by default for chat UX.** Streaming cuts perceived latency
dramatically.
* **Cache the stable parts of long prompts.** A 500K-token RAG context
on top of a 1KB user question can pay the cache-read rate on every
subsequent call instead of the standard input rate — a big saving on
multi-turn agents replaying long histories.
* **Tune `temperature` *or* `top_p`, not both.** Mixing them produces
results that are hard to reason about.
* **Use the native `/v1/messages` surface for Anthropic-only features.**
`cache_control`, native multi-block content, full tool-use spec — all
of those work through `/v1/messages` without needing translation.
***
## Related
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# claude-opus-5 (https://reapi.ai/docs/claude-opus-5)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Anthropic's Claude Opus 5 — "for complex agentic coding and enterprise work" —
> exposed through api.reapi.ai as a drop-in OpenAI-compatible Chat Completions
> endpoint. A **1M-token** context window, **128k** max output, **adaptive
> thinking on by default**, an effort dial from `low` through `max`, plus tool
> use, structured outputs, code execution, vision and PDF input. The wire `model`
> id is `claude-opus-5` — Anthropic's alias carries no date suffix. Current rates
> live on the [model page](https://reapi.ai/models/claude-opus-5) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-opus-5",
"messages": [
{ "role": "user", "content": "Refactor this module and add tests." }
],
"stream": true,
"max_tokens": 16000
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="claude-opus-5",
messages=[{"role": "user", "content": "Refactor this module and add tests."}],
max_tokens=16000,
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="")
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "claude-opus-5",
messages: [{ role: "user", content: "Refactor this module and add tests." }],
max_tokens: 16000,
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "claude-opus-5",
"messages": []map[string]string{
{"role": "user", "content": "Refactor this module and add tests."},
},
"max_tokens": 16000,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Chat models are served from the `api.reapi.ai` gateway, which has its own console
and its own key. Create the key there, then send it as a bearer token:
```http
Authorization: Bearer YOUR_API_KEY
```
The gateway key is **not** the same credential as a media-generation key used
against `reapi.ai/api/v1`. Sign in at
[api.reapi.ai](https://api.reapi.ai/) to create one.
***
## Endpoint
```http
POST /v1/chat/completions
```
Base URL `https://api.reapi.ai`. The wire format is OpenAI-compatible, so the
same SDKs (`openai-python`, `openai-node`, `openai-go`, …) work once you swap the
base URL, the key and the model string.
***
## Request body
### `model` — string, required
Must be `claude-opus-5` exactly. Anthropic's alias carries no date suffix, so
there is nothing to append.
### `messages` — array, required
Conversation history, each entry an object with `role` and `content`. Roles are
`system`, `user` and `assistant`. Text, image and PDF parts are supported — see
[Multimodal input](#multimodal-input).
### `max_tokens` — integer
Upper bound on output tokens for this response, **thinking tokens included**.
Anthropic documents a **128k**-token output ceiling for this model.
**Thinking is on by default on this model**, and `max_tokens` caps thinking plus
response text together. A budget carried over from a model that ran without
thinking can truncate mid-answer. Raise it, or turn thinking off — subject to the
effort cap below.
### `stream` — boolean, default `false`
When `true`, tokens arrive as server-sent events terminated by `data: [DONE]`.
Recommended for anything a person watches, and effectively required for large
`max_tokens` values — a non-streamed request with a big budget can hit an HTTP
timeout.
### `thinking` — object, optional
Omit it and the model runs **adaptive thinking**. See [Thinking](#thinking) for
the two rules that differ from the previous Opus generation.
### `output_config` — object, optional
Carries `effort` and the structured-output format. See [Effort](#effort).
### `tools` / `tool_choice` — optional
Tool definitions and the strategy for picking among them.
### `group` — string, default `"default"`
Token group on the gateway. Leave it at `default` unless your account has been
given another group to route through.
***
## Thinking
Two things differ from the previous Opus generation, and both bite code that was
carried straight over.
**1. Thinking runs when you omit the parameter.** On the previous generation,
omitting `thinking` meant no thinking. Here it runs adaptive. That is a silent
cost and truncation change on any route that never set the field.
**2. Turning thinking off is capped at `high` effort.** `thinking` disabled is
accepted only at effort `high` or lower; pairing it with `xhigh` or `max` is
rejected. The check runs **per request**, so a later call that raises effort while
thinking is still disabled fails even though earlier calls in the same
conversation succeeded.
**The raw chain of thought is never returned on this model.** The default is
omitted; ask for a summary if you surface reasoning to users. Fixed
thinking-token budgets are not accepted.
Given how well this model performs at `low` and `medium` effort, a
latency-sensitive route is usually better served by lowering effort than by
disabling thinking.
***
## Effort
`output_config.effort` takes five levels — `low`, `medium`, `high`, `xhigh`,
`max` — and **defaults to `high`** on the Claude API.
| Level | Use it for |
| -------- | ------------------------------------------------------------------------------------ |
| `max` | The hardest reasoning, where correctness outweighs cost. Can overthink routine work. |
| `xhigh` | Coding and agentic workloads — the recommended starting point for those. |
| `high` | The default; intelligence-sensitive work generally. |
| `medium` | A cost step-down that holds up unusually well on this model. |
| `low` | Short, scoped, latency-sensitive tasks. |
Effort is the primary cost and latency lever here — start at `xhigh` for
coding/agentic work and `high` elsewhere, then sweep downward against your own
evals. Defaults carried over from an older model rarely transfer.
At `xhigh` or `max`, set a large `max_tokens` so there is room to think and act
across tool calls.
***
## Parameters that are no longer accepted
These are rejected on this generation. Remove them rather than working around
them:
| Parameter | Do this instead |
| ------------------------------- | ------------------------------------------------------ |
| `temperature`, `top_p`, `top_k` | Steer with prompting and the `effort` level |
| Fixed thinking-token budget | Use `effort` |
| Last-assistant-turn prefill | Use structured outputs, or a system-prompt instruction |
***
## Multimodal input
| Modality | Supported |
| -------- | :-------: |
| Text | ✅ |
| Image | ✅ |
| PDF | ✅ |
Output is **text only**. Vision is strong on charts, diagrams and dense
documents — and on this model, giving it tools to crop and re-examine an image is
a more cost-effective lever than raising thinking depth alone.
***
## Pricing dimensions
Billing is **per token**, in USD, against your `api.reapi.ai` balance, with
separate **input** and **output** rates. Three things to keep in mind:
* **Thinking tokens bill as output.** Since thinking is on by default here, that
is the usual reason a bill exceeds an estimate built from visible response
length alone.
* **Output dominates.** For most generative workloads the output rate decides the
invoice.
* **Effort moves the bill.** It changes how much the model thinks and how many
tool calls it makes, so it is the lever to tune before anything else.
Both token rates sit at **less than half Anthropic's published per-token rate**.
Current numbers are on the [model page](https://reapi.ai/models/claude-opus-5)
and [api.reapi.ai/pricing](https://api.reapi.ai/pricing) — those tables are the
canonical source, not this page.
Chat models bill in USD on the gateway balance. They do **not** draw down the
integer credits used by the media-generation endpoints on `reapi.ai/api/v1`.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1785000000,
"model": "claude-opus-5",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1204,
"completion_tokens": 860,
"total_tokens": 2064
}
}
```
`usage.completion_tokens` includes thinking tokens, so it is the number to
reconcile a bill against.
### Streaming (`stream: true`)
Server-sent events, each `data:` line carrying a `chat.completion.chunk` with the
incremental text in `choices[0].delta.content`, terminated by `data: [DONE]`.
***
## Errors
Failures use the gateway's standard envelope. See the
[errors catalog](/docs/api/errors) for the full code list. Common cases:
| Trigger | What to do |
| ------------------------------------------- | ----------------------------------------------------- |
| Missing or invalid bearer token | Create a key in the api.reapi.ai console |
| Unknown `model` value | Send `claude-opus-5` exactly — no date suffix |
| `temperature` / `top_p` / `top_k` present | Remove them; they are not accepted on this generation |
| Thinking disabled at `xhigh` / `max` effort | Lower effort to `high`, or leave thinking on |
| `max_tokens` above the model ceiling | Lower it; the documented ceiling is 128k |
| Insufficient balance | Top up on the gateway |
| Upstream rate limit | Retry with backoff, or route through another model |
Anthropic runs elevated safety classifiers on this generation. A declined request
can come back as a **successful response whose stop reason is a refusal**, with
an empty or partial body rather than an error. Check the stop reason before
reading the content — code that indexes the first content block unconditionally
will break on a refusal.
***
## Tips
* **Give the whole task up front.** This model is built for long-horizon
autonomous work; a complete specification in one turn outperforms revealing the
task across many short interactive turns.
* **Stream anything interactive**, and stream anything with a large `max_tokens`.
* **Budget for thinking.** If responses truncate mid-sentence on hard prompts, the
reasoning pass consumed the allowance — raise `max_tokens`.
* **Sweep effort, don't inherit it.** `low` and `medium` are unusually strong
here; the right level is workload-specific.
* **Delete "double-check your work" instructions.** This model verifies its own
output without being told, and telling it to verify causes redundant work.
* **Ask for coverage in code review, then filter.** Instructions like "only
report high-severity issues" are followed literally, which depresses measured
recall. Have it report everything with confidence and severity, and filter in a
separate pass.
* **Short prompts cache now.** The minimum cacheable prefix is 512 tokens, half
the previous generation's floor — prompts you had written off as uncacheable
may qualify.
***
## Related
* [Claude Opus 5 model page](https://reapi.ai/models/claude-opus-5) — current rates
* [Claude Opus 4.8](/docs/claude-opus-4-8) — the previous generation
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# claude-sonnet-4-6 (https://reapi.ai/docs/claude-sonnet-4-6)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Claude Sonnet 4.6 is Anthropic's balanced everyday chat model, exposed
> through api.reapi.ai as a drop-in OpenAI-compatible Chat Completions
> endpoint (native Anthropic `/v1/messages` also available). 1M token
> context, 128K max output, vision input, tool use, and fast production
> latency. Current rates live on the
> [model page](https://reapi.ai/models/claude-sonnet-4-6) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"max_tokens": 4096,
"temperature": 0.7
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="claude-sonnet-4-6",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
max_tokens=4096,
temperature=0.7,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```python
from anthropic import Anthropic
client = Anthropic(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai",
)
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=4096,
messages=[{"role": "user", "content": "Hello"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "claude-sonnet-4-6",
messages: [{ role: "user", content: "Hello" }],
stream: true,
max_tokens: 4096,
temperature: 0.7,
// @ts-expect-error — `group` is an api.reapi.ai-specific extension
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "claude-sonnet-4-6",
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"max_tokens": 4096,
"temperature": 0.7,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The Claude Sonnet 4.6 chat workspace
lives on the `api.reapi.ai` platform — sign in there to create a key and
top up tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Endpoints
```http
POST https://api.reapi.ai/v1/chat/completions # OpenAI-compatible
POST https://api.reapi.ai/v1/messages # Anthropic-native
```
Both surfaces accept `claude-sonnet-4-6`. Pick whichever matches your SDK
of record:
* **`/v1/chat/completions`** — drop-in for the OpenAI SDKs. Same request
shape, same SSE wire format. Set `base_url` to `https://api.reapi.ai/v1`.
* **`/v1/messages`** — native Anthropic Messages format. Set `base_url`
to `https://api.reapi.ai` for the Anthropic Python / TypeScript SDKs.
Use this when you want the full Anthropic tool-use spec or native
content blocks.
***
## Request body — `/v1/chat/completions`
### `model` — string, required
Must be `"claude-sonnet-4-6"`. The value is echoed back in the response
envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec, plus content-parts for vision:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string OR content-parts array (text + image_url parts)"
}
```
### `max_tokens` — integer, default `4096`
Upper bound on output tokens. **Anthropic's API requires `max_tokens`
on every call, including streamed ones** — even though the OpenAI SDKs
treat it as optional. Set it generously (`128000` is the hard cap); the
model still stops at the natural end of its response.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the
OpenAI format, terminated by a `data: [DONE]` line.
### `temperature` — number, default `1`
Range `0.0` – `1.0`. Sampling temperature. Anthropic recommends tuning
**either** `temperature` or `top_p`, not both.
### `top_p` — number, default `1`
Range `0.0` – `1.0`. Nucleus sampling cutoff.
### `tools` / `tool_choice` — optional
Standard OpenAI tool-calling parameters. For Anthropic's native tool-use
schema, call `/v1/messages` directly.
### `group` — string, default `"default"`
api.reapi.ai-specific extension. Selects a token group on the gateway.
Omit if default routing is fine.
***
## Vision input
Send images alongside text via OpenAI content-parts:
```json
{
"model": "claude-sonnet-4-6",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Summarise the error in this screenshot." },
{
"type": "image_url",
"image_url": { "url": "https://example.com/screenshot.png" }
}
]
}
]
}
```
Supported formats: PNG, JPEG, GIF, WebP. Base64 URLs work too
(`data:image/png;base64,...`). Each image counts toward the input token
budget based on its resolution.
***
## Response shape — `/v1/chat/completions`
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1735000000,
"model": "claude-sonnet-4-6",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21
}
}
```
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta in
the OpenAI chunk format; the final event before `[DONE]` carries the
`finish_reason` (`stop` / `length` / `tool_calls` / `content_filter`).
***
## Pricing
Claude Sonnet 4.6 is billed **pay-as-you-go in USD** against your
api.reapi.ai token balance. Current rates live on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) and in the pricing
card at the top of the
[model page](https://reapi.ai/models/claude-sonnet-4-6).
Per-call bill:
```
input_cost = prompt_tokens × input_rate / 1,000,000
output_cost = completion_tokens × output_rate / 1,000,000
```
Failed requests are not charged.
***
## Limits
| Limit | Value |
| ------------------- | ----------- |
| Context window | 1M tokens |
| Max output per call | 128K tokens |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
| Status | When | Notes |
| ------ | ------------------------------------- | ----------------------------------------------------------------------- |
| `400` | Missing `max_tokens`, bad shape, etc. | Anthropic requires `max_tokens`; OpenAI SDKs that omit it will 400 here |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you,
that's a one-for-one wire failure and a retry from your side is safe;
the upstream provider may have already produced output, but the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "claude-sonnet-4-6",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "Summarise this in three sentences." }
]
}
```
### Tool use (function calling)
```json
{
"model": "claude-sonnet-4-6",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### Vision
```json
{
"model": "claude-sonnet-4-6",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Read the error in this screenshot and suggest a fix." },
{
"type": "image_url",
"image_url": { "url": "https://your-cdn.com/screenshot.png" }
}
]
}
]
}
```
***
## When to pick Claude Sonnet 4.6 over Claude Opus 4.7
Both share the same endpoint, the same context window, and the same
OpenAI-compatible wire format — switching is a one-line change in the
`model` field. Pick Claude Sonnet 4.6 when:
* **Production chat traffic** where time-to-first-token shows up in
user-experience metrics.
* **Code review and PR triage** that runs across high volume.
* **Mid-complexity agents** where Claude-grade reasoning is enough and
Opus-tier reasoning would be overkill.
* **Default routing** — use Sonnet as the everyday model and escalate
to Opus for the genuinely hard turns.
Pick Claude Opus 4.7 for high-stakes coding, large refactors, complex
multi-step agents, and long-context analysis where output quality
dominates the decision.
***
## Tips
* **Set `max_tokens` generously.** Anthropic enforces it strictly; the
model still stops at the natural end of its response, but a low cap
will truncate before the real ending.
* **Stream by default for chat UX.** Sonnet's lower time-to-first-token
makes the perceived latency advantage especially visible in streamed
responses.
* **Tune `temperature` *or* `top_p`, not both.** Mixing them produces
results that are hard to reason about.
* **Use Sonnet as the default and escalate to Opus.** The cleanest
production pattern: route everything to Sonnet, switch to Opus on
the calls where quality matters most.
***
## Related
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# deepseek-v4 (https://reapi.ai/docs/deepseek-v4)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> The DeepSeek V4 API ships two open-weight models — **`deepseek-v4-flash`**
> (fast, low-cost) and **`deepseek-v4-pro`** (frontier reasoning and agentic
> coding) — exposed through api.reapi.ai as a drop-in OpenAI-compatible Chat
> Completions endpoint. Both bring a 1M-token context window, 384K max output,
> thinking mode on by default, vision input, tool use, and context caching.
> Current rates live on the
> [model page](https://reapi.ai/models/deepseek-v4) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"max_tokens": 4096,
"temperature": 0.7
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="deepseek-v4-flash", # or "deepseek-v4-pro"
messages=[{"role": "user", "content": "Hello"}],
stream=True,
max_tokens=4096,
temperature=0.7,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "deepseek-v4-flash", // or "deepseek-v4-pro"
messages: [{ role: "user", content: "Hello" }],
stream: true,
max_tokens: 4096,
temperature: 0.7,
// `group` is an api.reapi.ai-specific extension; pass via extra body.
// @ts-expect-error — not part of the OpenAI types
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "deepseek-v4-flash", // or "deepseek-v4-pro"
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"max_tokens": 4096,
"temperature": 0.7,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The DeepSeek V4 chat workspace lives on
the `api.reapi.ai` platform — sign in there to create a key and top up tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Models
The DeepSeek V4 family ships two variants. Both share the same endpoint,
request shape, 1M context window, and 384K max output — pick the variant
with the `model` field.
| `model` | Best for | Architecture |
| ------------------- | ------------------------------------------------------------------------------------------------------------- | ----------------------------- |
| `deepseek-v4-flash` | Fast, low-cost everyday work — autocomplete, batch analysis, chat backends. Reasoning closely approaches Pro. | 284B total / 13B active (MoE) |
| `deepseek-v4-pro` | Frontier reasoning, complex debugging, and agentic coding. Rivals top closed-source models. | 1.6T total / 49B active (MoE) |
The legacy ids `deepseek-chat` and `deepseek-reasoner` map to
`deepseek-v4-flash` in non-thinking and thinking mode respectively. New
integrations should use the explicit `deepseek-v4-flash` /
`deepseek-v4-pro` ids.
***
## Endpoint
```http
POST https://api.reapi.ai/v1/chat/completions
```
Drop-in for the OpenAI SDKs — same request shape, same SSE wire format. Set
`base_url` to `https://api.reapi.ai/v1`. DeepSeek V4 also supports the
Anthropic API format natively; this guide documents the OpenAI-compatible
Chat Completions surface.
***
## Request body
### `model` — string, required
`"deepseek-v4-flash"` or `"deepseek-v4-pro"`. Echoed back in the response
envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec, plus content-parts for vision:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string OR content-parts array (text + image_url parts)"
}
```
Multi-turn history is sent in chronological order — the last message is the
one the model responds to. Do **not** echo a prior turn's `reasoning_content`
back into `messages`; strip it before the next request.
### `max_tokens` — integer, default `4096`
Upper bound on output tokens for this response, **including the
chain-of-thought when thinking mode is on**. The synchronous API supports up
to 384K output tokens — set it generously for long-form or reasoning-heavy
outputs.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the OpenAI
format, terminated by a `data: [DONE]` line.
### `temperature` — number, default `1`
Sampling temperature. Lower values produce more deterministic output.
**Ignored while the model is in thinking mode.**
### `top_p` — number, default `1`
Nucleus sampling cutoff. Ignored in thinking mode.
### `frequency_penalty` / `presence_penalty` — number, default `0`
Standard OpenAI repetition controls. Ignored in thinking mode.
### `tools` / `tool_choice` — optional
Standard OpenAI tool-calling parameters. DeepSeek V4 ships dedicated agentic
optimizations with reliable function calling and JSON output.
### `group` — string, default `"default"`
api.reapi.ai-specific extension. Selects a token group on the gateway, which
routes the request to a specific upstream channel pool. Omit if default
routing is fine.
***
## Thinking mode
DeepSeek V4 runs in **thinking mode by default**: before the final answer it
produces a chain of thought, returned in a `reasoning_content` field at the
same level as `content`.
```json
{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"reasoning_content": "Let me work through this step by step...",
"content": "The final answer."
},
"finish_reason": "stop"
}
]
}
```
For latency-sensitive or simple calls, switch to **non-thinking mode** for
faster, cheaper responses. When thinking is on, the sampling parameters
(`temperature`, `top_p`, `frequency_penalty`, `presence_penalty`) have no
effect.
Strip `reasoning_content` from assistant messages before sending them back
in a follow-up request — the chain-of-thought from a previous turn is not
meant to be re-fed as input.
***
## Vision input (beta)
Send images alongside text via OpenAI content-parts:
```json
{
"model": "deepseek-v4-pro",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What does this chart show?" },
{
"type": "image_url",
"image_url": { "url": "https://example.com/chart.png" }
}
]
}
]
}
```
Each image counts toward the input token budget based on its resolution.
***
## Context caching
DeepSeek V4 caches stable prompt prefixes automatically. When a request hits
the cache, the cached input tokens bill at a small fraction of the standard
input rate — a big saving for agent loops and chatbots that replay long
system prompts and tool schemas. No configuration is required; reuse the same
prefix across calls and the discount applies. The
`usage.prompt_tokens_details.cached_tokens` field reports how many input
tokens were served from cache.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1735000000,
"model": "deepseek-v4-flash",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21,
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
```
When thinking mode is on, `message.reasoning_content` carries the
chain-of-thought alongside `content`.
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta in the
OpenAI chunk format; the final event before `[DONE]` carries the
`finish_reason` (`stop` / `length` / `tool_calls` / `content_filter`).
***
## Pricing
DeepSeek V4 is billed **pay-as-you-go in USD** against your api.reapi.ai
token balance. It bills along three dimensions — input tokens (cache miss),
input tokens (cache hit), and output tokens — and `deepseek-v4-pro` costs
more per token than `deepseek-v4-flash`. Current rates live on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) and in the pricing card
at the top of the
[model page](https://reapi.ai/models/deepseek-v4).
Per-call bill:
```
billable_input = (prompt_tokens - cached_tokens) × input_rate / 1,000,000
cache_read_bill = cached_tokens × cache_hit_rate / 1,000,000
output_bill = completion_tokens × output_rate / 1,000,000
```
Output tokens include the chain-of-thought when thinking mode is on. Failed
requests are not charged.
***
## Limits
| Limit | Value |
| ------------------- | ----------- |
| Context window | 1M tokens |
| Max output per call | 384K tokens |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape — HTTP status, plus a JSON body:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
Common cases:
| Status | When | Notes |
| ------ | ------------------------------------------ | -------------------------------------------- |
| `400` | Bad request shape, unsupported param combo | Check the `messages` array and `model` id |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you, that
is a one-for-one wire failure and a retry from your side is safe; the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "deepseek-v4-flash",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "Summarise this in three sentences." }
]
}
```
### Tool use (function calling)
```json
{
"model": "deepseek-v4-pro",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo today?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### Long-context analysis
```json
{
"model": "deepseek-v4-pro",
"max_tokens": 8192,
"messages": [
{ "role": "system", "content": "" },
{ "role": "user", "content": "List every mention of constraint X with line numbers." }
]
}
```
Keep the long reference block stable across calls so the cache-hit rate
applies on subsequent requests.
***
## When to pick Flash vs Pro
* **`deepseek-v4-flash`** — latency-sensitive, high-throughput, cost-sensitive
work: in-IDE autocomplete, inline suggestions, CI code review, bulk
summarization, chat backends. Reasoning closely approaches Pro at a fraction
of the price.
* **`deepseek-v4-pro`** — work where reasoning depth dominates: complex
debugging, architecture planning, math/STEM, and long-horizon agentic
coding. Both share one key — route per request.
***
## Tips
* **Set `max_tokens` generously when thinking is on.** The chain-of-thought
counts toward the output budget; a low cap can truncate before the final
answer.
* **Strip `reasoning_content` before the next turn.** Re-feeding a prior
turn's chain-of-thought as input is not supported.
* **Stream by default for chat UX.** Streaming cuts perceived latency.
* **Cache stable prefixes.** Reuse the same system prompt and tool schemas
across calls to bill repeated input at the low cache-hit rate.
* **Route by difficulty.** Send simple, high-volume calls to Flash and
reserve Pro for the hardest reasoning, all on one key.
***
## Related
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# enhance-video-1.0 (https://reapi.ai/docs/enhance-video-1-0)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> `enhance-video-1.0` is a single async endpoint that takes a source video URL and
> returns a higher-quality MP4 — upscaled, denoised, smart-interpolated, with
> optional scene-aware restoration. Two quality tiers (**standard** /
> **professional**) and five scene presets cover everything from AI-generated
> footage to old-film restoration. See current pricing on the
> [model page](https://reapi.ai/models/enhance-video-1-0).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "enhance-video-1.0",
"video_url": "https://cdn.example.com/source_720p.mp4",
"tool_version": "standard",
"scene": "aigc",
"resolution": "1080p"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "enhance-video-1.0",
"video_url": "https://cdn.example.com/source_720p.mp4",
"tool_version": "standard",
"scene": "aigc",
"resolution": "1080p",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "enhance-video-1.0",
video_url: "https://cdn.example.com/source_720p.mp4",
tool_version: "standard",
scene: "aigc",
resolution: "1080p",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "enhance-video-1.0",
"video_url": "https://cdn.example.com/source_720p.mp4",
"tool_version": "standard",
"scene": "aigc",
"resolution": "1080p",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "enhance-video-1.0",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` until `status === "completed"`. The completed
payload's `output.video_urls` holds the enhanced MP4 URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; polling
the task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Request body
### `model` — required
Always `"enhance-video-1.0"`.
### `video_url` — required (string)
Public HTTP/HTTPS URL of the source video.
* **Allowed schemes:** `http://`, `https://` only. Base64 / `data:` URIs are
rejected.
* **Formats:** mp4, flv, ts, avi, mov, wmv, mkv (and most other mainstream
containers).
* **Size:** up to **10 GB** per file.
* **Duration:** up to **2 hours**.
* **Input resolution:** up to **2K** (short side ≤ 1440 px). Use
`resolution: "4k"` to upscale beyond the source.
### `tool_version` — optional (`"standard"` | `"professional"`)
Default: `"standard"`.
* **`standard`** — balanced quality / speed. Internally runs \~10 frequently
used algorithms; covers what most distribution platforms expect. Roughly
6 – 10× the source duration to process.
* **`professional`** — maximum quality. Runs 30+ deep AI algorithms for
shot-level enhancement. Roughly 30+× the source duration to process and
\~10× the credits of `standard`.
### `scene` — optional (string)
Selects a scene-tuned preset. Default: `"common"`.
| Value | Recommended for |
| -------------- | -------------------------------------------------------------------------------- |
| `common` | General-purpose enhancement (no scene-specific tuning). |
| `ugc` | Compressed UGC clips — fixes blocking, color banding, blur. |
| `short_series` | Short-drama footage — face / detail enhancement, stylized contrast. |
| `aigc` | Low-res AI-generated video — super-resolution + detail repaint. |
| `old_film` | Aged film — temporal denoise, scratch removal, deflicker, color cast correction. |
### `resolution` — optional (string)
Target output resolution. Mutually exclusive with `resolution_limit`.
Allowed values: `240p`, `360p`, `480p`, `540p`, `720p`, `1080p`, `2k`, `4k`.
If unset, the output keeps the source resolution.
### `resolution_limit` — optional (integer)
Lock the output **short-side** to this exact pixel count, then scale the
long side to preserve the source ratio. Range `[64, 2160]`.
Mutually exclusive with `resolution`.
Use this when you need precise output dimensions (e.g. `resolution_limit: 720`
with a 640×480 source produces 960×720).
### `fps` — optional (number)
Target output frame rate, max 120. If unset, the output matches the source
frame rate. Setting a value higher than the source triggers AI frame
interpolation; staying within 4× the source is recommended for natural-looking
motion.
### `client_token` — optional (string)
Client-supplied idempotency token. Up to 64 ASCII printable characters.
The same token within a short window returns the same `task_id`.
### `callback_args` — optional (string)
Opaque blob (≤ 512 bytes) you supply at submit time and receive back when
the task completes (via the standard task envelope). Useful for stitching
results back to your business records without storing the `task_id`.
***
## Output
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "enhance-video-1.0",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": [
"https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"
]
},
"error": null
}
```
The MP4 lives on our CDN — copy it to your own storage if you need long-term
retention.
***
## Pricing
Billed by **output duration × tier × resolution × fps tier**. The output
duration always matches the source video duration (`enhance-video-1.0`
does not change clip length).
When you submit, the gateway probes your `video_url` server-side to determine
the exact billable seconds and resolution bucket. If the URL is unreachable or
returns no metadata, you receive a `400 Could not determine source video
metadata for billing` and **no credits are charged**.
The bill scales with three knobs:
* **`tool_version`** — `professional` is roughly 10× the cost of `standard`.
* **Output resolution bucket** — `720p` \< `1080p` \< `2k` \< `4k`. Each step
doubles the cost.
* **Output fps tier** — `> 30 fps` doubles the cost vs `≤ 30 fps`. If you
don't pass `fps`, the output keeps the source frame rate; billing assumes
the **`≤ 30 fps`** tier, which matches the vast majority of user-uploaded
video. To opt into the `> 30 fps` tier, set `fps` explicitly.
See the live banner on the [model page](https://reapi.ai/models/enhance-video-1-0)
for the current rate range in credits.
***
## Errors
Standard envelope:
```json
{
"error": {
"code": 20003,
"message": "resolution and resolution_limit are mutually exclusive",
"request_id": "req_..."
}
}
```
Common cases:
| Code | When |
| ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `20002` | `video_url` missing. |
| `20003` | Invalid enum (`tool_version`, `scene`, `resolution`), `resolution` + `resolution_limit` both set, `resolution_limit` outside `[64, 2160]`, or `video_url` not a public http(s) URL. |
| `30002` | Source video metadata couldn't be probed (used for billing). |
| `30001` | Insufficient credits for the probed source duration. |
| `80003` | Provider rejected or failed the task (e.g. unsupported codec). |
---
# flux-2 (https://reapi.ai/docs/flux-2)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Black Forest Labs' FLUX.2 on reAPI — photoreal **text-to-image** and
> **multi-reference editing** (up to 8 images), with accurate in-image text.
> Two tiers: `flux-2` (Pro) and `flux-2-flex` (Flex). Submit returns a
> `task_id`; poll until ready. See current pricing on the
> [model page](https://reapi.ai/models/flux-2).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-2",
"prompt": "an infographic poster titled OPENING SOON, clean layout",
"aspect_ratio": "3:4",
"resolution": "2K"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "flux-2",
"prompt": "an infographic poster titled OPENING SOON, clean layout",
"aspect_ratio": "3:4",
"resolution": "2K",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "flux-2",
prompt: "an infographic poster titled OPENING SOON, clean layout",
aspect_ratio: "3:4",
resolution: "2K",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "flux-2",
"prompt": "an infographic poster titled OPENING SOON, clean layout",
"aspect_ratio": "3:4",
"resolution": "2K",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "flux-2",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls` holds the
generated image URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Tiers
Two customer model ids share one parameter shape:
| Model id | Tier | Notes |
| ------------- | ---- | ------------------------------------------------------ |
| `flux-2` | Pro | Production-ready, fast, lower price. The default tier. |
| `flux-2-flex` | Flex | Maximum quality / detail, higher price. |
## Modes
There is no `mode` field — the modality is implicit in the request shape:
* **Text-to-image** — no `input_urls`.
* **Image-to-image / multi-reference editing** — pass `input_urls` (1–8
reference images). FLUX.2 keeps characters, products, and styles consistent
across the result.
***
## Request body
### `model` — string, required
One of `flux-2` (Pro) or `flux-2-flex` (Flex).
### `prompt` — string, required
**3–5,000 characters.** Put any text you want rendered in the image in quotes.
### `input_urls` — array, optional
1–8 reference images for image-to-image / multi-reference editing. **Public
HTTPS URLs only** — base64 / `data:` URIs are rejected at the gateway. Passing
any image switches the request to editing mode.
### `aspect_ratio` — string, default `1:1`
One of `1:1`, `4:3`, `3:4`, `16:9`, `9:16`, `3:2`, `2:3`. The value `auto`
(match the first reference image) is available **only for image-to-image**.
`aspect_ratio: "auto"` requires `input_urls`. Using it without reference images
(text-to-image) is rejected with `400`.
### `resolution` — string, default `1K`
`1K` or `2K`. The `2K` tier renders up to 4MP.
### `nsfw_checker` — boolean, optional, default `false`
Enables upstream content moderation. Off by default.
***
## Pricing
FLUX.2 bills a flat rate **per generated image**, by tier (`flux-2` /
`flux-2-flex`) and resolution (`1K` / `2K`). Text-to-image and image-to-image
cost the same within a tier:
```
credits = ceil(per_image_usd × 1000)
```
where `1 credit = $0.001 USD`. Each request produces one image. Failed and
rejected requests are not charged.
The exact per-image credit cost for each tier/resolution surfaces on the
[model page](https://reapi.ai/models/flux-2) and through the playground
estimator before submit.
***
## Response
The poll envelope returns the image URL in `output.image_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "flux-2",
"status": "completed",
"output": {
"image_urls": [
"https://cdn.reapi.ai/...jpg"
]
}
}
```
Generated URLs expire — mirror them to your own storage if you need long-term
retention.
***
## Errors
Failures return the standard reAPI envelope `{ error: { code, message,
request_id } }`. Common cases:
* Invalid input (prompt outside 3–5000 chars, more than 8 `input_urls`,
`auto` aspect without reference images, a non-HTTPS URL) → `400`.
* Insufficient credits → `402`.
* Rate limited → `429`.
See the full catalog at [/docs/api/errors](/docs/api/errors).
***
## Tips
* Put the literal text you want rendered **in the prompt, in quotes** — FLUX.2
is tuned for legible typography, ad headlines, and infographics.
* For consistency across a campaign, pass the same reference image(s) in
`input_urls` and let FLUX.2 keep the character/style on-model.
* Use `flux-2` (Pro) for fast, cost-efficient production and `flux-2-flex`
(Flex) when a hero shot needs maximum quality.
* Reach for `resolution: "2K"` (up to 4MP) only when the asset needs the detail.
***
## Related
* [Image generation models](/docs/gpt-image-2)
* [Qwen Image 2](/docs/qwen-image-2)
* [Tasks API](/docs/api/tasks)
* [Error codes](/docs/api/errors)
---
# FLUX 3 (https://reapi.ai/docs/flux-3)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
**Coming soon.** FLUX 3 has no public API yet. Black Forest Labs announced the
model on 2026-07-23 and is rolling it out in phases, starting with Early
Access for FLUX 3 Video. Official model ids, parameters, limits and pricing
have **not** been published. Everything below marked *(preview)* is the shape
reAPI expects to expose — treat it as provisional and watch the
[model page](https://reapi.ai/models/flux-3) for launch.
> FLUX 3 is Black Forest Labs' multimodal foundation model: images, video and
> audio learned jointly inside one architecture built on Self-Flow. It
> generates video with native audio — up to 20 seconds in a single generation —
> follows keyframes, reference images, reference clips and existing
> video-audio, speaks multilingual dialogue, and synthesizes and edits images
> from the same backbone.
## Status
FLUX 3 is **not callable on reAPI yet**. Black Forest Labs' published rollout
order is:
| Stage | Capability | Access |
| ----- | --------------------------------------------------- | ---------------------------------------------- |
| 1 | FLUX 3 Video — video + audio generation and editing | Early Access started |
| 2 | FLUX 3 Action / FLUX-mimic — action prediction | Selected research + commercial partners |
| 3 | FLUX 3 Image — image synthesis and editing | Early Access announced for the following weeks |
| 4 | FLUX 3 Dev — open-weight multimodal backbone | After the phases above |
When the public API lands:
* The `model` ids, full parameter set, constraints and rates are finalized here.
* The playground on the [model page](https://reapi.ai/models/flux-3) goes live.
* This banner is removed.
## Capabilities
Published by Black Forest Labs for FLUX 3 Video. All outputs carry native
audio generation.
* **Text-to-video** — prompt in, video with sound out.
* **Image-to-video** — continue from a starting frame ("animation"), or use
images as visual references.
* **Video-to-video** — carry central elements of a source clip, such as the
same character, into a new scene or context.
* **Video-audio continuation** — extend an existing video *and* its audio.
* **Keyframe-to-video** — controlled transitions between defined moments.
* **Multilingual dialogue** — spoken dialogue across languages.
* **Typography** — strong text rendering and animated designs inside the frame.
* **Agentic chaining** — individual clips chained into multi-shot sequences
that run for minutes, with visual references holding characters consistent.
Single-generation ceiling: **20 seconds**. Longer pieces come from chaining.
FLUX 3 Image adds synthesis and editing across styles, aspect ratios and
resolutions, with high-accuracy text in multiple languages.
## Quick example (preview)
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-3",
"prompt": "A rain-soaked cafe terrace at dusk, a tram rolls past",
"duration": 10
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "flux-3",
"prompt": "A rain-soaked cafe terrace at dusk, a tram rolls past",
"duration": 10,
},
timeout=30,
)
task = resp.json()
print(task["id"])
```
```js
const resp = await fetch('https://reapi.ai/api/v1/videos/generations', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'flux-3',
prompt: 'A rain-soaked cafe terrace at dusk, a tram rolls past',
duration: 10,
}),
});
const task = await resp.json();
console.log(task.id);
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "flux-3",
"prompt": "A rain-soaked cafe terrace at dusk, a tram rolls past",
"duration": 10,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var task map[string]any
json.NewDecoder(resp.Body).Decode(&task)
fmt.Println(task["id"])
}
```
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate project
header.
## Endpoint (preview)
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
## Request body (preview)
Black Forest Labs has not published FLUX 3's parameter surface. The field
names below are reAPI's provisional mapping of the **input types BFL named**
in its announcement — they are how the model will be wired in, not a quoted
vendor spec. Expect names, defaults and enums to change at launch.
Mode is implicit: which media fields you set decides text-to-video,
image-to-video, video-to-video, continuation, or keyframe transition.
| Field | Type | Default | Notes |
| ------------ | --------- | ------- | ------------------------------------------------------------------------------- |
| `model` | string | — | `flux-3`. **Required.** |
| `prompt` | string | — | Scene, motion and sound to generate. **Required.** |
| `duration` | integer | — | Output length in seconds. Published ceiling: `20`. |
| `image_urls` | string\[] | — | Starting frame to animate, or images as visual references. Public HTTP(S) URLs. |
| `video_urls` | string\[] | — | Reference clip for video-to-video, or the video to continue. |
| `audio_urls` | string\[] | — | Reference audio for video-audio continuation. |
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — every URL
field must be a public HTTP(S) URL. Upload to your own object storage (S3, R2,
OSS, …) and pass the URL.
## Response envelope (preview)
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "flux-3",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/.../0.mp4"]
},
"error": null
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. `output.video_urls` holds the generated MP4 URL —
audio is muxed into that file, not returned separately.
## Pricing
Black Forest Labs has **not announced FLUX 3 pricing**, and reAPI does not
publish speculative rates. Once the API ships, the rate appears on the
[model page](https://reapi.ai/models/flux-3) — that table is dynamic and always
reflects the current price.
Video models on reAPI bill per second of output:
```
credits = ceil(per_second_usd × billable_seconds × 1000)
```
where `1 credit = $0.001`. Failed jobs are refunded automatically.
## Errors
FLUX 3 will return the platform's standard envelope:
```json
{
"error": {
"code": "INVALID_REQUEST",
"message": "duration must be at most 20",
"request_id": "req_01hq2k..."
}
}
```
See the [errors catalog](/docs/api/errors) for the full code list and retry
guidance.
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [FLUX.2 — available now](/docs/flux-2)
* [Seedance 2.0 — available now](/docs/seedance-2-0)
---
# gemini-2.5-flash-image-preview (https://reapi.ai/docs/gemini-2-5-flash-image-preview)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Async image generation tuned for speed. One endpoint covers
> text-to-image and image-to-image with up to 14 reference images.
> Two channels share the same parameter shape:
>
> * `gemini-2.5-flash-image-preview` — default channel.
> * `gemini-2.5-flash-image-preview-official` — Google's official endpoint.
* Async processing — `POST` returns a `task_id`, poll `GET /api/v1/tasks/{id}` for the result.
* standard `/api/v1/images/generations` envelope (text-to-image / image-to-image).
* 10 ratios via `size`; only **1K** resolution on this model.
* Up to **14** reference images via `image_urls` — public HTTP(S) URLs only.
* Up to **4** images per request via `n`.
* Failed / moderated requests are not charged.
***
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-2.5-flash-image-preview",
"prompt": "moonlight on a bamboo path, ink wash painting",
"size": "16:9"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "gemini-2.5-flash-image-preview",
"prompt": "moonlight on a bamboo path, ink wash painting",
"size": "16:9",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gemini-2.5-flash-image-preview",
prompt: "moonlight on a bamboo path, ink wash painting",
size: "16:9",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gemini-2.5-flash-image-preview",
"prompt": "moonlight on a bamboo path, ink wash painting",
"size": "16:9",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-2.5-flash-image-preview",
"status": "processing",
"created_at": 1735000000
}
```
Hold onto `id` and poll `GET /api/v1/tasks/{id}` until completion.
***
## Endpoint
```http
POST /api/v1/images/generations # submit a job
GET /api/v1/tasks/{id} # poll for the result
```
Async — submit returns a `task_id` immediately; the actual image
arrives via the polling endpoint. Polling is free.
***
## Authentication
Bearer token, minted at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer rk_live_xxx
```
***
## Body
### `model`
`string` · **required**
One of:
* `"gemini-2.5-flash-image-preview"` — default channel.
* `"gemini-2.5-flash-image-preview-official"` — Google's official endpoint.
### `prompt`
`string` · **required**
1 – 2000 characters. English and Chinese both supported. Don't
restate the ratio in the prompt — pass it via `size`.
Image-to-image is no exception — the prompt describes what to do with
the reference. Sending `image_urls` without a `prompt` returns `400`.
### `size`
`string` · default `"1:1"` (text-to-image) / inherited (image-to-image)
Output ratio. One of these 10 values:
```
1:1 2:3 3:2 3:4 4:3
4:5 5:4 9:16 16:9 21:9
```
Anything outside the list is rejected with `400`.
When `image_urls` is provided **without** `size`, the output inherits the
first reference image's resolution (image-to-image mode). Pass `size`
to force a specific ratio.
### `resolution`
`string` · default `"1K"`
Only `1K` is supported on this model. Sending `2K` / `4K` returns
`400`. Switch to
[`gemini-3-pro-image-preview`](/docs/gemini-3-pro-image-preview) or
[`gemini-3.1-flash-image-preview`](/docs/gemini-3-1-flash-image-preview)
when you need higher resolutions.
### `n`
`integer` · default `1`
1 – 4 images per request. Must be a number, not a string.
### `image_urls`
`string[]` · optional
Reference images for image-to-image. Up to **14** entries. Each entry
must be a public HTTP(S) URL — `data:` / base64 payloads are rejected
at the gateway. Upload to your own object storage (S3 / R2 / OSS) and
pass the URL.
***
## Use cases
### 1. Text-to-image (minimal)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-2.5-flash-image-preview",
"prompt": "moonlight on a bamboo path, ink wash painting"
}'
```
### 2. Text-to-image, widescreen
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-2.5-flash-image-preview",
"prompt": "a corgi astronaut on the moon, cinematic",
"size": "16:9"
}'
```
### 3. Multiple variations in one call
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-2.5-flash-image-preview",
"prompt": "vintage botanical illustration, hibiscus",
"size": "4:5",
"n": 4
}'
```
### 4. Image-to-image (single reference)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-2.5-flash-image-preview",
"prompt": "turn this photo into a watercolor painting",
"image_urls": ["https://your-cdn.com/photo.jpg"]
}'
```
### 5. Multi-reference fusion
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-2.5-flash-image-preview",
"prompt": "blend these two photos into a poster",
"size": "4:3",
"image_urls": [
"https://your-cdn.com/photo-a.jpg",
"https://your-cdn.com/photo-b.jpg"
]
}'
```
### 6. Force the official channel
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-2.5-flash-image-preview-official",
"prompt": "studio portrait of a tabby cat, soft light",
"size": "1:1"
}'
```
***
## Response
`POST` returns immediately with `status: "processing"`; poll
`GET /api/v1/tasks/{id}` until `status` is `completed` or `failed`.
Both responses share the same envelope — the `output` field is
`null` until the task finishes.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-2.5-flash-image-preview",
"status": "processing",
"created_at": 1735000000
}
```
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-2.5-flash-image-preview",
"status": "completed",
"created_at": 1735000000,
"output": {
"image_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png"]
},
"error": null
}
```
URLs are stable for **7 days**. Download to your own storage if you
need them longer.
```json
{
"error": {
"code": 20003,
"message": "gemini-2.5-flash-image-preview: only resolution=1K is supported",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 10003,
"message": "API key invalid",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 30001,
"message": "Insufficient credits",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 50001,
"message": "Rate limit exceeded",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 60099,
"message": "Internal error — please retry",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
### Upstream errors (502 / 503)
reAPI does not surface raw upstream `5xx` codes to the caller. When
the upstream provider returns `502 Bad Gateway`, `503 Service
Unavailable`, or a connection failure, the worker retries up to 5
attempts with exponential backoff (\~30s total budget). If all
retries fail, the task ends with one of these reapi-specific error
codes (returned via `GET /api/v1/tasks/{id}` as `error.code`):
| reapi code | Meaning | Origin |
| ---------- | -------------------------- | ------------------------------------ |
| `80001` | `provider_submit_failed` | Upstream `5xx` / network on submit |
| `80002` | `provider_polling_timeout` | Wall-clock cap reached while polling |
| `80003` | `provider_failed` | Upstream returned a terminal failure |
See [Errors catalog](/docs/api/errors) for the full code list.
***
## Polling
| `status` | Meaning |
| ------------ | ----------------------------- |
| `processing` | Submitted, still generating |
| `completed` | `output.image_urls` is ready |
| `failed` | See `error.code`; not charged |
Recommended cadence:
```
0–10s: wait before the first poll
10s–60s: poll every 2–3s
60s+: back off to 5s; cap at 15s
```
A 1K image typically completes in 5 – 20 seconds.
***
## Related
* [`gemini-3-pro-image-preview`](/docs/gemini-3-pro-image-preview) — higher-quality variant with 1K / 2K / 4K resolutions.
* [`gemini-3.1-flash-image-preview`](/docs/gemini-3-1-flash-image-preview) — newer variant with extreme ratios and Google search grounding.
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Pricing — gemini-2.5-flash-image-preview](https://reapi.ai/models/gemini-2-5-flash-image-preview#pricing)
---
# gemini-3.1-flash-image-preview (https://reapi.ai/docs/gemini-3-1-flash-image-preview)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Async image generation with a wide ratio surface (down to
> 1:8 / 8:1) and optional Google search grounding. One endpoint covers
> text-to-image and image-to-image with up to 14 reference images. Two
> channels share the same parameter shape:
>
> * `gemini-3.1-flash-image-preview` — default channel.
> * `gemini-3.1-flash-image-preview-official` — Google's official endpoint.
* Async processing — `POST` returns a `task_id`, poll `GET /api/v1/tasks/{id}` for the result.
* standard `/api/v1/images/generations` envelope (text-to-image / image-to-image).
* 14 ratios via `size`, including extreme `1:4` / `4:1` / `1:8` / `8:1`.
* Four resolution tiers via `resolution`: `0.5K` / `1K` / `2K` / `4K`.
* Up to **14** reference images via `image_urls` — public HTTP(S) URLs only.
* Up to **4** images per request via `n`.
* Optional Google search grounding via `google_search` and `google_image_search`.
***
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "neon-lit cyberpunk skyline at midnight",
"size": "16:9",
"resolution": "2K"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "gemini-3.1-flash-image-preview",
"prompt": "neon-lit cyberpunk skyline at midnight",
"size": "16:9",
"resolution": "2K",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gemini-3.1-flash-image-preview",
prompt: "neon-lit cyberpunk skyline at midnight",
size: "16:9",
resolution: "2K",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gemini-3.1-flash-image-preview",
"prompt": "neon-lit cyberpunk skyline at midnight",
"size": "16:9",
"resolution": "2K",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-3.1-flash-image-preview",
"status": "processing",
"created_at": 1735000000
}
```
Hold onto `id` and poll `GET /api/v1/tasks/{id}` until completion.
***
## Endpoint
```http
POST /api/v1/images/generations # submit a job
GET /api/v1/tasks/{id} # poll for the result
```
Async — submit returns a `task_id` immediately; the actual image
arrives via the polling endpoint. Polling is free.
***
## Authentication
Bearer token, minted at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer rk_live_xxx
```
***
## Body
### `model`
`string` · **required**
One of:
* `"gemini-3.1-flash-image-preview"` — default channel.
* `"gemini-3.1-flash-image-preview-official"` — Google's official endpoint.
### `prompt`
`string` · **required**
Detailed prompts produce better results. English and Chinese both
supported. Don't restate the ratio in the prompt — pass it via `size`.
Image-to-image is no exception — the prompt describes what to do with
the reference. Sending `image_urls` without a `prompt` returns `400`.
### `size`
`string` · default `"1:1"` (text-to-image) / inherited (image-to-image)
Output ratio. One of these 14 values:
```
1:1 2:3 3:2 3:4 4:3
4:5 5:4 9:16 16:9 21:9
1:4 4:1 1:8 8:1
```
`1:4` / `4:1` / `1:8` / `8:1` are useful for tall posters, banner
ads, and long-strip social formats. Anything outside the list is
rejected with `400`.
### `resolution`
`string` · default `"1K"`
`0.5K` / `1K` / `2K` / `4K`. Case-insensitive — `"2K"` and `"2k"` are
equivalent.
### `n`
`integer` · default `1`
1 – 4 images per request. Must be a number, not a string.
### `image_urls`
`string[]` · optional
Reference images for image-to-image. Up to **14** entries. Each entry
must be a public HTTP(S) URL — `data:` / base64 payloads are rejected
at the gateway. Recommended split: ≤ 10 object references + ≤ 4
character references for best consistency.
### `google_search`
`boolean` · default `false`
When `true`, the model first searches the web for textual context to
ground the generated image in real-world facts. Useful for prompts
that reference recent events, named entities, or specific places.
Adds an extra search round-trip — budget extra polling time.
### `google_image_search`
`boolean` · default `false`
When `true`, the model also searches the web for **image** references
in addition to text. **Requires `google_search: true`** — sending
`google_image_search: true` with `google_search: false` (or omitted)
returns `400`.
***
## Use cases
### 1. Text-to-image (minimal)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "neon-lit cyberpunk skyline at midnight"
}'
```
### 2. Long-banner poster (extreme ratio)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "long horizontal banner of a serene lakeside at sunrise",
"size": "8:1",
"resolution": "2K"
}'
```
### 3. Tall mobile-first poster
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "vertical movie poster, lone astronaut on a frozen moon",
"size": "1:4",
"resolution": "2K"
}'
```
### 4. Search-grounded generation
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "the Eiffel Tower lit up for Bastille Day fireworks",
"size": "16:9",
"resolution": "2K",
"google_search": true,
"google_image_search": true
}'
```
### 5. Image-to-image with reference
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "stylize this product photo as a vintage magazine cover",
"image_urls": ["https://your-cdn.com/product.jpg"],
"size": "4:5",
"resolution": "2K"
}'
```
### 6. Multi-reference fusion
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "place subject from reference 1 into the scene of reference 2",
"size": "16:9",
"resolution": "4K",
"image_urls": [
"https://your-cdn.com/subject.jpg",
"https://your-cdn.com/scene.jpg"
]
}'
```
### 7. Low-cost preview at 0.5K
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3.1-flash-image-preview",
"prompt": "concept thumbnail of a forest temple",
"size": "1:1",
"resolution": "0.5K",
"n": 4
}'
```
***
## Response
`POST` returns immediately with `status: "processing"`; poll
`GET /api/v1/tasks/{id}` until `status` is `completed` or `failed`.
Both responses share the same envelope — the `output` field is
`null` until the task finishes.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-3.1-flash-image-preview",
"status": "processing",
"created_at": 1735000000
}
```
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-3.1-flash-image-preview",
"status": "completed",
"created_at": 1735000000,
"output": {
"image_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png"]
},
"error": null
}
```
URLs are stable for **7 days**. Download to your own storage if you
need them longer.
```json
{
"error": {
"code": 20003,
"message": "gemini-3.1-flash-image-preview: google_image_search requires google_search:true",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 10003,
"message": "API key invalid",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 30001,
"message": "Insufficient credits",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 50001,
"message": "Rate limit exceeded",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 60099,
"message": "Internal error — please retry",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
### Upstream errors (502 / 503)
reAPI does not surface raw upstream `5xx` codes to the caller. When
the upstream provider returns `502 Bad Gateway`, `503 Service
Unavailable`, or a connection failure, the worker retries up to 5
attempts with exponential backoff (\~30s total budget). If all
retries fail, the task ends with one of these reapi-specific error
codes (returned via `GET /api/v1/tasks/{id}` as `error.code`):
| reapi code | Meaning | Origin |
| ---------- | -------------------------- | ------------------------------------ |
| `80001` | `provider_submit_failed` | Upstream `5xx` / network on submit |
| `80002` | `provider_polling_timeout` | Wall-clock cap reached while polling |
| `80003` | `provider_failed` | Upstream returned a terminal failure |
See [Errors catalog](/docs/api/errors) for the full code list.
***
## Polling
| `status` | Meaning |
| ------------ | ----------------------------- |
| `processing` | Submitted, still generating |
| `completed` | `output.image_urls` is ready |
| `failed` | See `error.code`; not charged |
Recommended cadence:
```
0–15s: wait before the first poll
15s–2m: poll every 3–5s
2m+: back off to 10s; cap at 30s
```
A 1K image typically completes in 10 – 30 seconds; 4K and Google
search grounding both add latency.
***
## Related
* [`gemini-2.5-flash-image-preview`](/docs/gemini-2-5-flash-image-preview) — earlier variant (1K only).
* [`gemini-3-pro-image-preview`](/docs/gemini-3-pro-image-preview) — higher-fidelity variant without search grounding.
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Pricing — gemini-3.1-flash-image-preview](https://reapi.ai/models/gemini-3-1-flash-image-preview#pricing)
---
# gemini-3-6-flash (https://reapi.ai/docs/gemini-3-6-flash)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Google's Gemini 3.6 Flash, exposed through api.reapi.ai as a drop-in
> OpenAI-compatible Chat Completions endpoint. A **1,048,576-token** input window,
> **65,536-token** max output, inputs across **text, image, video, audio and
> PDF** with text output, plus **thinking**, **function calling**, **code
> execution**, **structured outputs**, **context caching**, **file search**,
> **URL context** and **Search / Maps grounding**. The wire `model` id is
> `gemini-3.6-flash`. Current rates live on the
> [model page](https://reapi.ai/models/gemini-3-6-flash) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3.6-flash",
"messages": [
{ "role": "user", "content": "Summarise the attached contract in five bullets." }
],
"stream": true,
"max_tokens": 8192
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="gemini-3.6-flash",
messages=[{"role": "user", "content": "Summarise the attached contract in five bullets."}],
max_tokens=8192,
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="")
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "gemini-3.6-flash",
messages: [
{ role: "user", content: "Summarise the attached contract in five bullets." },
],
max_tokens: 8192,
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gemini-3.6-flash",
"messages": []map[string]string{
{"role": "user", "content": "Summarise the attached contract in five bullets."},
},
"max_tokens": 8192,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Chat models are served from the `api.reapi.ai` gateway, which has its own
console and its own key. Create the key there, then send it as a bearer token:
```http
Authorization: Bearer YOUR_API_KEY
```
The gateway key is **not** the same credential as a media-generation key used
against `reapi.ai/api/v1`. Sign in at
[api.reapi.ai](https://api.reapi.ai/) to create one.
***
## Endpoint
```http
POST /v1/chat/completions
```
Base URL `https://api.reapi.ai`. The wire format is OpenAI-compatible, so the
same SDKs (`openai-python`, `openai-node`, `openai-go`, …) work once you swap
the base URL, the key and the model string. The gateway also lists a native
Gemini endpoint type for this model if you prefer Google's own request format.
***
## Request body
### `model` — string, required
Must be `gemini-3.6-flash` exactly. The dotted form is the wire id; the
`/models/gemini-3-6-flash` landing page uses a hyphenated slug for the URL.
### `messages` — array, required
Conversation history, each entry an object with `role` and `content`. Roles are
`system`, `user` and `assistant`. Multimodal parts are supported — see
[Multimodal input](#multimodal-input).
### `max_tokens` — integer
Upper bound on output tokens for this response, **thinking tokens included**.
Google documents a **65,536**-token output ceiling for this model, so higher
values are clamped to it.
### `stream` — boolean, default `false`
When `true`, tokens arrive as server-sent events terminated by `data: [DONE]`.
Recommended for anything a person watches.
### `temperature` — number, default `1`
Sampling temperature. Lower is more deterministic.
### `top_p` — number, default `0.95`
Nucleus sampling cutoff. Tune either `temperature` or `top_p`, not both.
### `tools` / `tool_choice` — optional
Function declarations for tool calling, and the strategy for picking among
them. See [Tools and grounding](#tools-and-grounding).
### `group` — string, default `"default"`
Token group on the gateway. Leave it at `default` unless your account has been
given another group to route through.
***
## Multimodal input
Google documents the following supported inputs for this model:
| Modality | Supported |
| -------- | :-------: |
| Text | ✅ |
| Image | ✅ |
| Video | ✅ |
| Audio | ✅ |
| PDF | ✅ |
Output is **text only**. Audio generation, image generation and the Live API are
**not** supported on this model — use a dedicated model for those.
PDF being a first-class input matters for document workloads: a contract set or
a research bundle goes in whole, without a chunking layer of your own.
***
## Thinking
Thinking is a supported capability. The reasoning pass is what carries a
multi-step plan across tool calls instead of restarting on each turn.
**Thinking tokens are billed as output tokens** and count toward
`max_tokens`. A long reasoning pass therefore competes with the visible answer
for the same budget — raise `max_tokens` when you ask for both deep reasoning
and long output.
***
## Tools and grounding
| Capability | Status |
| -------------------------- | ------------------- |
| Function calling | Supported |
| Code execution | Supported |
| File search | Supported |
| Structured outputs | Supported |
| Context caching | Supported |
| URL context | Supported |
| Search grounding | Supported |
| Grounding with Google Maps | Supported |
| Computer use | Supported (preview) |
Google meters grounded search queries separately from tokens, and a single
request may fan out into more than one search query.
***
## Pricing dimensions
Billing is **per token**, in USD, against your `api.reapi.ai` balance, with
separate **input** and **output** rates. Three things to keep in mind:
* **Thinking tokens bill as output.** They are the usual reason a bill exceeds
an estimate built from visible response length alone.
* **Output dominates.** For most generative workloads the output rate, not the
input rate, decides the invoice.
* **Grounded search is metered by Google separately** from tokens.
Both token rates sit **20% below Google's published Standard rate**. Current
numbers are on the [model page](https://reapi.ai/models/gemini-3-6-flash) and
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) — those tables are the
canonical source, not this page.
Chat models bill in USD on the gateway balance. They do **not** draw down the
integer credits used by the media-generation endpoints on `reapi.ai/api/v1`.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1785000000,
"model": "gemini-3.6-flash",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 812,
"completion_tokens": 240,
"total_tokens": 1052
}
}
```
`usage.completion_tokens` includes thinking tokens, so it is the number to
reconcile a bill against.
### Streaming (`stream: true`)
Server-sent events, each `data:` line carrying a `chat.completion.chunk` with
the incremental text in `choices[0].delta.content`, terminated by
`data: [DONE]`.
***
## Errors
Failures use the gateway's standard envelope. See the
[errors catalog](/docs/api/errors) for the full code list. Common cases:
| Trigger | What to do |
| ------------------------------------ | -------------------------------------------------- |
| Missing or invalid bearer token | Create a key in the api.reapi.ai console |
| Unknown `model` value | Send `gemini-3.6-flash` exactly, dots included |
| `max_tokens` above the model ceiling | Lower it; the documented ceiling is 65,536 |
| Insufficient balance | Top up on the gateway |
| Upstream rate limit | Retry with backoff, or route through another model |
***
## Tips
* **Stream anything interactive.** Time-to-first-token is what users perceive;
a non-streamed long answer reads as a hang.
* **Budget for thinking.** If responses truncate mid-sentence on hard prompts,
the reasoning pass consumed the `max_tokens` allowance — raise it.
* **Put the whole document in.** With a 1,048,576-token window, a retrieval
layer whose only purpose was working around a small context is usually
worth deleting.
* **Use structured outputs for tool arguments.** Schema-constrained output
removes a whole class of parse-and-retry logic.
* **Reach for grounding rather than a stale answer.** Search grounding and URL
context exist precisely for facts newer than the training data.
* **Pin the Stable string.** `gemini-3.6-flash` is Google's Stable channel id;
`latest`-style aliases can be hot-swapped underneath you.
***
## Related
* [Gemini 3.6 Flash model page](https://reapi.ai/models/gemini-3-6-flash) — current rates
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# gemini-3-pro-image-preview (https://reapi.ai/docs/gemini-3-pro-image-preview)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Async high-quality image generation. One endpoint covers
> text-to-image and image-to-image with up to 14 reference images.
> Two channels share the same parameter shape:
>
> * `gemini-3-pro-image-preview` — default channel.
> * `gemini-3-pro-image-preview-official` — Google's official endpoint.
* Async processing — `POST` returns a `task_id`, poll `GET /api/v1/tasks/{id}` for the result.
* standard `/api/v1/images/generations` envelope (text-to-image / image-to-image).
* 10 ratios via `size`; three resolution tiers (`1K` / `2K` / `4K`) via `resolution`.
* Up to **14** reference images via `image_urls` — public HTTP(S) URLs only.
* Up to **4** images per request via `n`.
* Failed / moderated requests are not charged.
***
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-3-pro-image-preview",
"prompt": "ancient castle beneath a starry sky, cinematic",
"size": "16:9",
"resolution": "2K"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "gemini-3-pro-image-preview",
"prompt": "ancient castle beneath a starry sky, cinematic",
"size": "16:9",
"resolution": "2K",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gemini-3-pro-image-preview",
prompt: "ancient castle beneath a starry sky, cinematic",
size: "16:9",
resolution: "2K",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gemini-3-pro-image-preview",
"prompt": "ancient castle beneath a starry sky, cinematic",
"size": "16:9",
"resolution": "2K",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-3-pro-image-preview",
"status": "processing",
"created_at": 1735000000
}
```
Hold onto `id` and poll `GET /api/v1/tasks/{id}` until completion.
***
## Endpoint
```http
POST /api/v1/images/generations # submit a job
GET /api/v1/tasks/{id} # poll for the result
```
Async — submit returns a `task_id` immediately; the actual image
arrives via the polling endpoint. Polling is free.
***
## Authentication
Bearer token, minted at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer rk_live_xxx
```
***
## Body
### `model`
`string` · **required**
One of:
* `"gemini-3-pro-image-preview"` — default channel.
* `"gemini-3-pro-image-preview-official"` — Google's official endpoint.
### `prompt`
`string` · **required**
Detailed prompts produce better results. English and Chinese both
supported. Don't restate the ratio in the prompt — pass it via `size`.
Image-to-image is no exception — the prompt describes what to do with
the reference. Sending `image_urls` without a `prompt` returns `400`.
### `size`
`string` · default `"1:1"` (text-to-image) / inherited (image-to-image)
Output ratio. One of these 10 values:
```
1:1 2:3 3:2 3:4 4:3
4:5 5:4 9:16 16:9 21:9
```
Anything outside the list is rejected with `400`.
When `image_urls` is provided **without** `size`, the output inherits the
first reference image's resolution (image-to-image mode). Pass `size`
to force a specific ratio.
### `resolution`
`string` · default `"1K"`
`1K` / `2K` / `4K`. Case-insensitive — `"2K"` and `"2k"` are
equivalent.
### `n`
`integer` · default `1`
1 – 4 images per request. Must be a number, not a string.
### `image_urls`
`string[]` · optional
Reference images for image-to-image. Up to **14** entries. Each entry
must be a public HTTP(S) URL — `data:` / base64 payloads are rejected
at the gateway. Upload to your own object storage (S3 / R2 / OSS) and
pass the URL.
***
## Use cases
### 1. Text-to-image (minimal)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3-pro-image-preview",
"prompt": "ancient castle beneath a starry sky, cinematic"
}'
```
### 2. Text-to-image at 2K
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3-pro-image-preview",
"prompt": "neon-lit Tokyo street in the rain, cyberpunk",
"size": "16:9",
"resolution": "2K"
}'
```
### 3. Text-to-image at 4K
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3-pro-image-preview",
"prompt": "cinematic mountain valley at golden hour",
"size": "21:9",
"resolution": "4K"
}'
```
### 4. Image-to-image (single reference)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3-pro-image-preview",
"prompt": "convert this snapshot into a film-grain editorial shot",
"image_urls": ["https://your-cdn.com/photo.jpg"],
"resolution": "2K"
}'
```
### 5. Multi-reference fusion
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3-pro-image-preview",
"prompt": "blend the subject from photo A with the lighting of photo B",
"size": "4:3",
"resolution": "2K",
"image_urls": [
"https://your-cdn.com/photo-a.jpg",
"https://your-cdn.com/photo-b.jpg"
]
}'
```
### 6. Force the official channel
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gemini-3-pro-image-preview-official",
"prompt": "marble bust of a philosopher, dramatic side lighting",
"size": "1:1",
"resolution": "2K"
}'
```
***
## Response
`POST` returns immediately with `status: "processing"`; poll
`GET /api/v1/tasks/{id}` until `status` is `completed` or `failed`.
Both responses share the same envelope — the `output` field is
`null` until the task finishes.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-3-pro-image-preview",
"status": "processing",
"created_at": 1735000000
}
```
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-3-pro-image-preview",
"status": "completed",
"created_at": 1735000000,
"output": {
"image_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png"]
},
"error": null
}
```
URLs are stable for **7 days**. Download to your own storage if you
need them longer.
```json
{
"error": {
"code": 20003,
"message": "gemini-3-pro-image-preview: invalid resolution \"8K\"",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 10003,
"message": "API key invalid",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 30001,
"message": "Insufficient credits",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 50001,
"message": "Rate limit exceeded",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 60099,
"message": "Internal error — please retry",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
### Upstream errors (502 / 503)
reAPI does not surface raw upstream `5xx` codes to the caller. When
the upstream provider returns `502 Bad Gateway`, `503 Service
Unavailable`, or a connection failure, the worker retries up to 5
attempts with exponential backoff (\~30s total budget). If all
retries fail, the task ends with one of these reapi-specific error
codes (returned via `GET /api/v1/tasks/{id}` as `error.code`):
| reapi code | Meaning | Origin |
| ---------- | -------------------------- | ------------------------------------ |
| `80001` | `provider_submit_failed` | Upstream `5xx` / network on submit |
| `80002` | `provider_polling_timeout` | Wall-clock cap reached while polling |
| `80003` | `provider_failed` | Upstream returned a terminal failure |
See [Errors catalog](/docs/api/errors) for the full code list.
***
## Polling
| `status` | Meaning |
| ------------ | ----------------------------- |
| `processing` | Submitted, still generating |
| `completed` | `output.image_urls` is ready |
| `failed` | See `error.code`; not charged |
Recommended cadence:
```
0–30s: wait before the first poll
30s–3m: poll every 3–5s
3m+: back off to 10s; cap at 30s
```
A 1K image typically completes in 30 – 60 seconds; 2K adds \~30s; 4K
can take 90 – 120 seconds.
***
## Related
* [`gemini-2.5-flash-image-preview`](/docs/gemini-2-5-flash-image-preview) — faster, cheaper variant (1K only).
* [`gemini-3.1-flash-image-preview`](/docs/gemini-3-1-flash-image-preview) — newer variant with extreme ratios and Google search grounding.
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Pricing — gemini-3-pro-image-preview](https://reapi.ai/models/gemini-3-pro-image-preview#pricing)
---
# gemini-omni (https://reapi.ai/docs/gemini-omni)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Gemini Omni — Google's any-to-any video model, exposed through reapi as
> a single async endpoint. Mode is implicit: the counts of `image_urls` /
> `video_urls` pick **text-to-video**, **image-to-video**,
> **three-image fusion**, or **reference-to-video**. 4 to 10 second outputs
> at 720p, 1080p, or 4K. Flat per-generation pricing across every mode — see
> the [model page](https://reapi.ai/models/gemini-omni).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-omni",
"prompt": "A kitten playing piano, slow camera push-in",
"duration": 6,
"resolution": "1080p",
"aspect_ratio": "16:9"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "gemini-omni",
"prompt": "A kitten playing piano, slow camera push-in",
"duration": 6,
"resolution": "1080p",
"aspect_ratio": "16:9",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gemini-omni",
prompt: "A kitten playing piano, slow camera push-in",
duration: 6,
resolution: "1080p",
aspect_ratio: "16:9",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gemini-omni",
"prompt": "A kitten playing piano, slow camera push-in",
"duration": 6,
"resolution": "1080p",
"aspect_ratio": "16:9",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-omni",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls`
holds the generated MP4 URL, valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Mode routing
`gemini-omni` picks its mode from the **counts of `image_urls` and
`video_urls`** you send — there is no `mode` parameter:
| `image_urls` | `video_urls` | Mode | What it does |
| ------------ | ------------ | ---------------------- | ------------------------------------------------------------------------ |
| `0` | `0` | **Text-to-video** | Generate from a prompt. |
| `1` | `0` | **Image-to-video** | Animate from a single starting frame. |
| `3` | `0` | **Three-image fusion** | Combine three references into one motion shot. |
| `0` / `1` | `1` | **Reference-to-video** | Reference a source clip (≤ 30s) for motion / style. Flat per generation. |
**Unsupported counts.**
* `image_urls = 2` is **rejected** with `400 image_urls cardinality 2 is
not supported`. Submit 0, 1, or 3 — there is no first/last-frame mode
on `gemini-omni`.
* `image_urls = 4` or more is also rejected.
* `video_urls = 2` or more is **rejected** with `400 video_urls accepts
at most 1 entry`.
* `duration` and `video_urls` cannot both be set. Reference-to-video mode
reads the source clip's length; passing `duration` alongside is
rejected with `400 duration and video_urls cannot both be set`.
***
## Request body
### `model` — required
`string`. Must be `"gemini-omni"`.
### `prompt` — string, required
Up to **2,000 characters**. Required in every mode (text-to-video,
image-to-video, three-image fusion, reference-to-video). Empty /
whitespace-only prompts are treated as missing.
**Failure modes.**
* Empty / missing → `400 prompt is required` (code `20002`).
* Longer than 2,000 chars → `400 prompt exceeds 2000 characters (got N)` (code `20007`).
### `duration` — integer, default `6`
One of `4`, `6`, `8`, `10` seconds. Other values are rejected with
`400 duration must be 4, 6, 8, or 10 seconds, got N`.
**Reference-to-video mode ignores `duration`.** When `video_urls` is set,
the vendor reads the source clip's length to drive the output, and
`duration` must be omitted. Passing both is rejected with
`400 duration and video_urls cannot both be set`.
### `resolution` — string, default `"720p"`
`720p` / `1080p` / `4k`. Lowercase is canonical; uppercase forms (`"4K"`)
are accepted and normalized. Drives the per-generation rate — 720p and
1080p share the same price; only 4K is uplifted.
### `aspect_ratio` — string, default `"16:9"`
Output framing. One of:
| Value | Shape |
| ------ | --------- |
| `16:9` | Landscape |
| `9:16` | Portrait |
Unknown ratios are rejected with `400 invalid aspect_ratio`.
### `size` — string, alias for `aspect_ratio`
The same value the supplier doc lists as a separate field. If both are
sent, they must match; otherwise `400 aspect_ratio and size disagree`.
The reapi playground does not surface `size`; the JSON body still accepts
it for parity.
### `image_urls` — string\[]
Array of public HTTP(S) URLs. **Allowed counts: 0, 1, or 3**.
* **0 entries** — text-to-video.
* **1 entry** — image-to-video; the image is treated as the starting frame.
* **3 entries** — three-image fusion. The model combines all three
references into one motion shot.
### `video_urls` — string\[]
Array of public HTTP(S) URLs. **Allowed counts: 0 or 1**.
* **0 entries** — non-reference modes (text / image / fusion).
* **1 entry** — reference-to-video. The source clip drives the output; it
must be **≤ 30 seconds** (longer is rejected with `400`), and the first
≤ 10 seconds are used as the reference. Billed at a **flat per-generation
rate** (not per second). `duration` MUST be omitted in this mode.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — every
URL field on this endpoint must be a public HTTP(S) URL. Upload to your
own object storage (S3, R2, OSS, …) and pass the URL.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in
over time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gemini-omni",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Always `"gemini-omni"` (echo of the submitted `model`) |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
`output.video_urls` URLs are valid for **7 days**. Re-host to your own
storage if you need them longer.
***
## Validation errors
All cases below return HTTP 400 with code `20003` unless noted. Pattern-match
on `code`, not `message` — message strings carry request-specific context
(field names, observed values, etc.) and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| -------------------------------------------------- | ------- | -------------------------------------------------------------------------------- |
| `prompt` missing or blank | `20002` | `gemini-omni: prompt is required` |
| `prompt` longer than 2,000 chars | `20007` | `gemini-omni: prompt exceeds 2000 characters (got N)` |
| `image_urls` length `2` | `20003` | `gemini-omni: image_urls cardinality 2 is not supported` |
| `image_urls` length > `3` | `20003` | `gemini-omni: image_urls accepts at most 3 entries, got N` |
| `duration` not one of 4/6/8/10 | `20003` | `gemini-omni: duration must be 4, 6, 8, or 10 seconds, got N` |
| Unknown `resolution` | `20003` | `gemini-omni: invalid resolution "X" (allowed: 720p / 1080p / 4k)` |
| Unknown `aspect_ratio` | `20003` | `gemini-omni: invalid aspect_ratio "X" (allowed: 16:9 / 9:16)` |
| `aspect_ratio` and `size` disagree | `20003` | `gemini-omni: aspect_ratio "X" and size "Y" disagree` |
| `image_urls` carrying a `data:` URI or non-http(s) | `20003` | `gemini-omni: image_urls entries must be public http(s) URLs` |
| `video_urls` source clip longer than 30 seconds | `20003` | `Reference video must be at most 30s (got Ns); trim the clip before submitting.` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` —
see [Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Text-to-video — minimum request
```json
{
"model": "gemini-omni",
"prompt": "A little girl walking down a sunset coastal road"
}
```
### Text-to-video — full parameters
```json
{
"model": "gemini-omni",
"prompt": "A kitten playing piano, slow camera push-in, cinematic warm tones",
"duration": 8,
"resolution": "1080p",
"aspect_ratio": "16:9"
}
```
### Image-to-video — animate a single frame
```json
{
"model": "gemini-omni",
"prompt": "Bring the scene to life with a gentle camera dolly forward",
"image_urls": ["https://your-cdn.com/first_frame.jpg"],
"duration": 6,
"resolution": "1080p"
}
```
### Three-image fusion
```json
{
"model": "gemini-omni",
"prompt": "Compose a 10-second product spot mixing scene, character, and product",
"image_urls": [
"https://your-cdn.com/scene.jpg",
"https://your-cdn.com/character.jpg",
"https://your-cdn.com/product.jpg"
],
"duration": 10,
"resolution": "1080p",
"aspect_ratio": "9:16"
}
```
### 4K hero shot
```json
{
"model": "gemini-omni",
"prompt": "A neon city street in the rain, slow camera pan, reflections on the asphalt",
"duration": 4,
"resolution": "4k",
"aspect_ratio": "16:9"
}
```
### Reference-to-video — match a source clip
```json
{
"model": "gemini-omni",
"prompt": "the same scene but at night with neon lights",
"resolution": "720p",
"aspect_ratio": "16:9",
"video_urls": ["https://your-cdn.com/source.mp4"]
}
```
The output tracks the source clip (which must be **≤ 30s**; the first ≤ 10s
are used as the reference); **omit `duration`**. Billing is a flat
per-generation rate (see [Pricing](#pricing) below).
***
## Choosing a mode
| Need | Send |
| ---------------------------------------- | ------------------------------------------------- |
| Generate from text | `prompt` only |
| Animate a still | `prompt + image_urls` (1 entry) |
| Compose scene + character + product | `prompt + image_urls` (3 entries) |
| Match an existing clip's motion / length | `prompt + video_urls` (1 entry, no `duration`) |
| Cut spend | Drop `resolution` to `720p` and `duration` to `4` |
| Hero shot | Pick `4k` at `4-10s` |
***
## Polling pattern
The task endpoint behaves identically to other video tasks — the only
difference is the completed `output` shape (`video_urls` instead of
`image_urls`). A pragmatic schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in a few minutes. The worker's wall-clock cap
is **48 hours**, comfortably above any realistic queue.
***
## Pricing
Two billing modes — picked by request shape.
### Per generation — text / image / fusion
Charged once per submitted job. 720p and 1080p share the same rate at
every duration; only 4K is uplifted. Duration tiers are `4s` / `6s` /
`8s` / `10s`. See current per-tier rates on the
[Gemini Omni model page](https://reapi.ai/models/gemini-omni).
### Flat per generation — reference-to-video
Triggered when `video_urls` is set. Charged once per job at a flat rate by
resolution — independent of clip length. 720p and 1080p share the same
rate; 4K is uplifted. The source clip must be **≤ 30 seconds**: the gateway
probes its decoded length server-side and rejects longer clips with `400`;
the first ≤ 10 seconds are used as the reference. See current rates on the
[Gemini Omni model page](https://reapi.ai/models/gemini-omni).
### Bill formula
`1 credit = $0.001`. Integer credits = `ceil(usd × 1000)`. Failed jobs
refund automatically.
***
## Tips
* **Prompt motion, not just scene.** "Slow push-in, warm tones, shallow
depth of field" outperforms a pure noun-list of what's on screen.
* **Pick 720p first if you're iterating.** It's the same per-generation
price as 1080p, but renders faster and lets you change your mind on
the final tier without re-doing the bill math.
* **Three-image fusion needs cohesive references.** Pick three images
that share lighting and composition cues — the model fuses them
more cleanly than three random shots.
* **Pick 4K only when shipping.** A 4K render is roughly 2× the cost
of 720p / 1080p; reserve it for the final keeper.
* **Keep reference clips ≤ 30s.** Reference-to-video requires a source clip
of **≤ 30 seconds** (the server probes its length via ffmpeg and rejects
anything longer with `400`); only the first ≤ 10 seconds are used as the
reference. Billing is a flat per-generation rate regardless of length.
Omit `duration` — the server rejects requests that pass both.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# glm-5.2 (https://reapi.ai/docs/glm-5-2)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Z.AI's GLM-5.2 — a flagship foundation model built for long-horizon tasks —
> exposed through api.reapi.ai as a drop-in OpenAI-compatible Chat Completions
> endpoint. A **1M-token** context window Z.AI describes as *solid and lossless*,
> **128K** max output, thinking **on by default but switchable off**, a
> `reasoning_effort` dial that no other model in the GLM family accepts, genuinely
> tunable `temperature` / `top_p`, function calling with streamed tool arguments,
> JSON mode and context caching. Text in, text out. The wire `model` id is
> **`glm-5.2`** — with the dot. Current rates live on the
> [model page](https://reapi.ai/models/glm-5-2) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
**The URL and the model id differ.** This page and the model page spell the slug
`glm-5-2` because a URL cannot carry the dot. The string your request body needs
is `glm-5.2`. Sending `glm-5-2` as the model is the first thing to check on an
unknown-model error.
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "glm-5.2",
"messages": [
{ "role": "user", "content": "Refactor this module and add tests." }
],
"thinking": { "type": "enabled" },
"reasoning_effort": "max",
"max_tokens": 8192,
"stream": true
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="glm-5.2",
messages=[{"role": "user", "content": "Refactor this module and add tests."}],
max_tokens=8192,
stream=True,
extra_body={
"thinking": {"type": "enabled"},
"reasoning_effort": "max",
},
)
for chunk in stream:
delta = chunk.choices[0].delta
reasoning = getattr(delta, "reasoning_content", None)
if reasoning:
print(reasoning, end="")
if delta.content:
print(delta.content, end="")
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "glm-5.2",
messages: [{ role: "user", content: "Refactor this module and add tests." }],
max_tokens: 8192,
stream: true,
// @ts-expect-error vendor-specific fields pass through
thinking: { type: "enabled" },
reasoning_effort: "max",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "glm-5.2",
"messages": []map[string]string{
{"role": "user", "content": "Refactor this module and add tests."},
},
"thinking": map[string]string{"type": "enabled"},
"reasoning_effort": "max",
"max_tokens": 8192,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Chat models are served from the `api.reapi.ai` gateway, which has its own console
and its own key. Create the key there, then send it as a bearer token:
```http
Authorization: Bearer YOUR_API_KEY
```
The gateway key is **not** the same credential as a media-generation key used
against `reapi.ai/api/v1`. Sign in at
[api.reapi.ai](https://api.reapi.ai/) to create one.
***
## Endpoint
```http
POST /v1/chat/completions
```
Base URL `https://api.reapi.ai`. The wire format is OpenAI-compatible, so the
same SDKs (`openai-python`, `openai-node`, `openai-go`, …) work once you swap the
base URL, the key and the model string. Vendor-specific fields such as `thinking`
go through the SDK's pass-through mechanism — `extra_body` in Python.
***
## Request body
### `model` — string, required
Must be `glm-5.2` exactly, dot included.
### `messages` — array, required
Conversation history, each entry an object with `role` and `content`. Roles are
`system`, `user`, `assistant` and `tool`. **Text only** — see
[Modality](#modality).
### `max_tokens` — integer
Upper bound on output tokens for this response. This model supports a **128K**
maximum output and the schema accepts values up to **131,072**. Reasoning is
generated before the answer and draws on the same allowance.
### `stream` — boolean, default `false`
When `true`, tokens arrive as server-sent events terminated by `data: [DONE]`.
Reasoning and answer arrive as **separate deltas** — `reasoning_content` and
`content`.
### `thinking` — object, default `{"type": "enabled"}`
Chain-of-thought control. See [Reasoning](#reasoning).
### `reasoning_effort` — string, default `"max"`
How hard the model thinks; applies only while thinking is enabled. See
[Reasoning](#reasoning) for the value-collapsing behaviour, which is the one
surprise on this model.
### `temperature` / `top_p` — numbers
Actually tunable here. See [Sampling parameters](#sampling-parameters).
### `tools` / `tool_choice` / `tool_stream` — optional
Function definitions, selection strategy and streamed arguments. See
[Tool calling](#tool-calling).
### `response_format` — object, optional
`{"type": "text"}` (default) or `{"type": "json_object"}`. See
[Structured output](#structured-output).
### `stop` — array, optional
Stop words. **Only one is supported** despite the array shape.
### `group` — string, default `"default"`
Token group on the gateway. Leave it at `default` unless your account has been
given another group to route through.
***
## Reasoning
Two things are unusual here, and both cut in the developer's favour.
**1. Thinking is on by default, and can be turned off.** `thinking` defaults to
`{"type": "enabled"}`, and `{"type": "disabled"}` is accepted — a switch several
models in this class no longer offer.
**2. With thinking on, the model decides per request whether to think.** Z.AI
contrasts this explicitly with the previous generation, which reasoned
unconditionally. Leaving thinking enabled therefore does not mean paying for
reasoning on every trivial turn.
### The effort ladder collapses
`reasoning_effort` accepts seven values for cross-provider compatibility, but they
map onto **three** behaviours:
| You send | What actually happens |
| --------- | --------------------------------------------- |
| `max` | **Default.** Deep reasoning. |
| `xhigh` | Mapped to `max`. |
| `high` | Enhanced reasoning — the real middle setting. |
| `medium` | Mapped to `high`. |
| `low` | Mapped to `high`. |
| `minimal` | Skips thinking. |
| `none` | Skips thinking. |
**A middling value does not buy a middling cost.** `low` and `medium` both behave
as `high`, so a client that drops to `low` expecting a cheap pass gets the same
work as `high`. The only ways down are `high`, or skipping thinking entirely with
`minimal` / `none`.
### Prior-turn reasoning is dropped by default
`thinking.clear_thinking` defaults to **`true`**: the model ignores
`reasoning_content` from earlier turns and keeps only visible text, tool calls and
tool results. That keeps multi-turn context short and cheap, and it is why this
model needs no special history handling out of the box.
Set it to `false` for Preserved Thinking — and then the full, unmodified,
correctly ordered `reasoning_content` history must be forwarded. Missing,
truncated, rewritten or reordered blocks degrade the result.
***
## Sampling parameters
Unlike several open-weight peers that pin these, both work:
| Parameter | Range | Default |
| ------------- | ----------------- | ------- |
| `temperature` | `0.0` – **`1.0`** | `1.0` |
| `top_p` | `0.01` – `1.0` | `0.95` |
**`temperature` is capped at 1.0.** Code carried over from a provider that allows
up to 2.0 will be rejected. Z.AI also advises tuning **one** of the two rather
than both — `temperature` for creative latitude, `top_p` for convergence.
A separate `do_sample` flag defaults to `true`; setting it `false` makes both
sampling parameters stop taking effect.
***
## Tool calling
* `tools` accepts up to **128** functions.
* `tool_choice` accepts **`auto` only** — the model decides when a call is
warranted. There is no `required` or per-function forcing on this model.
* `tool_stream: true` streams tool-call arguments as they are generated instead
of delivering the complete call at once. Concatenate
`delta.tool_calls[*].function.arguments` across chunks.
After executing calls, append one `tool` message per call with the matching
`tool_call_id` before asking for the next completion.
***
## Structured output
```json
{ "response_format": { "type": "json_object" } }
```
This is **JSON mode, not JSON Schema** — you do not supply a schema and the model
does not validate against one. Ask for the shape you want in the prompt as well,
which Z.AI recommends explicitly. If you need schema-guaranteed fields, validate
on your side after parsing.
***
## Modality
| Modality | Supported |
| -------- | :-------: |
| Text in | ✅ |
| Text out | ✅ |
| Image in | ❌ |
| Video in | ❌ |
Z.AI's model card lists input and output modalities for this model as **text**.
Vision is a different model in their lineup. If a step in your workflow needs a
screenshot read, route that step elsewhere — this model is a strong choice for the
code around it, not for looking at it.
***
## Context caching
Caching is part of this model's capability set and rewards the obvious shape: keep
a long stable prefix — system prompt, knowledge document, tool definitions — in
front of a changing question, and let repeat requests reuse it. No cache id and no
TTL to manage.
Z.AI prices cached input as its own dimension. **reAPI does not publish a separate
cached-input rate for this model**, so treat caching as a latency and
context-length benefit here, and bill against the input and output rates in the
pricing table.
***
## Pricing dimensions
Billing is **per token**, in USD, against your `api.reapi.ai` balance, with
separate **input** and **output** rates. Three things to keep in mind:
* **Reasoning tokens bill as output.** Thinking is on by default at the deepest
effort setting, which is the usual reason a bill exceeds an estimate built from
visible answer length.
* **Effort is the first lever.** Because the ladder collapses, the meaningful
choices are deep (`max`), enhanced (`high`) or none at all.
* **Output dominates.** For most generative workloads the output rate decides the
invoice.
Both token rates sit at **about a third below Z.AI's published per-token rate**.
Current numbers are on the [model page](https://reapi.ai/models/glm-5-2) and
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) — those tables are the
canonical source, not this page.
Chat models bill in USD on the gateway balance. They do **not** draw down the
integer credits used by the media-generation endpoints on `reapi.ai/api/v1`.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1785000000,
"model": "glm-5.2",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"reasoning_content": "...",
"content": "..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1204,
"completion_tokens": 860,
"total_tokens": 2064
}
}
```
`reasoning_content` is a sibling of `content`. Unless you have turned off
`clear_thinking`, you do not need to send it back.
### Streaming (`stream: true`)
Server-sent events, each `data:` line carrying a `chat.completion.chunk`.
Reasoning arrives in `choices[0].delta.reasoning_content` and the answer in
`choices[0].delta.content`, terminated by `data: [DONE]`.
***
## Errors
Failures use the gateway's standard envelope. See the
[errors catalog](/docs/api/errors) for the full code list. Common cases:
| Trigger | What to do |
| --------------------------------------------------- | ----------------------------------------------------- |
| Missing or invalid bearer token | Create a key in the api.reapi.ai console |
| Unknown `model` value | Send `glm-5.2` with the dot — not the hyphenated slug |
| `temperature` above `1.0` | Lower it; the ceiling is 1.0 on this model |
| `tool_choice` set to `required` or a named function | Only `auto` is supported |
| An image part in `messages` | Not a multimodal model — route vision elsewhere |
| More than one entry in `stop` | Only one stop word is supported |
| `max_tokens` above the ceiling | Lower it; the documented ceiling is 131,072 |
| `reasoning_effort` sent with thinking disabled | It only takes effect while thinking is enabled |
| Insufficient balance | Top up on the gateway |
| Upstream rate limit | Retry with backoff, or route through another model |
***
## Tips
* **Send the whole project, then ask for the audit first.** Z.AI's own
recommended opening move is an architecture map, module responsibilities, API
contracts, data flows and technical debt — before any edit.
* **Bound long tasks explicitly.** State what must not change (business logic, API
signatures, runtime behaviour) and ask for the plan, impact scope, risk
boundaries and verification method up front.
* **Give it your real engineering standards.** Lint rules, build commands, test
requirements, commit conventions and prohibited actions belong in the prompt;
adherence under long context is what this generation improved.
* **Pick an effort behaviour, not a number.** Deep, enhanced, or none — the values
in between are aliases.
* **Mind the `temperature` ceiling** of 1.0, and tune one sampling parameter
rather than both.
* **Stream anything interactive**, and render `reasoning_content` separately from
the answer rather than concatenating them.
* **Front-load the stable bulk** of your prompt so caching can do its job.
***
## Related
* [GLM-5.2 model page](https://reapi.ai/models/glm-5-2) — current rates
* [Kimi K3](/docs/kimi-k3) — the other open-weight 1M-context coding flagship
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# gpt-5.4 (https://reapi.ai/docs/gpt-5-4)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> GPT-5.4 is OpenAI's established reasoning model, exposed through
> api.reapi.ai as a drop-in OpenAI-compatible Chat Completions endpoint.
> 1M token context, 128K max output, function calling, and JSON-mode
> responses. The cost-efficient route in the GPT family — current rates
> live on the [model page](https://reapi.ai/models/gpt-5-4) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"temperature": 0.7,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="gpt-5.4",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
temperature=0.7,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "gpt-5.4",
messages: [{ role: "user", content: "Hello" }],
stream: true,
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
// `group` is an api.reapi.ai-specific extension; pass it via extra body.
// @ts-expect-error — not part of the OpenAI types
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gpt-5.4",
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"temperature": 0.7,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The GPT-5.4 chat workspace lives on
the `api.reapi.ai` platform — sign in there to create a key and top up
tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Endpoint
```http
POST https://api.reapi.ai/v1/chat/completions
```
OpenAI-compatible. The same SDKs (`openai-python`, `openai-node`,
`openai-go`, …) work once the base URL is set to
`https://api.reapi.ai/v1`.
***
## Request body
### `model` — string, required
Must be `"gpt-5.4"`. The value is echoed back in the response envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string or content-parts array"
}
```
Multi-turn history is sent in chronological order — the last message is
the one the model responds to.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the
OpenAI format, terminated by a `data: [DONE]` line. When `false`, the
full response body is returned in one HTTP response.
### `temperature` — number, default `1`
Range `0.0` – `2.0`. Sampling temperature. Lower values make output more
deterministic; higher values increase randomness. OpenAI recommends
tuning either `temperature` or `top_p`, not both.
### `top_p` — number, default `1`
Range `0.0` – `1.0`. Nucleus sampling cutoff — restricts sampling to the
smallest set of tokens whose cumulative probability mass exceeds
`top_p`.
### `frequency_penalty` — number, default `0`
Range `-2.0` – `2.0`. Penalises tokens by how often they've already
appeared in the response so far. Positive values discourage literal
repetition.
### `presence_penalty` — number, default `0`
Range `-2.0` – `2.0`. Penalises tokens that have appeared at all,
regardless of frequency. Positive values encourage the model to talk
about new topics.
### `group` — string, default `"default"`
api.reapi.ai-specific extension. Selects a token group on the gateway,
which routes the request to a specific upstream channel pool.
`"default"` is the standard pool and covers nearly every workload —
omit the field if you don't need custom routing.
### Other OpenAI parameters
Every other field on the OpenAI Chat Completions spec — `max_tokens`,
`stop`, `n`, `seed`, `tools`, `tool_choice`, `response_format`,
`logprobs`, `top_logprobs`, `user`, `parallel_tool_calls` — passes
through unchanged. The OpenAI SDKs do not need a reAPI-specific shim.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1735000000,
"model": "gpt-5.4",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21
}
}
```
`usage.prompt_tokens` and `usage.completion_tokens` are the inputs to
the bill — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing) for
the live rate card.
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta:
```
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735000000,"model":"gpt-5.4","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735000000,"model":"gpt-5.4","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735000000,"model":"gpt-5.4","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
The final event before `[DONE]` carries the `finish_reason`
(`stop` / `length` / `tool_calls` / `content_filter`). Usage stats are
omitted from the stream — call again with `stream: false` if you need
exact token counts per turn.
***
## Pricing
GPT-5.4 is billed **pay-as-you-go in USD** against your api.reapi.ai
token balance. Current rates live on the model page and on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing); the table at the
top of the [model page](https://reapi.ai/models/gpt-5-4) is the
authoritative number for what you'll be charged today.
Long-context billing: when the input portion of a single request
exceeds **272K tokens**, the whole call is billed at **2× input and 1.5×
output**. Requests at or below 272K input use the standard rate. Failed
requests are not charged.
***
## Limits
| Limit | Value |
| ------------------- | ------------- |
| Context window | 1M tokens |
| Max output per call | 128K tokens |
| Standard-rate input | ≤ 272K tokens |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape — HTTP status, plus a JSON
body:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
Common cases:
| Status | When | Notes |
| ------ | -------------------------------------- | -------------------------------------------- |
| `400` | Bad request shape, unknown field, etc. | Same shape OpenAI returns |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you,
that's a one-for-one wire failure and a retry from your side is safe;
the upstream provider may have already produced output, but the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "gpt-5.4",
"messages": [
{ "role": "user", "content": "Summarise the OpenAI Chat Completions spec in three sentences." }
]
}
```
### Full parameter set
```json
{
"model": "gpt-5.4",
"group": "default",
"messages": [
{ "role": "system", "content": "You are a senior staff engineer." },
{ "role": "user", "content": "Walk me through a 1M-token codebase review strategy." }
],
"stream": true,
"temperature": 0.7,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0
}
```
### Tool use (function calling)
```json
{
"model": "gpt-5.4",
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo today?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### JSON mode
```json
{
"model": "gpt-5.4",
"response_format": { "type": "json_object" },
"messages": [
{ "role": "system", "content": "Return a JSON object with keys: title, summary, tags." },
{ "role": "user", "content": "Article: ..." }
]
}
```
***
## When to pick GPT-5.4 over GPT-5.5
GPT-5.4 and GPT-5.5 share the same endpoint, the same context window,
and the same OpenAI-compatible wire format — switching between them is
a one-line change in the `model` field. Pick GPT-5.4 when:
* **High-volume production traffic** where per-call cost dominates the
decision.
* **Established workflows** (classification, extraction, summarisation,
routine support replies) that don't benefit from GPT-5.5's newest
reasoning gains or Tool Search.
* **Latency-sensitive surfaces** where a slightly less expensive model
also tends to respond faster on identical inputs.
Pick GPT-5.5 when you need its strongest reasoning, Tool Search for
large agent ecosystems, or `reasoning_effort` controls.
***
## Tips
* **Stream by default for chat UX.** Streaming responses cut perceived
latency dramatically and let your UI render tokens as they're produced.
* **Watch the long-context boundary.** Splitting a 300K-token prompt
into a 270K turn and a follow-up keeps you on the standard rate
rather than the long-context tier.
* **Tune `temperature` *or* `top_p`, not both.** Mixing them tends to
produce results that are hard to reason about.
* **Use `response_format: { type: "json_object" }` for structured
output.** Much more reliable than parsing free-text JSON that the
model writes inside backticks.
* **Drop `frequency_penalty` and `presence_penalty` first when
debugging weird output.** Non-zero values can introduce artefacts
that look like model bugs.
***
## Related
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# gpt-5.5 (https://reapi.ai/docs/gpt-5-5)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> GPT-5.5 is OpenAI's frontier reasoning model, exposed through reAPI as a
> drop-in OpenAI-compatible Chat Completions endpoint. 1M token context,
> 128K max output, advanced reasoning with adjustable effort, and Tool
> Search for large agent workflows. Current rates live on the
> [model page](https://reapi.ai/models/gpt-5-5) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.5",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"temperature": 0.7,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="gpt-5.5",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
temperature=0.7,
top_p=1,
frequency_penalty=0,
presence_penalty=0,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "gpt-5.5",
messages: [{ role: "user", content: "Hello" }],
stream: true,
temperature: 0.7,
top_p: 1,
frequency_penalty: 0,
presence_penalty: 0,
// `group` is a reAPI-specific extension; pass it via extra body.
// @ts-expect-error — not part of the OpenAI types
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gpt-5.5",
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"temperature": 0.7,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The GPT-5.5 chat workspace lives on
the `api.reapi.ai` platform — sign in there to create a key and top up
tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Endpoint
```http
POST https://api.reapi.ai/v1/chat/completions
```
OpenAI-compatible. The same SDKs (`openai-python`, `openai-node`,
`openai-go`, …) work once the base URL is set to
`https://api.reapi.ai/v1`.
***
## Request body
### `model` — string, required
Must be `"gpt-5.5"`. The value is echoed back in the response envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string or content-parts array"
}
```
Multi-turn history is sent in chronological order — the last message is
the one the model responds to.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the
OpenAI format, terminated by a `data: [DONE]` line. When `false`, the
full response body is returned in one HTTP response.
### `temperature` — number, default `1`
Range `0.0` – `2.0`. Sampling temperature. Lower values make output more
deterministic; higher values increase randomness. OpenAI recommends
tuning either `temperature` or `top_p`, not both.
### `top_p` — number, default `1`
Range `0.0` – `1.0`. Nucleus sampling cutoff — restricts sampling to the
smallest set of tokens whose cumulative probability mass exceeds
`top_p`.
### `frequency_penalty` — number, default `0`
Range `-2.0` – `2.0`. Penalises tokens by how often they've already
appeared in the response so far. Positive values discourage literal
repetition.
### `presence_penalty` — number, default `0`
Range `-2.0` – `2.0`. Penalises tokens that have appeared at all,
regardless of frequency. Positive values encourage the model to talk
about new topics.
### `group` — string, default `"default"`
reAPI-specific extension. Selects a token group on the gateway, which
routes the request to a specific upstream channel pool. `"default"` is
the standard pool and covers nearly every workload — you can omit the
field if you don't need custom routing.
### Other OpenAI parameters
Every other field on the OpenAI Chat Completions spec — `max_tokens`,
`stop`, `n`, `seed`, `tools`, `tool_choice`, `response_format`,
`logprobs`, `top_logprobs`, `user`, `parallel_tool_calls`,
`reasoning_effort` (`none` / `low` / `medium` / `high` / `xhigh`) —
passes through unchanged. The OpenAI SDKs do not need a reAPI-specific
shim.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1735000000,
"model": "gpt-5.5",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21
}
}
```
`usage.prompt_tokens` and `usage.completion_tokens` are the inputs to
the bill — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing) for
the live rate card.
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta:
```
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735000000,"model":"gpt-5.5","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735000000,"model":"gpt-5.5","choices":[{"index":0,"delta":{"content":"!"},"finish_reason":null}]}
data: {"id":"chatcmpl-…","object":"chat.completion.chunk","created":1735000000,"model":"gpt-5.5","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
The final event before `[DONE]` carries the `finish_reason`
(`stop` / `length` / `tool_calls` / `content_filter`). Usage stats are
omitted from the stream — call again with `stream: false` if you need
exact token counts per turn.
***
## Pricing
GPT-5.5 is billed **pay-as-you-go in USD** against your api.reapi.ai
token balance. The live per-1M-token rate card lives on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing); top up tokens at
[api.reapi.ai](https://api.reapi.ai/).
The bill for a single call is:
```
input_cost = prompt_tokens × input_rate / 1,000,000
output_cost = completion_tokens × output_rate / 1,000,000
```
Failed requests are not charged.
### Long-context tier (>272K input tokens)
When the input portion of a single request exceeds 272K tokens, the
entire request is billed at **2× the input rate** and **1.5× the output
rate**. A request with 270K input tokens stays at the standard rate; a
request with 280K input tokens shifts the whole call (input *and*
output) to the long-context rate. See
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) for the resolved
per-1M-token numbers in both tiers.
***
## Limits
| Limit | Value |
| ------------------- | ------------- |
| Context window | 1M tokens |
| Max output per call | 128K tokens |
| Standard-rate input | ≤ 272K tokens |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape — HTTP status, plus a JSON
body:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
Common cases:
| Status | When | Notes |
| ------ | -------------------------------------- | -------------------------------------------- |
| `400` | Bad request shape, unknown field, etc. | Same shape OpenAI returns |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you,
that's a one-for-one wire failure and a retry from your side is safe;
the upstream provider may have already produced output, but the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "Summarise the OpenAI Chat Completions spec in three sentences." }
]
}
```
### Full parameter set
```json
{
"model": "gpt-5.5",
"group": "default",
"messages": [
{ "role": "system", "content": "You are a senior staff engineer." },
{ "role": "user", "content": "Walk me through a 1M-token codebase review strategy." }
],
"stream": true,
"temperature": 0.7,
"top_p": 1,
"frequency_penalty": 0,
"presence_penalty": 0
}
```
### Tool use (function calling)
```json
{
"model": "gpt-5.5",
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo today?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### Reasoning effort
```json
{
"model": "gpt-5.5",
"reasoning_effort": "high",
"messages": [
{ "role": "user", "content": "Prove that the sum of the first n odd numbers is n^2." }
]
}
```
`reasoning_effort` accepts `none` / `low` / `medium` / `high` / `xhigh`
— pick the lowest level that still produces correct output for the
workload to keep latency and token spend down.
***
## Tips
* **Stream by default for chat UX.** Streaming responses cut perceived
latency dramatically and let your UI render tokens as they're produced.
* **Watch the long-context boundary.** Splitting a 300K-token prompt
into a 270K turn and a follow-up keeps you on the standard rate
rather than paying the 2× / 1.5× long-context premium.
* **Tune `temperature` *or* `top_p`, not both.** Mixing them tends to
produce results that are hard to reason about.
* **`reasoning_effort: high` is the right default for agents.** Reserve
`xhigh` for the genuinely hard turns — it adds latency and token spend.
* **Drop `frequency_penalty` and `presence_penalty` first when debugging
weird output.** Non-zero values can introduce artefacts that look
like model bugs.
***
## Related
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# gpt-5.6-luna (https://reapi.ai/docs/gpt-5-6-luna)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> OpenAI's GPT-5.6 Luna — the tier optimised for cost-sensitive, high-volume workloads — exposed through api.reapi.ai as a drop-in
> OpenAI-compatible Chat Completions endpoint. A **1,050,000-token** context
> window, **128,000** max output tokens, six `reasoning_effort` rungs from
> `none` to `max` defaulting to **`medium`**, text and image input, and
> functions, web search, file search and computer use. OpenAI rates its reasoning **High** and its speed Fast. The wire
> `model` id is **`gpt-5.6-luna`**. Current rates live on the
> [model page](https://reapi.ai/models/gpt-5-6-luna) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
**The URL and the model id differ.** This page and the model page spell the slug
`gpt-5-6-luna` because a URL cannot carry dots. The string your request body needs is
`gpt-5.6-luna`.
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-luna",
"messages": [
{ "role": "user", "content": "Classify this ticket. Return only the category id." }
],
"reasoning_effort": "none",
"max_completion_tokens": 4096,
"stream": true
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="gpt-5.6-luna",
messages=[{"role": "user", "content": "Classify this ticket. Return only the category id."}],
reasoning_effort="none",
max_completion_tokens=4096,
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="")
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "gpt-5.6-luna",
messages: [{ role: "user", content: "Classify this ticket. Return only the category id." }],
reasoning_effort: "none",
max_completion_tokens: 4096,
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gpt-5.6-luna",
"messages": []map[string]string{
{"role": "user", "content": "Classify this ticket. Return only the category id."},
},
"reasoning_effort": "none",
"max_completion_tokens": 4096,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Chat models are served from the `api.reapi.ai` gateway, which has its own console
and its own key. Create the key there, then send it as a bearer token:
```http
Authorization: Bearer YOUR_API_KEY
```
The gateway key is **not** the same credential as a media-generation key used
against `reapi.ai/api/v1`. Sign in at
[api.reapi.ai](https://api.reapi.ai/) to create one.
***
## Endpoint
```http
POST /v1/chat/completions
```
Base URL `https://api.reapi.ai`. The wire format is OpenAI-compatible, so the
same SDKs work once you swap the base URL, the key and the model string.
OpenAI states that its reasoning models "work better with the Responses API",
and that "while the Chat Completions API is still supported, you'll get improved
model intelligence and performance by using Responses." The GPT-5.6 release entry
lists `v1/responses`, `v1/chat/completions` and `v1/batch` as this family's
endpoints — so Chat Completions is a documented, supported surface, and this is a
vendor preference rather than a restriction.
***
## Request body
### `model` — string, required
Must be `gpt-5.6-luna` exactly. There is no alias for this tier — the bare `gpt-5.6` id routes to the frontier model instead.
### `messages` — array, required
Conversation history, each entry an object with `role` and `content`. Roles are
`system`, `user`, `assistant` and `tool`. Text and image parts are supported —
see [Image input](#image-input).
### `max_completion_tokens` — integer
Upper bound on generated tokens for this response, **reasoning tokens included**.
The documented maximum output is **128,000** tokens.
Reasoning tokens bill as **output** tokens and occupy the context window. A
budget sized only for the visible answer can truncate on a harder prompt, and it
is the usual reason an invoice exceeds an estimate built from answer length.
### `reasoning_effort` — string, default `"medium"`
How much the model thinks before answering. See [Reasoning effort](#reasoning-effort).
### `stream` — boolean, default `false`
When `true`, tokens arrive as server-sent events terminated by `data: [DONE]`.
On a volume route the useful reason to stream is early cancellation: you can abandon a response that has already gone wrong instead of paying for the rest of it.
### `tools` / `tool_choice` — optional
Function definitions and the selection strategy. Functions, web search, file
search and computer use are all available on this family, and this generation
adds programmatic tool calling.
### `response_format` — object, optional
Constrains the answer's shape, including a JSON schema for machine-consumed
output.
### `group` — string, default `"default"`
Token group on the gateway. Leave it at `default` unless your account has been
given another group to route through.
***
## Reasoning effort
Six rungs, and unlike some families these do not collapse into one another —
each is a distinct behaviour. The default is **`medium`**: OpenAI's docs state
that "if you omit reasoning.effort, GPT-5.6 defaults to medium in both modes."
| Effort | OpenAI's stated best-for |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `none` | Latency-critical tasks that do not benefit from reasoning or multi-chained tool calls — voice, fast retrieval, classification |
| `low` | Efficient reasoning with modest added latency: tool use, planning, search, multi-step decisions |
| `medium` | **Default.** Quality and reliability where the task involves planning and judgement — agentic coding, research, spreadsheets and slides, delegated long-horizon work |
| `high` | Hard reasoning, complex debugging, deep planning, high-value tasks where quality beats latency |
| `xhigh` | Deep research and long asynchronous runs — security and code review, enterprise productivity. "Only use when your evals show a clear benefit" |
| `max` | "Maximum reasoning for your most complex tasks. If you are currently using xhigh, evaluate if max results in stronger performance" |
There is **no `minimal`** on this family.
This is the tier the bottom of that ladder exists for. **Set effort explicitly**
— the default is `medium`, so a request that omits the field is paying for a
reasoning pass a classification route probably does not want.
OpenAI also documents a **Pro reasoning mode** for GPT-5.6 (`reasoning.mode`,
independent of effort), and **multi-agent orchestration** in beta. Both are
features of OpenAI's Responses API, so neither is part of a Chat Completions
request through this endpoint.
***
## Image input
| Modality | Supported |
| -------- | :-------: |
| Text in | ✅ |
| Image in | ✅ |
| Text out | ✅ |
This generation accepts images **at their original dimensions**, with `original`
or `auto` image detail — no resizing step required before a screenshot or scan
goes in. Output is text only.
***
## Pricing dimensions
Billing is **per token**, in USD, against your `api.reapi.ai` balance, with
separate **input** and **output** rates. Three things to keep in mind:
* **Reasoning tokens bill as output.** The default effort is `medium`, so a
request that omits the field is paying for a reasoning pass.
* **Effort is the first lever**, and on this family every rung is real — a middle
setting buys a middle cost.
* **The tier is the second lever.** All three GPT-5.6 tiers share this context
window, this output ceiling, this effort ladder and this endpoint, so moving
between them changes reasoning depth and price, not capability surface.
Both token rates sit **20% below OpenAI's published per-token rate**, on input
and output alike. Current numbers are on the
[model page](https://reapi.ai/models/gpt-5-6-luna) and
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) — those tables are the
canonical source, not this page.
OpenAI prices cached input as a separate dimension. **reAPI does not publish a
cached-input rate for this model**, so bill against the input and output rates in
the pricing table.
Chat models bill in USD on the gateway balance. They do **not** draw down the
integer credits used by the media-generation endpoints on `reapi.ai/api/v1`.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1785000000,
"model": "gpt-5.6-luna",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1204,
"completion_tokens": 860,
"total_tokens": 2064
}
}
```
`usage.completion_tokens` includes reasoning tokens, so it is the number to
reconcile a bill against. On OpenAI's own Responses API the split is reported
under `output_tokens_details.reasoning_tokens`.
### Streaming (`stream: true`)
Server-sent events, each `data:` line carrying a `chat.completion.chunk` with the
incremental text in `choices[0].delta.content`, terminated by `data: [DONE]`.
***
## Errors
Failures use the gateway's standard envelope. See the
[errors catalog](/docs/api/errors) for the full code list. Common cases:
| Trigger | What to do |
| ----------------------------------------- | ---------------------------------------------------------------------------------- |
| Missing or invalid bearer token | Create a key in the api.reapi.ai console |
| Unknown `model` value | Send `gpt-5.6-luna` with the dots — not the hyphenated slug |
| `reasoning_effort` set to `minimal` | Not a value on this family — use `none`, `low`, `medium`, `high`, `xhigh` or `max` |
| `reasoning.mode` or multi-agent fields | Responses-API features; not part of a Chat Completions request |
| `max_completion_tokens` above the ceiling | Lower it; the documented maximum output is 128,000 |
| Truncated answer on a hard prompt | The reasoning pass consumed the allowance — raise `max_completion_tokens` |
| Insufficient balance | Top up on the gateway |
| Upstream rate limit | Retry with backoff, or route through another tier |
***
## Tips
* **Set effort explicitly.** The default is `medium`; on this tier `none` or
`low` is usually what you actually mean.
* **Cap `max_completion_tokens` tight.** On volume work it is a cost ceiling, not
just a length limit.
* **Constrain the output with a schema.** A machine-parsed route turns a bad
answer into a validation error you can see.
* **Do not pair `none` with a long tool loop.** OpenAI ties the no-reasoning rung
to routes without multi-chained tool calls; raise the effort instead.
* **Use it as the first stage.** Cheap triage here, escalation to a dearer tier
by model string, is the biggest cost lever on a chat workload.
* **The window is the frontier tier's.** Dropping to this tier does not force you
to split long inputs.
***
## Related
* [GPT-5.6 Luna model page](https://reapi.ai/models/gpt-5-6-luna) — current rates
* [GPT-5.6 Terra](/docs/gpt-5-6-terra) — the balanced tier above it
* [GPT-5.6 Sol](/docs/gpt-5-6-sol) — the frontier tier
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# gpt-5.6-sol (https://reapi.ai/docs/gpt-5-6-sol)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> OpenAI's GPT-5.6 Sol — the frontier model for complex professional work — exposed through api.reapi.ai as a drop-in
> OpenAI-compatible Chat Completions endpoint. A **1,050,000-token** context
> window, **128,000** max output tokens, six `reasoning_effort` rungs from
> `none` to `max` defaulting to **`medium`**, text and image input, and
> functions, web search, file search and computer use. OpenAI rates its reasoning **Highest** in this family and its speed Fast. The wire
> `model` id is **`gpt-5.6-sol`**. Current rates live on the
> [model page](https://reapi.ai/models/gpt-5-6-sol) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
**The URL and the model id differ.** This page and the model page spell the slug
`gpt-5-6-sol` because a URL cannot carry dots. The string your request body needs is
`gpt-5.6-sol`.
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-sol",
"messages": [
{ "role": "user", "content": "Review this migration plan and identify the failure modes." }
],
"reasoning_effort": "high",
"max_completion_tokens": 16000,
"stream": true
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="gpt-5.6-sol",
messages=[{"role": "user", "content": "Review this migration plan and identify the failure modes."}],
reasoning_effort="high",
max_completion_tokens=16000,
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="")
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "gpt-5.6-sol",
messages: [{ role: "user", content: "Review this migration plan and identify the failure modes." }],
reasoning_effort: "high",
max_completion_tokens: 16000,
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gpt-5.6-sol",
"messages": []map[string]string{
{"role": "user", "content": "Review this migration plan and identify the failure modes."},
},
"reasoning_effort": "high",
"max_completion_tokens": 16000,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Chat models are served from the `api.reapi.ai` gateway, which has its own console
and its own key. Create the key there, then send it as a bearer token:
```http
Authorization: Bearer YOUR_API_KEY
```
The gateway key is **not** the same credential as a media-generation key used
against `reapi.ai/api/v1`. Sign in at
[api.reapi.ai](https://api.reapi.ai/) to create one.
***
## Endpoint
```http
POST /v1/chat/completions
```
Base URL `https://api.reapi.ai`. The wire format is OpenAI-compatible, so the
same SDKs work once you swap the base URL, the key and the model string.
OpenAI states that its reasoning models "work better with the Responses API",
and that "while the Chat Completions API is still supported, you'll get improved
model intelligence and performance by using Responses." The GPT-5.6 release entry
lists `v1/responses`, `v1/chat/completions` and `v1/batch` as this family's
endpoints — so Chat Completions is a documented, supported surface, and this is a
vendor preference rather than a restriction.
***
## Request body
### `model` — string, required
Must be `gpt-5.6-sol` exactly. The bare `gpt-5.6` alias also routes here; sending the suffixed id pins the tier you are paying for.
### `messages` — array, required
Conversation history, each entry an object with `role` and `content`. Roles are
`system`, `user`, `assistant` and `tool`. Text and image parts are supported —
see [Image input](#image-input).
### `max_completion_tokens` — integer
Upper bound on generated tokens for this response, **reasoning tokens included**.
The documented maximum output is **128,000** tokens.
Reasoning tokens bill as **output** tokens and occupy the context window. A
budget sized only for the visible answer can truncate on a harder prompt, and it
is the usual reason an invoice exceeds an estimate built from answer length.
### `reasoning_effort` — string, default `"medium"`
How much the model thinks before answering. See [Reasoning effort](#reasoning-effort).
### `stream` — boolean, default `false`
When `true`, tokens arrive as server-sent events terminated by `data: [DONE]`.
Worth enabling on this tier in particular: at high effort the model can think for a long time before the first visible token, and a non-streamed request with a large budget can reach an HTTP timeout.
### `tools` / `tool_choice` — optional
Function definitions and the selection strategy. Functions, web search, file
search and computer use are all available on this family, and this generation
adds programmatic tool calling.
### `response_format` — object, optional
Constrains the answer's shape, including a JSON schema for machine-consumed
output.
### `group` — string, default `"default"`
Token group on the gateway. Leave it at `default` unless your account has been
given another group to route through.
***
## Reasoning effort
Six rungs, and unlike some families these do not collapse into one another —
each is a distinct behaviour. The default is **`medium`**: OpenAI's docs state
that "if you omit reasoning.effort, GPT-5.6 defaults to medium in both modes."
| Effort | OpenAI's stated best-for |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `none` | Latency-critical tasks that do not benefit from reasoning or multi-chained tool calls — voice, fast retrieval, classification |
| `low` | Efficient reasoning with modest added latency: tool use, planning, search, multi-step decisions |
| `medium` | **Default.** Quality and reliability where the task involves planning and judgement — agentic coding, research, spreadsheets and slides, delegated long-horizon work |
| `high` | Hard reasoning, complex debugging, deep planning, high-value tasks where quality beats latency |
| `xhigh` | Deep research and long asynchronous runs — security and code review, enterprise productivity. "Only use when your evals show a clear benefit" |
| `max` | "Maximum reasoning for your most complex tasks. If you are currently using xhigh, evaluate if max results in stronger performance" |
There is **no `minimal`** on this family.
This is the tier the top of that ladder exists for. Starting at `high` and
evaluating `xhigh` and `max` against your own cases is the usual approach; the
`medium` default will under-use what you are paying for.
OpenAI also documents a **Pro reasoning mode** for GPT-5.6 (`reasoning.mode`,
independent of effort), and **multi-agent orchestration** in beta. Both are
features of OpenAI's Responses API, so neither is part of a Chat Completions
request through this endpoint.
***
## Image input
| Modality | Supported |
| -------- | :-------: |
| Text in | ✅ |
| Image in | ✅ |
| Text out | ✅ |
This generation accepts images **at their original dimensions**, with `original`
or `auto` image detail — no resizing step required before a screenshot or scan
goes in. Output is text only.
***
## Pricing dimensions
Billing is **per token**, in USD, against your `api.reapi.ai` balance, with
separate **input** and **output** rates. Three things to keep in mind:
* **Reasoning tokens bill as output.** The default effort is `medium`, so a
request that omits the field is paying for a reasoning pass.
* **Effort is the first lever**, and on this family every rung is real — a middle
setting buys a middle cost.
* **The tier is the second lever.** All three GPT-5.6 tiers share this context
window, this output ceiling, this effort ladder and this endpoint, so moving
between them changes reasoning depth and price, not capability surface.
Both token rates sit **20% below OpenAI's published per-token rate**, on input
and output alike. Current numbers are on the
[model page](https://reapi.ai/models/gpt-5-6-sol) and
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) — those tables are the
canonical source, not this page.
OpenAI prices cached input as a separate dimension. **reAPI does not publish a
cached-input rate for this model**, so bill against the input and output rates in
the pricing table.
Chat models bill in USD on the gateway balance. They do **not** draw down the
integer credits used by the media-generation endpoints on `reapi.ai/api/v1`.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1785000000,
"model": "gpt-5.6-sol",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1204,
"completion_tokens": 860,
"total_tokens": 2064
}
}
```
`usage.completion_tokens` includes reasoning tokens, so it is the number to
reconcile a bill against. On OpenAI's own Responses API the split is reported
under `output_tokens_details.reasoning_tokens`.
### Streaming (`stream: true`)
Server-sent events, each `data:` line carrying a `chat.completion.chunk` with the
incremental text in `choices[0].delta.content`, terminated by `data: [DONE]`.
***
## Errors
Failures use the gateway's standard envelope. See the
[errors catalog](/docs/api/errors) for the full code list. Common cases:
| Trigger | What to do |
| ----------------------------------------- | ---------------------------------------------------------------------------------- |
| Missing or invalid bearer token | Create a key in the api.reapi.ai console |
| Unknown `model` value | Send `gpt-5.6-sol` with the dots — not the hyphenated slug |
| `reasoning_effort` set to `minimal` | Not a value on this family — use `none`, `low`, `medium`, `high`, `xhigh` or `max` |
| `reasoning.mode` or multi-agent fields | Responses-API features; not part of a Chat Completions request |
| `max_completion_tokens` above the ceiling | Lower it; the documented maximum output is 128,000 |
| Truncated answer on a hard prompt | The reasoning pass consumed the allowance — raise `max_completion_tokens` |
| Insufficient balance | Top up on the gateway |
| Upstream rate limit | Retry with backoff, or route through another tier |
***
## Tips
* **Start at `high`, not the default.** The default is `medium`; if a route is on
this tier it is usually because it needs more than that.
* **Evaluate `max` only against `xhigh`.** OpenAI's own framing is to compare the
two rather than jump straight to the top.
* **Stream, and set a generous `max_completion_tokens`** so a deep pass has room
to finish.
* **Send the suffixed id in production.** `gpt-5.6` works, but it follows
whatever OpenAI points the alias at later.
* **Keep the routine turns on a cheaper tier.** Same key, same endpoint, one
model string apart — escalation is a field, not an integration.
* **Give it the whole system.** A 1.05M-token window is only useful if you
actually put the repository, the tests and the history in it.
***
## Related
* [GPT-5.6 Sol model page](https://reapi.ai/models/gpt-5-6-sol) — current rates
* [GPT-5.6 Terra](/docs/gpt-5-6-terra) — the balanced tier below it
* [GPT-5.6 Luna](/docs/gpt-5-6-luna) — the volume tier
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# gpt-5.6-terra (https://reapi.ai/docs/gpt-5-6-terra)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> OpenAI's GPT-5.6 Terra — the tier that balances intelligence and cost — exposed through api.reapi.ai as a drop-in
> OpenAI-compatible Chat Completions endpoint. A **1,050,000-token** context
> window, **128,000** max output tokens, six `reasoning_effort` rungs from
> `none` to `max` defaulting to **`medium`**, text and image input, and
> functions, web search, file search and computer use. OpenAI rates its reasoning **Higher** and its speed Fast. The wire
> `model` id is **`gpt-5.6-terra`**. Current rates live on the
> [model page](https://reapi.ai/models/gpt-5-6-terra) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
**The URL and the model id differ.** This page and the model page spell the slug
`gpt-5-6-terra` because a URL cannot carry dots. The string your request body needs is
`gpt-5.6-terra`.
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.6-terra",
"messages": [
{ "role": "user", "content": "Implement the change in the linked issue, with tests." }
],
"reasoning_effort": "medium",
"max_completion_tokens": 8192,
"stream": true
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="gpt-5.6-terra",
messages=[{"role": "user", "content": "Implement the change in the linked issue, with tests."}],
reasoning_effort="medium",
max_completion_tokens=8192,
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta.content
if delta:
print(delta, end="")
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "gpt-5.6-terra",
messages: [{ role: "user", content: "Implement the change in the linked issue, with tests." }],
reasoning_effort: "medium",
max_completion_tokens: 8192,
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gpt-5.6-terra",
"messages": []map[string]string{
{"role": "user", "content": "Implement the change in the linked issue, with tests."},
},
"reasoning_effort": "medium",
"max_completion_tokens": 8192,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Chat models are served from the `api.reapi.ai` gateway, which has its own console
and its own key. Create the key there, then send it as a bearer token:
```http
Authorization: Bearer YOUR_API_KEY
```
The gateway key is **not** the same credential as a media-generation key used
against `reapi.ai/api/v1`. Sign in at
[api.reapi.ai](https://api.reapi.ai/) to create one.
***
## Endpoint
```http
POST /v1/chat/completions
```
Base URL `https://api.reapi.ai`. The wire format is OpenAI-compatible, so the
same SDKs work once you swap the base URL, the key and the model string.
OpenAI states that its reasoning models "work better with the Responses API",
and that "while the Chat Completions API is still supported, you'll get improved
model intelligence and performance by using Responses." The GPT-5.6 release entry
lists `v1/responses`, `v1/chat/completions` and `v1/batch` as this family's
endpoints — so Chat Completions is a documented, supported surface, and this is a
vendor preference rather than a restriction.
***
## Request body
### `model` — string, required
Must be `gpt-5.6-terra` exactly. There is no alias for this tier — the bare `gpt-5.6` id routes to the frontier model instead.
### `messages` — array, required
Conversation history, each entry an object with `role` and `content`. Roles are
`system`, `user`, `assistant` and `tool`. Text and image parts are supported —
see [Image input](#image-input).
### `max_completion_tokens` — integer
Upper bound on generated tokens for this response, **reasoning tokens included**.
The documented maximum output is **128,000** tokens.
Reasoning tokens bill as **output** tokens and occupy the context window. A
budget sized only for the visible answer can truncate on a harder prompt, and it
is the usual reason an invoice exceeds an estimate built from answer length.
### `reasoning_effort` — string, default `"medium"`
How much the model thinks before answering. See [Reasoning effort](#reasoning-effort).
### `stream` — boolean, default `false`
When `true`, tokens arrive as server-sent events terminated by `data: [DONE]`.
At the default effort the model thinks before it answers, so streaming is what keeps an interactive surface from looking stalled.
### `tools` / `tool_choice` — optional
Function definitions and the selection strategy. Functions, web search, file
search and computer use are all available on this family, and this generation
adds programmatic tool calling.
### `response_format` — object, optional
Constrains the answer's shape, including a JSON schema for machine-consumed
output.
### `group` — string, default `"default"`
Token group on the gateway. Leave it at `default` unless your account has been
given another group to route through.
***
## Reasoning effort
Six rungs, and unlike some families these do not collapse into one another —
each is a distinct behaviour. The default is **`medium`**: OpenAI's docs state
that "if you omit reasoning.effort, GPT-5.6 defaults to medium in both modes."
| Effort | OpenAI's stated best-for |
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `none` | Latency-critical tasks that do not benefit from reasoning or multi-chained tool calls — voice, fast retrieval, classification |
| `low` | Efficient reasoning with modest added latency: tool use, planning, search, multi-step decisions |
| `medium` | **Default.** Quality and reliability where the task involves planning and judgement — agentic coding, research, spreadsheets and slides, delegated long-horizon work |
| `high` | Hard reasoning, complex debugging, deep planning, high-value tasks where quality beats latency |
| `xhigh` | Deep research and long asynchronous runs — security and code review, enterprise productivity. "Only use when your evals show a clear benefit" |
| `max` | "Maximum reasoning for your most complex tasks. If you are currently using xhigh, evaluate if max results in stronger performance" |
There is **no `minimal`** on this family.
This tier is where the default is genuinely the right starting point. Sweep `low`
for speed on planning-shaped work and `high` when a route needs complex debugging;
if your evals keep choosing `xhigh` or `max`, that route belongs on the frontier
tier instead.
OpenAI also documents a **Pro reasoning mode** for GPT-5.6 (`reasoning.mode`,
independent of effort), and **multi-agent orchestration** in beta. Both are
features of OpenAI's Responses API, so neither is part of a Chat Completions
request through this endpoint.
***
## Image input
| Modality | Supported |
| -------- | :-------: |
| Text in | ✅ |
| Image in | ✅ |
| Text out | ✅ |
This generation accepts images **at their original dimensions**, with `original`
or `auto` image detail — no resizing step required before a screenshot or scan
goes in. Output is text only.
***
## Pricing dimensions
Billing is **per token**, in USD, against your `api.reapi.ai` balance, with
separate **input** and **output** rates. Three things to keep in mind:
* **Reasoning tokens bill as output.** The default effort is `medium`, so a
request that omits the field is paying for a reasoning pass.
* **Effort is the first lever**, and on this family every rung is real — a middle
setting buys a middle cost.
* **The tier is the second lever.** All three GPT-5.6 tiers share this context
window, this output ceiling, this effort ladder and this endpoint, so moving
between them changes reasoning depth and price, not capability surface.
Both token rates sit **20% below OpenAI's published per-token rate**, on input
and output alike. Current numbers are on the
[model page](https://reapi.ai/models/gpt-5-6-terra) and
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) — those tables are the
canonical source, not this page.
OpenAI prices cached input as a separate dimension. **reAPI does not publish a
cached-input rate for this model**, so bill against the input and output rates in
the pricing table.
Chat models bill in USD on the gateway balance. They do **not** draw down the
integer credits used by the media-generation endpoints on `reapi.ai/api/v1`.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1785000000,
"model": "gpt-5.6-terra",
"choices": [
{
"index": 0,
"message": { "role": "assistant", "content": "..." },
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1204,
"completion_tokens": 860,
"total_tokens": 2064
}
}
```
`usage.completion_tokens` includes reasoning tokens, so it is the number to
reconcile a bill against. On OpenAI's own Responses API the split is reported
under `output_tokens_details.reasoning_tokens`.
### Streaming (`stream: true`)
Server-sent events, each `data:` line carrying a `chat.completion.chunk` with the
incremental text in `choices[0].delta.content`, terminated by `data: [DONE]`.
***
## Errors
Failures use the gateway's standard envelope. See the
[errors catalog](/docs/api/errors) for the full code list. Common cases:
| Trigger | What to do |
| ----------------------------------------- | ---------------------------------------------------------------------------------- |
| Missing or invalid bearer token | Create a key in the api.reapi.ai console |
| Unknown `model` value | Send `gpt-5.6-terra` with the dots — not the hyphenated slug |
| `reasoning_effort` set to `minimal` | Not a value on this family — use `none`, `low`, `medium`, `high`, `xhigh` or `max` |
| `reasoning.mode` or multi-agent fields | Responses-API features; not part of a Chat Completions request |
| `max_completion_tokens` above the ceiling | Lower it; the documented maximum output is 128,000 |
| Truncated answer on a hard prompt | The reasoning pass consumed the allowance — raise `max_completion_tokens` |
| Insufficient balance | Top up on the gateway |
| Upstream rate limit | Retry with backoff, or route through another tier |
***
## Tips
* **Leave the default alone first.** `medium` is what OpenAI calls the
well-balanced point, and this tier is priced for it.
* **Then sweep in both directions** on a real route — `low` for speed, `high` for
hard debugging — and keep whichever your evals prefer.
* **Escalate on evidence, not instinct.** A route that always wants `xhigh` or
`max` is telling you it belongs one tier up.
* **Use a JSON schema for agent steps** whose output another step consumes.
* **Budget for the reasoning pass** in `max_completion_tokens`, not just the
answer.
* **The window is the frontier tier's.** Choosing this tier does not shrink what
the model can see.
***
## Related
* [GPT-5.6 Terra model page](https://reapi.ai/models/gpt-5-6-terra) — current rates
* [GPT-5.6 Sol](/docs/gpt-5-6-sol) — the frontier tier above it
* [GPT-5.6 Luna](/docs/gpt-5-6-luna) — the volume tier below it
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# gpt-image-2-official (https://reapi.ai/docs/gpt-image-2-stable)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> The **stable channel** of GPT Image 2 on reAPI — async
> `/api/v1/images/generations` with the full feature surface: quality tiers
> (`auto` / `low` / `medium` / `high`), batches up to 4 images,
> mask-based inpainting, and background control. The channel always
> returns PNG. See current pricing on the
> [model page](https://reapi.ai/models/gpt-image-2).
**Wire id is `gpt-image-2-official`.** The doc / pricing page /
playground all label this variant as **"Stable"** for clarity, but the
request body must set `"model": "gpt-image-2-official"`. The shorter
`gpt-image-2` is a separate variant — see
[Differences from `gpt-image-2`](#differences-from-gpt-image-2).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2-official",
"prompt": "ancient castle beneath a starry sky",
"size": "16:9",
"resolution": "2k",
"quality": "high",
"n": 1
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "gpt-image-2-official",
"prompt": "ancient castle beneath a starry sky",
"size": "16:9",
"resolution": "2k",
"quality": "high",
"n": 1,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-image-2-official",
prompt: "ancient castle beneath a starry sky",
size: "16:9",
resolution: "2k",
quality: "high",
n: 1,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gpt-image-2-official",
"prompt": "ancient castle beneath a starry sky",
"size": "16:9",
"resolution": "2k",
"quality": "high",
"n": 1,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2-official",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls`
holds the generated image URLs (length = `n`), valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Request body
### `model` — required
`string`. Must be `"gpt-image-2-official"`.
### `prompt` — required
`string`, **1–4,000 characters**. English and Chinese both supported.
Detailed prompts produce better results. Every prompt goes through
pre-submission moderation; rejected prompts return `400` at no charge.
Don't restate the ratio in the prompt — pass it via `size`.
Repeating it confuses the upstream.
**Failure modes.**
* Missing / empty → `400 prompt is required` (code `20002`).
* Longer than 4,000 chars → `400 prompt must be at most 4000 characters` (code `20007`).
### `size` — string, default `"1:1"`
Output ratio. One of these 14 values (or `auto` to let the upstream
pick from the prompt / first reference image):
```
auto
1:1 16:9 9:16 4:3 3:4
3:2 2:3 5:4 4:5
2:1 1:2 21:9 9:21
```
This channel additionally accepts a pixel string `WIDTHxHEIGHT` (e.g.
`1024x1024`, `3840x2160`); the upstream snaps it to the closest
supported dimension. Anything outside the list and not a pixel string
is rejected with `400`.
When `size` is omitted:
* **T2I** (no `image_urls`) → defaults to `"1:1"`.
* **I2I** (`image_urls` set) → output mirrors the first reference
image's ratio (upstream-side derivation; gateway omits the field).
#### 4K constraint
`resolution: "4k"` is only valid with the six widescreen sizes below.
Other ratios — including `auto` — exceed the model's total-pixel cap
and are rejected with `400`. **`size` is required** when
`resolution: "4k"`; sending 4K with no `size` is rejected before the
request leaves the gateway.
| `size` | `1k` | `2k` | `4k` |
| --------------- | ------------ | ------------ | ------------- |
| `1:1` | 1024×1024 | 2048×2048 | ❌ |
| `3:2` / `2:3` | 1536×1024 | 2048×1360 | ❌ |
| `4:3` / `3:4` | 1024×768 | 2048×1536 | ❌ |
| `5:4` / `4:5` | 1280×1024 | 2560×2048 | ❌ |
| `16:9` / `9:16` | 1536×864 | 2048×1152 | **3840×2160** |
| `2:1` / `1:2` | 2048×1024 | 2688×1344 | **3840×1920** |
| `21:9` / `9:21` | 2016×864 | 2688×1152 | **3840×1648** |
| `auto` | server picks | server picks | ❌ |
> `3:2` / `2:3` at 2K resolves to 2048×1360 — 1360 is the nearest
> 16-aligned approximation of 3:2 (under 0.5% off). Other cells are
> exact.
**4K is widescreen-only.** `resolution: "4k"` requires `size` to be
one of `16:9`, `9:16`, `2:1`, `1:2`, `21:9`, or `9:21`. Sending 4K
with `auto`, a non-widescreen ratio, or no size returns `400 4K
requires an explicit size in {16:9, 9:16, 2:1, 1:2, 21:9, 9:21}`
(code `20003`). See the 4K table under `size` above for the full grid.
### `resolution` — string, default `"1k"`
`1k` / `2k` / `4k`. Case-insensitive — `"2K"` and `"2k"` are
equivalent. Drives pricing.
### `quality` — string, default `"auto"`
`auto` / `low` / `medium` / `high`. Higher tiers are slower and price
separately.
**`auto` bills at the `high` rate.** The upstream does not honor a
lower-tier hint when `auto` is set — the request runs at `high`
internally. reAPI mirrors that on billing: `auto` (and any request
that omits `quality`) is charged at the `high` rate. To pay the
`low` or `medium` rate, set `quality` explicitly.
### `n` — integer, default `1`
Number of images per request. Range `1`–`4`. Each generated image is
billed individually — a 4-image batch costs **4×** the per-image
rate. `output.image_urls` length matches `n`.
### `image_urls` — string\[]
Reference images for image-to-image. Up to **16** entries. Triggers
I2I when set. Public HTTP(S) URLs only.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide. Upload
to your own object storage (S3 / R2 / OSS) and pass the URL.
### `mask_url` — string
PNG mask URL for inpainting. **Requires `image_urls` with at least
one entry.** The mask must:
* carry an alpha channel where the model should paint, and
* share dimensions with the **first** entry in `image_urls`.
Public HTTP(S) URL only. Mismatched dimensions or a fully opaque mask
return `400` from upstream.
### `background` — string, default `"auto"`
`auto` / `opaque` / `transparent`.
**`transparent` is silently downgraded.** `gpt-image-2-official` does
not produce transparent output even when explicitly requested.
Pre- or post-process client-side if you need transparency.
### `moderation` — string, default `"auto"`
`auto` / `low`. `low` is more lenient — useful when prompts that
should pass are getting rejected.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output`
fill in over time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2-official",
"status": "completed",
"created_at": 1735000000,
"output": {
"image_urls": [
"https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png",
"https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/1.png"
]
},
"error": null
}
```
| Field | Type | Notes |
| ------------------- | -------------- | ----------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion |
| `output.image_urls` | string\[] | Generated image URLs — length = `n`, valid for 7 days |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
### Upstream errors (502 / 503)
reAPI does not surface raw upstream `5xx` codes to the caller. When
the upstream provider returns `502 Bad Gateway`, `503 Service
Unavailable`, or a connection failure, the worker retries up to 5
attempts with exponential backoff (\~30s total budget). If all retries
fail, the task ends with one of these reAPI-specific codes (returned
via `GET /api/v1/tasks/{id}` as `error.code`):
| reAPI code | Meaning | Origin |
| ---------- | -------------------------- | ------------------------------------ |
| `80001` | `provider_submit_failed` | Upstream `5xx` / network on submit |
| `80002` | `provider_polling_timeout` | Wall-clock cap reached while polling |
| `80003` | `provider_failed` | Upstream returned a terminal failure |
See [Errors catalog](/docs/api/errors) for the full code list.
***
## Validation errors
All cases below return HTTP 400 with the noted code. Pattern-match on
`code`, not `message` — message strings carry request-specific context
(field names, observed values) and are not a stable contract.
| Trigger | Code | Message |
| ----------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
| `prompt` missing / empty | `20002` | `prompt is required` |
| `prompt` longer than 4,000 chars | `20007` | `prompt must be at most 4000 characters` |
| Unknown `size` value | `20003` | `invalid size "..." (allowed: 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 5:4, 4:5, 2:1, 1:2, 21:9, 9:21, auto, or WxH pixels)` |
| Unknown `resolution` value | `20003` | `invalid resolution "..." (allowed: 1k, 2k, 4k)` |
| `4k` with non-widescreen / `auto` / no `size` | `20003` | `4K requires an explicit size in {16:9, 9:16, 2:1, 1:2, 21:9, 9:21}` |
| `n` outside `1`–`4` | `20003` | `n must be between 1 and 4` |
| `image_urls` > 16 | `20003` | `image_urls accepts at most 16 entries` |
| `mask_url` without `image_urls` | `20003` | `mask_url requires at least one entry in image_urls` |
| Any URL field carrying a `data:` URI | `20003` | `image URL must be a public http(s) URL — data: URIs are rejected` (or `mask_url must be a public URL …`) |
| Unknown `quality` / `background` / `moderation` | `20003` | `invalid "..." (allowed: …)` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` —
see [Errors catalog](/docs/api/errors) for the wire format and
request\_id correlation.
***
## Recipes
### Text-to-image — minimal
```json
{
"model": "gpt-image-2-official",
"prompt": "ancient castle beneath a starry sky"
}
```
### 2K poster
```json
{
"model": "gpt-image-2-official",
"prompt": "cyberpunk night cityscape",
"size": "16:9",
"resolution": "2k",
"quality": "high"
}
```
### 4K wallpaper
```json
{
"model": "gpt-image-2-official",
"prompt": "panoramic snow-capped mountains at sunrise",
"size": "16:9",
"resolution": "4k",
"quality": "high"
}
```
### Multi-reference fusion (image-to-image)
```json
{
"model": "gpt-image-2-official",
"prompt": "blend the two references into one cohesive illustrated poster",
"size": "1:1",
"quality": "high",
"image_urls": [
"https://your-cdn.com/input-a.png",
"https://your-cdn.com/input-b.png"
]
}
```
### Mask-based inpainting
The mask must match the dimensions of the first `image_urls` entry
and carry an alpha channel where the model should paint.
```json
{
"model": "gpt-image-2-official",
"prompt": "replace the background with a desert sunset",
"size": "1:1",
"quality": "medium",
"image_urls": ["https://your-cdn.com/photo.png"],
"mask_url": "https://your-cdn.com/mask.png"
}
```
### Batch of 4 variations
```json
{
"model": "gpt-image-2-official",
"prompt": "four minimalist poster variations of a red fox",
"size": "1:1",
"quality": "low",
"n": 4
}
```
### Lenient moderation
```json
{
"model": "gpt-image-2-official",
"prompt": "moody noir portrait, dramatic lighting",
"size": "4:5",
"resolution": "2k",
"quality": "high",
"moderation": "low"
}
```
***
## Polling pattern
Latency depends heavily on `quality` and `resolution`:
| `quality` × `resolution` | Typical end-to-end |
| ------------------------ | ------------------ |
| `low` × `1k` | 20 – 40 s |
| `medium` × `1k` | 30 – 60 s |
| `high` × `2k` | 60 – 120 s |
| `high` × `4k` | 120 – 180 s |
Recommended cadence:
```
0–30s: wait before the first poll
30s–3m: poll every 3–5s
3m+: back off to 10s; cap at 30s
```
Set client-side request timeouts to **≥ 180 seconds** when using
`quality: "high"` with `2k` / `4k`.
The worker's wall-clock cap is **1 hour** (image tasks), comfortably above
any realistic queue.
***
## Pricing
Per-image rates depend on three axes:
* **Resolution** (`1k` / `2k` / `4k`)
* **Quality** (`low` / `medium` / `high` — `auto` bills at `high`)
* **`size`** (the `2k` / `4k` cells charge slightly more for the
widest ratios; live numbers on the [model page](https://reapi.ai/models/gpt-image-2#pricing) are authoritative)
When `size` is `"auto"`, reAPI prices against the **most expensive
ratio** at the resolved `(resolution, quality)` cell so the bill never
undershoots once the upstream picks a ratio at runtime. The forwarded
body keeps `auto` — the override is billing-only.
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_image_usd × n × 1000)
```
Charged on submit; refunded automatically on `failed` /
`provider_submit_failed` / `provider_polling_timeout`. Moderated
prompts return `400` before charging — no balance impact.
**Worked example.** `n: 2`, `resolution: "2k"`, `quality: "high"`,
`size: "16:9"` charges **2× the 16:9\@2k\_high cell**. Rates evolve;
check the [model page](https://reapi.ai/models/gpt-image-2#pricing) for
live numbers.
***
## Tips
* **Specify ratio in `size`, not in the prompt.** Repeating "16:9 wide
shot" inside the prompt confuses the upstream. Use `size` for shape
and the prompt for content / style.
* **Set `quality` explicitly to save money.** `auto` runs at `high` —
if `medium` is good enough, send `"medium"` and pay the medium rate.
* **Batches are linear cost.** `n: 4` is exactly 4× the price of
`n: 1` at the same parameters — no batch discount, but no batch
surcharge either.
* **Mask alpha matters.** The mask's transparent regions are where the
model paints; opaque regions are preserved. A fully opaque mask
returns `400` from upstream.
* **Pre-cache reference images.** The upstream fetches each
`image_urls` entry once per submit; if your CDN is slow, latency
shows up as polling time, not as billing.
***
## Differences from `gpt-image-2`
| Feature | [`gpt-image-2`](/docs/gpt-image-2) | `gpt-image-2-official` (this page) |
| ------------------------------------- | :--------------------------------: | :--------------------------------------------------------------------------------------------: |
| Endpoint | `/api/v1/images/generations` | `/api/v1/images/generations` |
| Async / task model | ✅ | ✅ |
| `size` enum (14) | ✅ | ✅ |
| Resolution tiers (`1k` / `2k` / `4k`) | ✅ | ✅ |
| Image-to-image (`image_urls`) | ✅ | ✅ |
| Pricing | flat per resolution | varies by resolution × quality × ratio — see [model page](https://reapi.ai/models/gpt-image-2) |
| Batch (`n` > 1) | ❌ | ✅ up to 4 |
| Quality tiers | ❌ | ✅ `auto` / `low` / `medium` / `high` |
| Mask inpainting (`mask_url`) | ❌ | ✅ |
| Background control | ❌ | ✅ (`transparent` silently downgraded) |
| Moderation knob | ❌ (default) | ✅ `auto` / `low` |
***
## Related
* [`gpt-image-2`](/docs/gpt-image-2) — cheaper variant, single image,
flat per-resolution pricing
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Pricing — gpt-image-2](https://reapi.ai/models/gpt-image-2#pricing)
---
# gpt-image-2 (https://reapi.ai/docs/gpt-image-2)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Async image generation through reAPI's cheap channel. One endpoint
> covers text-to-image and image-to-image. Flat per-resolution
> pricing — the cheapest variant in the family. For batches, masks,
> quality tiers, or transparent backgrounds, switch to
> [`gpt-image-2 stable`](/docs/gpt-image-2-stable).
* Async processing — `POST` returns a `task_id`, poll `GET /api/v1/tasks/{id}` for the result.
* standard `/api/v1/images/generations` envelope (text-to-image / image-to-image).
* 14 ratios via `size`; three resolution tiers (`1k` / `2k` / `4k`) via `resolution`.
* Up to 16 reference images via `image_urls` — public HTTP(S) URLs only.
* Single image per request — `n` is fixed at 1.
* Flat per-resolution price; failed / moderated requests are not charged.
***
## Try it
***
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "a ginger cat on a windowsill at sunset, watercolor",
"size": "16:9",
"resolution": "2k"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "gpt-image-2",
"prompt": "a ginger cat on a windowsill at sunset, watercolor",
"size": "16:9",
"resolution": "2k",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "gpt-image-2",
prompt: "a ginger cat on a windowsill at sunset, watercolor",
size: "16:9",
resolution: "2k",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "gpt-image-2",
"prompt": "a ginger cat on a windowsill at sunset, watercolor",
"size": "16:9",
"resolution": "2k",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2",
"status": "processing",
"created_at": 1735000000
}
```
Hold onto `id` and poll `GET /api/v1/tasks/{id}` until completion.
***
## Endpoint
```http
POST /api/v1/images/generations # submit a job
GET /api/v1/tasks/{id} # poll for the result
```
Async — submit returns a `task_id` immediately; the actual image
arrives via the polling endpoint. Polling is free.
***
## Authentication
Bearer token, minted at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer rk_live_xxx
```
***
## Body
### `model`
`string` · **required** · default `"gpt-image-2"`
Must be `"gpt-image-2"`.
### `prompt`
`string` · **required**
1 – 4000 characters. English and Chinese both supported. Detailed
prompts produce better results. Every prompt goes through
pre-submission moderation; rejected prompts return `400` at no
charge.
Don't restate the ratio in the prompt — pass it via `size`.
Repeating it in the prompt confuses the upstream.
### `size`
`string` · default `"1:1"` (text-to-image) / inherited (image-to-image)
Output ratio. One of these 14 values:
```
auto
1:1 16:9 9:16 4:3 3:4
3:2 2:3 5:4 4:5
2:1 1:2 21:9 9:21
```
`auto` lets the upstream pick from the prompt or first reference
image. Pixel strings (e.g. `1024x1024`) are not accepted — combine
`size` with `resolution` to control output pixels. Anything outside
the list is rejected with `400`.
When `image_urls` is provided **without** `size`, the output
inherits the first reference image's resolution (image-to-image
mode). Pass `size` to force a specific ratio.
### `resolution`
`string` · default `"1k"`
`1k` / `2k` / `4k`. Case-insensitive — `"2K"` and `"2k"` are
equivalent.
`4k` is only valid with the six widescreen sizes below. Other
sizes — including `auto` — exceed the model's total-pixel cap and
are rejected with `400`.
| `size` | `1k` | `2k` | `4k` |
| --------------- | ------------ | ------------ | ------------- |
| `1:1` | 1024×1024 | 2048×2048 | ❌ |
| `3:2` / `2:3` | 1536×1024 | 2048×1360 | ❌ |
| `4:3` / `3:4` | 1024×768 | 2048×1536 | ❌ |
| `5:4` / `4:5` | 1280×1024 | 2560×2048 | ❌ |
| `16:9` / `9:16` | 1536×864 | 2048×1152 | **3840×2160** |
| `2:1` / `1:2` | 2048×1024 | 2688×1344 | **3840×1920** |
| `21:9` / `9:21` | 2016×864 | 2688×1152 | **3840×1648** |
| `auto` | server picks | server picks | ❌ |
### `n`
`integer` · default `1` · **must be `1`**
Single image per request. This variant produces one image per call;
sending any other value returns `400`. For batches up to 4, use
[`gpt-image-2 stable`](/docs/gpt-image-2-stable).
### `image_urls`
`string[]` · optional
Reference images for image-to-image. Up to **16** entries. Each
entry must be a public HTTP(S) URL — `data:` / base64 payloads are
rejected. Upload to your own object storage (S3 / R2 / OSS) and pass
the URL.
**No batch / quality / mask on this variant.** Sending `n > 1`,
`quality`, `mask_url`, `background`, `moderation`,
`output_format`, or `output_compression` against `gpt-image-2`
returns `400`. Switch to
[`gpt-image-2 stable`](/docs/gpt-image-2-stable) — set
`"model": "gpt-image-2-official"` — when you need those features.
***
## Use cases
### 1. Text-to-image (minimal)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gpt-image-2",
"prompt": "a ginger cat on a windowsill at sunset, watercolor"
}'
```
### 2. Text-to-image at 2K widescreen
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gpt-image-2",
"prompt": "a corgi astronaut on the moon, cinematic",
"size": "16:9",
"resolution": "2k"
}'
```
### 3. Text-to-image at 4K
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gpt-image-2",
"prompt": "ancient castle beneath a starry sky",
"size": "16:9",
"resolution": "4k"
}'
```
### 4. Image-to-image (single reference)
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gpt-image-2",
"prompt": "turn this photo into a watercolor painting",
"image_urls": ["https://your-cdn.com/photo.jpg"]
}'
```
### 5. Multi-reference fusion
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gpt-image-2",
"prompt": "blend these two photos into a poster",
"size": "4:3",
"resolution": "2k",
"image_urls": [
"https://your-cdn.com/photo-a.jpg",
"https://your-cdn.com/photo-b.jpg"
]
}'
```
### 6. Force a ratio for image-to-image
When the reference is portrait but you need a landscape output:
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-d '{
"model": "gpt-image-2",
"prompt": "extend the scene horizontally into a moonlit forest",
"size": "21:9",
"resolution": "2k",
"image_urls": ["https://your-cdn.com/portrait.jpg"]
}'
```
***
## Response
`POST` returns immediately with `status: "processing"`; poll
`GET /api/v1/tasks/{id}` until `status` is `completed` or `failed`.
Both responses share the same envelope — the `output` field is
`null` until the task finishes.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2",
"status": "processing",
"created_at": 1735000000
}
```
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2",
"status": "completed",
"created_at": 1735000000,
"output": {
"image_urls": [
"https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png"
]
},
"error": null
}
```
URLs are stable for **7 days**. Download to your own storage if you
need them longer.
```json
{
"error": {
"code": 20003,
"message": "gpt-image-2: field 'quality' is only supported on gpt-image-2-official",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 10003,
"message": "API key invalid",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 30001,
"message": "Insufficient credits",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 50001,
"message": "Rate limit exceeded",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
```json
{
"error": {
"code": 60099,
"message": "Internal error — please retry",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
### Upstream errors (502 / 503)
reAPI does not surface raw upstream `5xx` codes to the caller. When the
upstream provider returns `502 Bad Gateway`, `503 Service Unavailable`,
or a connection failure, the worker retries up to 5 attempts with
exponential backoff (\~30s total budget). If all retries fail, the task
ends with one of these reapi-specific error codes (returned via
`GET /api/v1/tasks/{id}` as `error.code`):
| reapi code | Meaning | Origin |
| ---------- | -------------------------- | ------------------------------------ |
| `80001` | `provider_submit_failed` | Upstream `5xx` / network on submit |
| `80002` | `provider_polling_timeout` | Wall-clock cap reached while polling |
| `80003` | `provider_failed` | Upstream returned a terminal failure |
See [Errors catalog](/docs/api/errors) for the full code list.
***
## Polling
| `status` | Meaning |
| ------------ | -------------------------------- |
| `processing` | Submitted, still generating |
| `completed` | `output.image_urls` is ready |
| `failed` | See `error.message`; not charged |
Recommended cadence:
```
0–30s: wait before the first poll
30s–3m: poll every 3–5s
3m+: back off to 10s; cap at 30s
```
A 1K image typically completes in 30–60s; 2K adds \~30s; 4K can take
90–120s.
***
## Differences from the stable channel
| Feature | `gpt-image-2` (this page) | [`gpt-image-2 stable`](/docs/gpt-image-2-stable) |
| ------------------------------- | :--------------------------: | :--------------------------------------------------------------------------: |
| Endpoint | `/api/v1/images/generations` | `/api/v1/images/generations` |
| `size` enum (14) | ✅ | ✅ |
| Resolution tiers (1k / 2k / 4k) | ✅ | ✅ |
| Image-to-image (`image_urls`) | ✅ | ✅ |
| Pricing | flat per resolution | varies by parameters — see [model page](https://reapi.ai/models/gpt-image-2) |
| Batch (`n` > 1) | ❌ | ✅ up to 4 |
| Quality tiers | ❌ | ✅ `auto` / `low` / `medium` / `high` |
| Mask inpainting (`mask_url`) | ❌ | ✅ |
| Background control | ❌ | ✅ (`transparent` is silently downgraded by upstream) |
| Output format / compression | ❌ (always PNG) | ✅ `png` / `jpeg` / `webp` + compression |
| Moderation knob | ❌ (default) | ✅ `auto` / `low` |
***
## Related
* [`gpt-image-2 stable`](/docs/gpt-image-2-stable) — full feature
surface (batch, mask, quality tiers).
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Pricing — gpt-image-2](https://reapi.ai/models/gpt-image-2#pricing)
---
# grok-imagine-1.0-video (https://reapi.ai/docs/grok-imagine-1-0-video)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Grok Imagine 1.0 Video — async video generation. **One model id**
> (`grok-imagine-1.0-video`) covers both **text-to-video** and
> **image-to-video**. Mode is implicit: zero `image_urls` runs T2V; 1 to 7
> reference images run I2V. 6 to 30 second outputs at 480p / 720p, five
> aspect ratios. See current pricing on the
> [model page](https://reapi.ai/models/grok-imagine-1-0-video).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-1.0-video",
"prompt": "A dog running on a sunlit beach, slow-motion",
"size": "16:9",
"duration": 6,
"quality": "720p"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "grok-imagine-1.0-video",
"prompt": "A dog running on a sunlit beach, slow-motion",
"size": "16:9",
"duration": 6,
"quality": "720p",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "grok-imagine-1.0-video",
prompt: "A dog running on a sunlit beach, slow-motion",
size: "16:9",
duration: 6,
quality: "720p",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "grok-imagine-1.0-video",
"prompt": "A dog running on a sunlit beach, slow-motion",
"size": "16:9",
"duration": 6,
"quality": "720p",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "grok-imagine-1.0-video",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls`
holds the generated MP4 URL, valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Mode routing
`grok-imagine-1.0-video` picks its mode from the **count of `image_urls`**
you send — there is no `mode` parameter:
| `image_urls` count | Mode | What it does |
| ------------------ | ------- | ---------------------------------------------------------------- |
| `0` (or omitted) | **T2V** | Generate from text. `size` controls output ratio. |
| `1` – `7` | **I2V** | Use the references as visual guidance. Output ratio follows ref. |
**Mutex rules.**
* In T2V (no `image_urls`), `prompt` is **required**.
* When `image_urls` is set (1 to 7 entries), `size` is forwarded but
ignored upstream — the source frame's ratio decides the output ratio.
* More than 7 `image_urls` is rejected with `400 image_urls accepts at
most 7 entries`.
***
## Request body
### `model` — required
`string`. Must be `"grok-imagine-1.0-video"`.
### `prompt` — string, conditional
Up to **4,000 characters**. Required in T2V (when `image_urls` is empty);
optional in I2V when at least one image is provided.
Empty / whitespace-only prompts are treated as missing.
**Failure modes.**
* Empty / missing in T2V → `400 prompt is required when image_urls is empty (text-to-video)` (code `20002`).
* Longer than 4,000 chars → `400 prompt exceeds 4000 characters (got N)` (code `20007`).
### `size` — string, default `"16:9"`
Output aspect ratio in T2V mode. One of:
| Value | Shape |
| ------ | ------------------- |
| `16:9` | Landscape (default) |
| `9:16` | Portrait |
| `1:1` | Square |
| `3:2` | Landscape |
| `2:3` | Portrait |
In I2V mode the upstream derives the ratio from the reference image, so
this field is ignored.
### `duration` — integer, default `6`
Output length in seconds. Any integer in `[6, 30]`. Out-of-range →
`400`. Drives pricing linearly: `ceil(per_second_usd × duration × 1000)`
credits (1 credit = $0.001).
**Send a number, not a string.** `"duration": "6"` is rejected with `400`.
### `quality` — string, default `"480p"`
`480p` (SD) or `720p` (HD). Lowercase is canonical. Quality does **not**
change the per-second rate.
### `image_urls` — string\[]
Array of public HTTP(S) URLs. **0 to 7 entries**:
* **0 entries** — pure text-to-video.
* **1 to 7 entries** — image-to-video; references guide subject and style.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — every
URL field on this endpoint must be a public HTTP(S) URL. Upload to
your own object storage (S3, R2, OSS, …) and pass the URL.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in
over time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "grok-imagine-1.0-video",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
`output.video_urls` URLs are valid for **7 days**. Re-host to your own
storage if you need them longer.
***
## Validation errors
All cases below return HTTP 400 with code `20003` unless noted. Pattern-match
on `code`, not `message` — message strings carry request-specific context
(field names, observed values, etc.) and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| -------------------------------------------------- | ------- | --------------------------------------------------------------------------- |
| `prompt` missing in T2V | `20002` | `grok-imagine: prompt is required when image_urls is empty (text-to-video)` |
| `prompt` longer than 4,000 chars | `20007` | `grok-imagine: prompt exceeds 4000 characters (got N)` |
| `image_urls` length > 7 | `20003` | `grok-imagine: image_urls accepts at most 7 entries, got N` |
| `duration` outside `[6, 30]` | `20003` | `grok-imagine: duration must be 6-30 seconds, got N` |
| Unknown `size` | `20003` | `grok-imagine: invalid size "X" (allowed: 16:9 / 9:16 / 1:1 / 3:2 / 2:3)` |
| Unknown `quality` | `20003` | `grok-imagine: invalid quality "X" (allowed: 480p / 720p)` |
| `image_urls` carrying a `data:` URI or non-http(s) | `20003` | `grok-imagine: image_urls entries must be public http(s) URLs` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` —
see [Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### T2V — minimum request
```json
{
"model": "grok-imagine-1.0-video",
"prompt": "A little girl walking down a sunset coastal road"
}
```
### T2V — full parameters
```json
{
"model": "grok-imagine-1.0-video",
"prompt": "A dog running on a sunlit beach, slow-motion, cinematic warm tones",
"size": "16:9",
"duration": 10,
"quality": "720p"
}
```
### I2V — animate from references
```json
{
"model": "grok-imagine-1.0-video",
"prompt": "Bring the scene to life with a gentle camera dolly forward",
"image_urls": ["https://your-cdn.com/reference.jpg"],
"duration": 8,
"quality": "720p"
}
```
### I2V — multi-reference
```json
{
"model": "grok-imagine-1.0-video",
"prompt": "Smooth cinematic motion across the reference subjects",
"image_urls": [
"https://your-cdn.com/ref-1.jpg",
"https://your-cdn.com/ref-2.jpg",
"https://your-cdn.com/ref-3.jpg"
],
"duration": 12
}
```
***
## Polling pattern
The task endpoint behaves identically to other video tasks — the only
difference is the completed `output` shape (`video_urls` instead of
`image_urls`). A pragmatic schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in a few minutes. The worker's wall-clock cap
is **48 hours**, comfortably above any realistic queue.
***
## Pricing
Per-second rate. `quality` does **not** change the price, and the rate is
flat across `480p` and `720p`. See current rate on the
[Grok Imagine model page](https://reapi.ai/models/grok-imagine-1-0-video).
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_second_usd × duration × 1000)
```
Failed jobs refund automatically.
***
## Tips
* **Prompt motion, not just scene.** "Slow push-in, warm tones, shallow
depth of field" outperforms a pure noun-list of what's on screen.
* **Sweet-spot duration: 6–10 seconds.** Above 10s the upstream wall-time
grows fast; the per-second price stays the same either way.
* **Reference-image quality matters.** Subject centered, clear composition,
no heavy filters — I2V output quality tracks input quality directly.
* **Send up to 7 references for richer guidance.** A first frame plus
several style boards usually outperforms a single reference.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# grok-imagine-video-1.5-official (https://reapi.ai/docs/grok-imagine-video-1-5-official)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Grok Imagine Video 1.5 **Official Channel** — async **image-to-video**
> with **native synchronized audio**, distinct from the
> [`grok-imagine-video-1.5-beta`](/docs/grok-imagine-video-1-5) channel.
> Same one-reference-image workflow at a **lower per-second rate**, with a
> slightly stricter input contract: the prompt is required and clips run
> 6 to 15 seconds. Model id `grok-imagine-video-1.5-official`. See current
> pricing on the
> [model page](https://reapi.ai/models/grok-imagine-video-1-5).
**Channel differences at a glance.** Compared to the beta channel this
surface requires a `prompt`, narrows `aspect_ratio` to `1:1` / `16:9` /
`9:16` (no `auto`), starts at 6-second clips, adds an `audio` toggle, and
has no `nsfw_checker` parameter. The two channels bill independently —
each has its own per-second rate.
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-video-1.5-official",
"prompt": "Gentle cinematic push-in, golden-hour light, subtle motion",
"image_urls": ["https://your-cdn.com/source.jpg"],
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 8
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "grok-imagine-video-1.5-official",
"prompt": "Gentle cinematic push-in, golden-hour light, subtle motion",
"image_urls": ["https://your-cdn.com/source.jpg"],
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 8,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "grok-imagine-video-1.5-official",
prompt: "Gentle cinematic push-in, golden-hour light, subtle motion",
image_urls: ["https://your-cdn.com/source.jpg"],
aspect_ratio: "16:9",
resolution: "720p",
duration: 8,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "grok-imagine-video-1.5-official",
"prompt": "Gentle cinematic push-in, golden-hour light, subtle motion",
"image_urls": []string{"https://your-cdn.com/source.jpg"},
"aspect_ratio": "16:9",
"resolution": "720p",
"duration": 8,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "grok-imagine-video-1.5-official",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
generated MP4 URL (with audio), valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
This is an **image-to-video** model — exactly one reference image is required
on every request. There is no text-to-video mode, no last-frame or reference
image input, and no `mode` parameter.
***
## Request body
### `model` — required
`string`. Must be `"grok-imagine-video-1.5-official"`.
### `prompt` — string, required
Describes the motion, camera, atmosphere, and any dialogue or sound you want
in the generated audio. **Required on this channel** (the beta channel accepts
image-only requests; this one does not). Maximum **4000 characters**;
whitespace-only prompts and prompts over 4000 characters are rejected with
`400`.
### `image_urls` — string\[], required
Array with **exactly one** public HTTP(S) image URL — the first frame the
model animates. Accepted source formats: JPEG, PNG, WebP.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — the image URL
must be a public HTTP(S) link. Upload to your own object storage (S3, R2, OSS, …)
and pass the URL. More than one entry is rejected.
### `aspect_ratio` — string, default `"16:9"`
Output framing. `auto` is **not** available on this channel. Output pixel
size follows the ratio × resolution matrix:
| Value | 480p | 720p |
| ------ | ------- | -------- |
| `1:1` | 544×544 | 960×960 |
| `16:9` | 736×400 | 1280×720 |
| `9:16` | 400×736 | 720×1280 |
### `resolution` — string, default `"480p"`
`480p` or `720p`. `720p` costs 2× per second — see Pricing.
### `duration` — integer, default `8`
Output length in seconds. Any integer in `[6, 15]` — note the 6-second
minimum (the beta channel starts at 1). Out-of-range → `400`. Drives pricing
linearly.
**Send a number, not a string.** `"duration": "8"` is rejected with `400`.
### `audio` — boolean, default `true`
Whether the generated clip carries a native synchronized audio track. Set
`false` for a silent video at the same per-second rate.
Each request produces exactly **one** video. For multiple takes, submit
concurrent requests — each bills and refunds independently.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "grok-imagine-video-1.5-official",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
`output.video_urls` URLs are valid for **7 days** — re-host to your own
storage if you need them longer.
***
## Validation errors
Pattern-match on `code`, not `message` — message strings carry request-specific
context (field names, observed values) and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| -------------------------------------------------- | ------- | ------------------------------------------------ |
| `prompt` missing, empty, or whitespace-only | `20002` | `prompt is required` |
| `prompt` longer than 4000 characters | `20003` | `prompt must be at most 4000 characters` |
| `image_urls` missing | `20002` | `image_urls is required` |
| `image_urls` with more than one entry | `20003` | `image_urls allows at most 1 entry` |
| `duration` outside `[6, 15]` | `20003` | `duration must be 6-15 seconds` |
| Unknown `resolution` | `20003` | `invalid resolution (allowed: 480p / 720p)` |
| `aspect_ratio` outside `1:1` / `16:9` / `9:16` | `20003` | `invalid aspect_ratio` |
| `image_urls` carrying a `data:` URI or non-http(s) | `20003` | `image_urls entries must be public http(s) URLs` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` — see
the [Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Minimum request
```json
{
"model": "grok-imagine-video-1.5-official",
"prompt": "Slow push-in with ambient room tone",
"image_urls": ["https://your-cdn.com/source.jpg"]
}
```
### Directed motion + HD
```json
{
"model": "grok-imagine-video-1.5-official",
"prompt": "Slow dolly forward, wind moving the hair, warm cinematic light",
"image_urls": ["https://your-cdn.com/portrait.jpg"],
"resolution": "720p",
"duration": 10
}
```
### Vertical social clip, no audio
```json
{
"model": "grok-imagine-video-1.5-official",
"prompt": "Energetic motion, quick handheld feel",
"image_urls": ["https://your-cdn.com/poster.jpg"],
"aspect_ratio": "9:16",
"resolution": "720p",
"duration": 6,
"audio": false
}
```
***
## Polling pattern
The task endpoint behaves identically to other video tasks. A pragmatic
schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in about a minute. The worker's wall-clock cap is
**48 hours**, comfortably above any realistic queue.
***
## Pricing
Per-second rate by **resolution** — `720p` costs 2× the `480p` rate. This
channel is priced independently of (and lower than) the beta channel. See
current rates on the
[Grok Imagine Video 1.5 model page](https://reapi.ai/models/grok-imagine-video-1-5).
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_second_usd × duration × 1000)
```
Failed jobs refund automatically.
***
## Tips
* **Write the prompt like a shot direction.** It is required here — lean into
it: camera move, subject motion, atmosphere, and the sound you want.
* **One clear subject image works best.** A centered, well-lit reference with
clean composition tracks identity and motion far better than a busy frame.
* **Sweet-spot duration: 6–10 seconds.** Longer clips raise wall-time without
changing the per-second rate.
* **Pick `720p` for hero shots, `480p` to save.** The 720p rate is exactly 2×
the 480p rate on this channel.
***
## Related
* [grok-imagine-video-1.5-beta](/docs/grok-imagine-video-1-5)
* [grok-imagine-1.0-video](/docs/grok-imagine-1-0-video)
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# grok-imagine-video-1.5-beta (https://reapi.ai/docs/grok-imagine-video-1-5)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Grok Imagine Video 1.5 — async **image-to-video** with **native
> synchronized audio**. Pass exactly one reference image (and an optional
> prompt) and the model returns a realistic clip with lifelike motion at
> 480p or 720p, 1 to 15 seconds. Model id `grok-imagine-video-1.5-beta`.
> See current pricing on the
> [model page](https://reapi.ai/models/grok-imagine-video-1-5).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-video-1.5-beta",
"prompt": "Gentle cinematic push-in, golden-hour light, subtle motion",
"image_urls": ["https://your-cdn.com/source.jpg"],
"resolution": "720p",
"duration": 8
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "grok-imagine-video-1.5-beta",
"prompt": "Gentle cinematic push-in, golden-hour light, subtle motion",
"image_urls": ["https://your-cdn.com/source.jpg"],
"resolution": "720p",
"duration": 8,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "grok-imagine-video-1.5-beta",
prompt: "Gentle cinematic push-in, golden-hour light, subtle motion",
image_urls: ["https://your-cdn.com/source.jpg"],
resolution: "720p",
duration: 8,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "grok-imagine-video-1.5-beta",
"prompt": "Gentle cinematic push-in, golden-hour light, subtle motion",
"image_urls": []string{"https://your-cdn.com/source.jpg"},
"resolution": "720p",
"duration": 8,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "grok-imagine-video-1.5-beta",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
generated MP4 URL (with audio), valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
This is an **image-to-video** model — exactly one reference image is required
on every request. There is no text-to-video mode and no `mode` parameter.
***
## Request body
### `model` — required
`string`. Must be `"grok-imagine-video-1.5-beta"`.
### `image_urls` — string\[], required
Array with **exactly one** public HTTP(S) image URL — the frame the model
animates. Accepted source formats: JPEG, PNG, WebP.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — the image URL
must be a public HTTP(S) link. Upload to your own object storage (S3, R2, OSS, …)
and pass the URL. More than one entry is rejected.
### `prompt` — string, optional
Up to **4,096 characters**. Optional — the reference image alone will animate.
Use it to direct motion, camera, atmosphere, and any dialogue or sound you want
in the generated audio.
### `aspect_ratio` — string, default `"auto"`
Output framing. `auto` follows the source image's size. One of:
| Value | Shape |
| ------ | ----------------------------- |
| `auto` | Follow source image (default) |
| `1:1` | Square |
| `16:9` | Landscape |
| `9:16` | Portrait |
| `4:3` | Landscape |
| `3:4` | Portrait |
| `3:2` | Landscape |
| `2:3` | Portrait |
### `resolution` — string, default `"480p"`
`480p` or `720p`. Unlike a flat-rate model, `720p` costs more per second than
`480p` — see Pricing.
### `duration` — integer, default `8`
Output length in seconds. Any integer in `[1, 15]`. Out-of-range → `400`.
Drives pricing linearly.
**Send a number, not a string.** `"duration": "8"` is rejected with `400`.
### `nsfw_checker` — boolean, default `true`
Content safety filter. Defaults to `true`. You can set it to `false` to relax
filtering for your own request.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "grok-imagine-video-1.5-beta",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
The generated MP4 carries the native audio track. `output.video_urls` URLs are
valid for **7 days** — re-host to your own storage if you need them longer.
***
## Validation errors
Pattern-match on `code`, not `message` — message strings carry request-specific
context (field names, observed values) and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| -------------------------------------------------- | ------- | ------------------------------------------------ |
| `image_urls` missing | `20002` | `image_urls is required` |
| `image_urls` with more than one entry | `20003` | `image_urls allows at most 1 entry` |
| `prompt` longer than 4,096 chars | `20007` | `prompt must be at most 4096 characters` |
| `duration` outside `[1, 15]` | `20003` | `duration must be 1-15 seconds` |
| Unknown `resolution` | `20003` | `invalid resolution (allowed: 480p / 720p)` |
| Unknown `aspect_ratio` | `20003` | `invalid aspect_ratio` |
| `image_urls` carrying a `data:` URI or non-http(s) | `20003` | `image_urls entries must be public http(s) URLs` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` — see
the [Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Minimum request — image only
```json
{
"model": "grok-imagine-video-1.5-beta",
"image_urls": ["https://your-cdn.com/source.jpg"]
}
```
### Directed motion + HD
```json
{
"model": "grok-imagine-video-1.5-beta",
"prompt": "Slow dolly forward, wind moving the hair, warm cinematic light",
"image_urls": ["https://your-cdn.com/portrait.jpg"],
"resolution": "720p",
"duration": 10
}
```
### Vertical social clip
```json
{
"model": "grok-imagine-video-1.5-beta",
"prompt": "Energetic motion with ambient street sound",
"image_urls": ["https://your-cdn.com/poster.jpg"],
"aspect_ratio": "9:16",
"resolution": "720p",
"duration": 6
}
```
***
## Polling pattern
The task endpoint behaves identically to other video tasks. A pragmatic
schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in a few minutes. The worker's wall-clock cap is
**48 hours**, comfortably above any realistic queue.
***
## Channels
Grok Imagine Video 1.5 ships in two independent channels. You pick a channel
by switching the `model` id you POST; each bills at its own per-second rate
and neither falls back to the other.
| Channel | `model` id | prompt | duration | aspect\_ratio | extras |
| -------------------- | --------------------------------- | -------- | -------- | ----------------------- | -------------- |
| Official *(default)* | `grok-imagine-video-1.5-official` | required | 6–15 s | `1:1` / `16:9` / `9:16` | `audio` toggle |
| Beta | `grok-imagine-video-1.5-beta` | optional | 1–15 s | `auto` + 7 ratios | `nsfw_checker` |
Both are image-to-video with exactly one reference image and native audio;
submit/poll envelope, task id format, retention (7 days), and error codes are
identical. The official channel has the lower per-second rate — see the
[full official-channel reference](/docs/grok-imagine-video-1-5-official).
***
## Pricing
Per-second rate by **resolution** — `720p` costs more per second than `480p`.
See current rates on the
[Grok Imagine Video 1.5 model page](https://reapi.ai/models/grok-imagine-video-1-5).
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_second_usd × duration × 1000)
```
Failed jobs refund automatically.
***
## Tips
* **One clear subject image works best.** A centered, well-lit reference with
clean composition tracks identity and motion far better than a busy frame.
* **Prompt the motion and the sound.** "Slow push-in, ambient room tone, soft
breathing" gives the native audio something to lock onto, not just the video.
* **Sweet-spot duration: 5–10 seconds.** Longer clips raise wall-time without
changing the per-second rate.
* **Pick `720p` for hero shots, `480p` to save.** Resolution changes the
per-second cost on this model, unlike flat-rate siblings.
***
## Related
* [grok-imagine-video-1.5-official](/docs/grok-imagine-video-1-5-official)
* [grok-imagine-1.0-video](/docs/grok-imagine-1-0-video)
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# happyhorse-1.0-official (https://reapi.ai/docs/happyhorse-1-0-official)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Happy Horse 1.0 **Official Channel** — direct first-party routing,
> distinct from the standard [`happyhorse-1.0`](/docs/happyhorse-1-0)
> channel. It uses the official Aliyun Model Studio SKUs behind
> one ReAPI model id and keeps ReAPI's URL-only media contract.
**Official channel, ReAPI boundary.** The upstream provider is Aliyun
Model Studio first-party. Media inputs must still be public `http(s)` URLs;
base64/data URI media is not accepted by ReAPI even where upstream docs show
base64 examples.
Watermark policy is also a ReAPI override: Aliyun's official default is
`true`, but ReAPI defaults `watermark` to `false`. Send `"watermark": true`
if you want the upstream watermark.
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.0-official",
"prompt": "A coastal road at sunset, slow-motion camera push-in, cinematic feel",
"resolution": "1080P",
"size": "4:5",
"duration": 5,
"seed": 42
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "happyhorse-1.0-official",
"prompt": "A coastal road at sunset, slow-motion camera push-in",
"resolution": "1080P",
"size": "4:5",
"duration": 5,
"seed": 42,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "happyhorse-1.0-official",
prompt: "A coastal road at sunset, slow-motion camera push-in",
resolution: "1080P",
size: "4:5",
duration: 5,
seed: 42,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "happyhorse-1.0-official",
"prompt": "A coastal road at sunset, slow-motion camera push-in",
"resolution": "1080P",
"size": "4:5",
"duration": 5,
"seed": 42,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Request surface
The public endpoint and async response envelope are the same as the standard
channel:
```txt
POST /api/v1/videos/generations
```
One `model` id auto-routes by request shape:
| Shape | Official upstream SKU |
| ---------------------------------------------------- | --------------------------- |
| `prompt` only | `happyhorse-1.0-t2v` |
| `first_frame_image` (+ optional `prompt`) | `happyhorse-1.0-i2v` |
| `image_urls` 1-9 + `prompt` | `happyhorse-1.0-r2v` |
| `video_url` (+ optional `image_urls` 0-5) + `prompt` | `happyhorse-1.0-video-edit` |
`first_frame_image`, `image_urls`, and `video_url` are mutually exclusive
except for the legal EDIT combination: `video_url + image_urls`.
## Official-channel constraints
| Field | Official channel behavior |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | Must be `"happyhorse-1.0-official"` |
| `prompt` | Required for T2V / R2V / EDIT, optional for I2V. Aliyun truncates over-length prompt text upstream; ReAPI does not reject official-channel prompts at the standard-channel 2500-character cap. |
| `first_frame_image` | Public `http(s)` URL only. Base64/data URI is rejected by ReAPI. |
| `image_urls` | Public `http(s)` URLs only. R2V accepts 1-9 images; EDIT accepts 0-5 reference images. |
| `video_url` | Public `http(s)` URL only. EDIT source video must be 3-60 seconds, long side ≤2160 px, short side ≥320 px, and aspect ratio between 1:2.5 and 2.5:1. Aliyun also enforces file size ≤100 MB and fps >8. |
| `resolution` | `"720P"` or `"1080P"`; omitted lets upstream apply its default. |
| `size` | T2V/R2V ratio: `"16:9"`, `"9:16"`, `"1:1"`, `"4:3"`, `"3:4"`, `"4:5"`, `"5:4"`. Ignored for I2V and EDIT because output shape follows the source media. |
| `duration` | 3-15 seconds for T2V/I2V/R2V. Ignored for EDIT because output length follows the source video. |
| `watermark` | ReAPI default is `false` even though Aliyun's official default is `true`. Explicit `true` / `false` is honored. |
| `seed` | Integer from 0 to 2147483647. |
| `audio_setting` | EDIT only: `"auto"` or `"origin"`. |
## Differences from `happyhorse-1.0`
| Aspect | `happyhorse-1.0` | `happyhorse-1.0-official` |
| ----------------------- | ----------------------------------- | ----------------------------------------------------------------------- |
| Upstream channel | Standard | Aliyun Model Studio official |
| Wire `model` value | `"happyhorse-1.0"` | `"happyhorse-1.0-official"` |
| T2V/R2V ratios | `16:9`, `9:16`, `1:1`, `4:3`, `3:4` | Adds `4:5` and `5:4` |
| Prompt cap | ReAPI rejects above 2500 chars | Aliyun truncation semantics; no local 2500-char reject |
| EDIT video dimensions | Standard channel validation rules | Official Aliyun validation rules |
| Default watermark | ReAPI default `false` | ReAPI default `false` (intentional override from Aliyun default `true`) |
| Per-second rate | Baseline | **10% off** the baseline |
| Validation error prefix | `happyhorse:` | `happyhorse-official:` |
***
## Pricing
Per-second × resolution. **10% off** the standard channel at every
supported resolution. See current 720P / 1080P / 4K rates on the
[Happy Horse model page](https://reapi.ai/models/happyhorse-1-0).
**Bill formula** (1 credit = $0.001):
| Mode | Billable seconds |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| T2V / I2V / R2V | `duration` you sent (default 5) |
| EDIT | `ceil(probed_source_seconds) + min(ceil(probed_source_seconds), 15)` — vendor processes the source AND produces a capped output; both sides are billable. Failed probe → `400 PRICING_UNAVAILABLE`, no charge. |
Final bill: `ceil(per_second_usd_after_discount × billable_seconds × 1000)` credits.
Failed jobs refund automatically.
### Examples
| Source video | Mode | Billable seconds |
| ------------ | ------------------ | --------------------- |
| — | T2V, `duration=5` | 5 |
| — | T2V, `duration=10` | 10 |
| 5s clip | EDIT | 5 + min(5, 15) = 10 |
| 8.3s clip | EDIT | 9 + min(9, 15) = 18 |
| 60s clip | EDIT | 60 + min(60, 15) = 75 |
| 20s clip | EDIT | 20 + min(20, 15) = 35 |
Apply `ceil(per_second_usd_after_discount × billable_seconds × 1000)` for
the credit charge.
***
## Validation errors
The response envelope and numeric error-code system match the standard
channel, but official-channel validation follows the official Aliyun rules
listed above. Error messages from this channel use the
`happyhorse-official:` prefix.
Pattern-match on the numeric `code`, not the message string.
***
## When to pick which channel
| Need | Channel |
| --------------------------------------------- | ------------------------- |
| Lowest unit price | `happyhorse-1.0-official` |
| Official Aliyun Model Studio route | `happyhorse-1.0-official` |
| `size` values `4:5` or `5:4` | `happyhorse-1.0-official` |
| Existing integration with no migration | `happyhorse-1.0` |
| Standard channel route / existing constraints | `happyhorse-1.0` |
Switching channels = changing the `model` string in your request body.
The response envelope and polling flow are shared; provider-specific
validation and pricing differ as documented above.
***
## Related
* [`happyhorse-1.0` — full reference](/docs/happyhorse-1-0)
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Aliyun official T2V reference](https://help.aliyun.com/zh/model-studio/happyhorse-text-to-video-api-reference)
* [Aliyun official I2V reference](https://help.aliyun.com/zh/model-studio/happyhorse-image-to-video-api-reference)
* [Aliyun official R2V reference](https://help.aliyun.com/zh/model-studio/happyhorse-reference-to-video-api-reference)
* [Aliyun official EDIT reference](https://help.aliyun.com/zh/model-studio/happyhorse-video-edit-api-reference)
---
# happyhorse-1.0 (https://reapi.ai/docs/happyhorse-1-0)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Alibaba Cloud Bailian Happy Horse 1.0 — a single async video endpoint that
> auto-routes between **T2V / I2V / R2V / EDIT** based on which media field
> your request carries. 720P or 1080P, 3–15 second outputs, billed only by
> resolution × duration regardless of mode. See current pricing on the
> [model page](https://reapi.ai/models/happyhorse-1-0).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1.0",
"prompt": "A coastal road at sunset, slow-motion camera push-in, cinematic feel",
"resolution": "1080P",
"size": "16:9",
"duration": 5,
"seed": 42
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "happyhorse-1.0",
"prompt": "A coastal road at sunset, slow-motion camera push-in",
"resolution": "1080P",
"size": "16:9",
"duration": 5,
"seed": 42,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "happyhorse-1.0",
prompt: "A coastal road at sunset, slow-motion camera push-in",
resolution: "1080P",
size: "16:9",
duration: 5,
seed: 42,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "happyhorse-1.0",
"prompt": "A coastal road at sunset, slow-motion camera push-in",
"resolution": "1080P",
"size": "16:9",
"duration": 5,
"seed": 42,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "happyhorse-1.0",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls`
holds the generated MP4 URLs, valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Mode routing
`happyhorse-1.0` is **one model id** that picks its mode from which media
fields you set:
| Fields you send | Mode | What it does |
| --------------------------------------------------------------------- | -------- | ----------------------------------------- |
| `prompt` only | **T2V** | Generate from text |
| `prompt` + `first_frame_image` | **I2V** | Animate starting from a still frame |
| `prompt` + `image_urls` (1–9) | **R2V** | Compose a new scene from reference images |
| `video_url` (+ `prompt`, optional `image_urls` ≤ 5 / `audio_setting`) | **EDIT** | Restyle / rewrite an existing clip |
Resolution priority when multiple fields are set:
`video_url` > `first_frame_image` > `image_urls` > `prompt`-only.
**Mutex rule.** The three media fields (`first_frame_image`, `image_urls`,
`video_url`) are mutually exclusive **in pairs**. The single legal pairing
is `video_url + image_urls` (EDIT with style references). Any other
combination is rejected with `400 first_frame_image and image_urls
cannot be combined` or `400 first_frame_image and video_url cannot be
combined` (code `20003`).
***
## Request body
### `model` — required
`string`. Always `"happyhorse-1.0"`.
### `prompt` — string
Up to **2,500 characters**. Required in T2V / R2V / EDIT; optional in I2V
(but recommended — phrasing the camera move and motion improves output
quality).
**Failure modes.**
* Missing in non-I2V → `400 prompt is required in T2V / R2V / EDIT modes` (code `20002`).
* Longer than 2,500 chars → `400 prompt must be at most 2500 characters` (code `20007`).
### `first_frame_image` — string
Public HTTP(S) URL pointing at the still you want animated. Triggers
**I2V** when set. Mutually exclusive with `image_urls` and `video_url`.
Recommended source asset: JPEG / PNG / BMP / WEBP, short side ≥ 300px,
ratio between `1:2.5` and `2.5:1`, ≤ 10MB. The upstream model
also enforces these — the gateway forwards the URL untouched, so
non-compliant assets surface as a delayed `provider_failed` rather than
an immediate 400. Validate before submitting if the source isn't yours.
**No `data:` URIs.** reapi rejects base64 inputs platform-wide. Upload to
public storage (your own CDN, S3, R2…) and pass the URL.
### `image_urls` — string\[]
Array of public HTTP(S) URLs.
* **R2V** (no `video_url`): **1–9** entries. The upstream composes a fresh
scene that incorporates the supplied subjects / styles.
* **EDIT** (`video_url` set): **0–5** entries acting as style references
on top of the source video. Sending more than 5 is rejected with
`400 image_urls allows at most 5 entries in EDIT mode`.
Same per-asset constraints as `first_frame_image` (short side ≥ 720px is
recommended for R2V, short / long ≥ 0.4, ≤ 10MB each). T2V and I2V
requests are **rejected** if `image_urls` is present — the upstream
wouldn't consume it, and silently dropping it would make billing
surprising. Mutually exclusive with `first_frame_image`.
### `video_url` — string
Public HTTP(S) URL of the source clip. Triggers **EDIT** mode. Combinable
with `image_urls` (≤ 5) and `audio_setting`. Mutually exclusive with
`first_frame_image`.
Source asset shape (enforced upstream): MP4 / MOV (H.264 recommended),
duration 3–60 seconds, ≥ 480p / short side ≥ 360, ratio between `1:8`
and `8:1`, > 8 fps, ≤ 100MB.
**Length caps the output.** If the source video runs longer than 15s,
the upstream truncates from second 0 to second 15 before processing.
The output clip's length **matches the source's processed length** —
trim the input yourself if you need a different segment or a tighter
total. In EDIT mode the `duration` parameter is **ignored for both
generation and billing**; reapi probes the source clip server-side and
charges
`(ceil(probed_seconds) + min(ceil(probed_seconds), 15)) × per_second_usd`
— input and capped output are both billable. See the `duration` field
below.
### `audio_setting` — string, default `"auto"`
Only valid in EDIT mode. Sending it without `video_url` is rejected with
`400 audio_setting requires video_url (EDIT mode only)`.
| Value | Behavior |
| ---------- | -------------------------------------- |
| `"auto"` | Generate a fresh audio track (default) |
| `"origin"` | Keep the source video's original audio |
### `resolution` — string, default `"1080P"`
`"720P"` or `"1080P"`. Drives pricing — every other parameter is free
across the two tiers. Lowercase forms (`"720p"`, `"1080p"`) are accepted
and normalized for symmetry with seedance; the upstream sees the
canonical uppercase form.
### `size` — string, default `"16:9"`
Output ratio for **T2V and R2V only**. One of `16:9` / `9:16` /
`1:1` / `4:3` / `3:4`.
In **I2V** and **EDIT**, the upstream derives the output ratio from the
input media (first-frame image / source video) and ignores `size`. The
gateway still accepts the field — it's just inert.
### `duration` — integer, default `5`
Output length in seconds for **T2V / I2V / R2V**. Any integer in
`[3, 15]`. Drives pricing linearly:
`ceil(per_second_usd × duration × 1000)` credits (1 credit = $0.001).
**EDIT mode ignores `duration` for billing.** When `video_url` is set,
reapi probes the source video's actual length on the server (ffmpeg
metadata) and bills both the input clip AND the (capped) output:
```
billable = ceil(probed_seconds) + min(ceil(probed_seconds), 15)
bill_usd = billable × per_second_usd
```
A 5s source bills 10s; a 60s source bills 75s (60 + 15-cap output).
Whatever you send in `duration` is ignored for cost. The upstream also
ignores `duration` here (output length tracks the source). Probe
failures return `400 PRICING_UNAVAILABLE` (code `30002`) with no charge.
### `watermark` — boolean, default `false`
Adds the platform watermark to the generated clip when `true`.
### `seed` — integer
Reproducibility hint. Range `[0, 2147483647]`. Same seed plus an
otherwise identical request returns a similar (not bit-for-bit
identical) result. Omit for full randomness.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in
over time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "happyhorse-1.0",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
`output.video_urls` URLs are valid for **7 days**. Re-host to your own
storage if you need them longer.
***
## Validation errors
All cases below return HTTP 400 with code `20003` unless noted. Pattern-match
on `code`, not `message` — message strings carry request-specific context
(field names, observed dimensions, etc.) and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| ----------------------------------------------------- | ------- | --------------------------------------------------------------------------------- |
| `prompt` missing in T2V / R2V / EDIT | `20002` | `happyhorse: prompt is required in T2V / R2V / EDIT modes` |
| `prompt` longer than 2,500 chars | `20007` | `happyhorse: prompt must be at most 2500 characters` |
| `first_frame_image` + `image_urls` together | `20003` | `happyhorse: first_frame_image and image_urls cannot be combined` |
| `first_frame_image` + `video_url` together | `20003` | `happyhorse: first_frame_image and video_url cannot be combined` |
| `image_urls` empty array in R2V | `20003` | `happyhorse: image_urls contained no usable URLs` |
| `image_urls` > 5 in EDIT | `20003` | `happyhorse: image_urls allows at most 5 entries in EDIT mode` |
| `audio_setting` set without `video_url` | `20003` | `happyhorse: audio_setting requires video_url (EDIT mode only)` |
| `image_urls` carrying a `data:` URI | `20003` | `happyhorse: image_urls must be public http(s) URLs` |
| `video_url` carrying a `data:` URI | `20003` | `happyhorse: video_url must be a public http(s) URL` |
| EDIT source duration \< 3s or > 60s | `20003` | `video_url duration X.XXs is out of range (must be 3–60s)` |
| EDIT source resolution below 480p (short side \< 360) | `20003` | `video_url resolution WxH is below the 480p minimum (short side must be ≥ 360px)` |
| EDIT source ratio outside 1:8–8:1 | `20003` | `video_url aspect ratio WxH is out of range (must be between 1:8 and 8:1)` |
| EDIT source video probe fails (network / format) | `30002` | `Could not determine source video duration for billing: …` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` —
see [Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### T2V — minimum request
```json
{
"model": "happyhorse-1.0",
"prompt": "A little girl walking down the road, cinematic feel"
}
```
### T2V — full parameters
```json
{
"model": "happyhorse-1.0",
"prompt": "Sunset coastal road, slow camera push-in, cinematic warm tones",
"resolution": "1080P",
"size": "16:9",
"duration": 8,
"watermark": false,
"seed": 42
}
```
### I2V — first-frame animation
```json
{
"model": "happyhorse-1.0",
"prompt": "Bring the scene to life with gentle camera dolly forward",
"first_frame_image": "https://your-cdn.com/first_frame.png",
"resolution": "1080P",
"duration": 5
}
```
### R2V — multiple references
```json
{
"model": "happyhorse-1.0",
"prompt": "The protagonist from image 1 runs through image 2's scene then picks up the prop from image 3. 3D cartoon style, smooth motion.",
"image_urls": [
"https://your-cdn.com/img_01.jpg",
"https://your-cdn.com/img_02.png",
"https://your-cdn.com/img_03.jpeg"
],
"resolution": "1080P",
"size": "16:9",
"duration": 5
}
```
### EDIT — keep original audio + style reference
```json
{
"model": "happyhorse-1.0",
"prompt": "Repaint the character in a 3D cartoon style, keep the original motion",
"video_url": "https://your-cdn.com/source.mp4",
"image_urls": ["https://your-cdn.com/style_ref.jpg"],
"resolution": "1080P",
"audio_setting": "origin",
"seed": 42
}
```
### 720P — cost-conscious
```json
{
"model": "happyhorse-1.0",
"prompt": "Waves crashing on a beach at sunset, wide shot",
"resolution": "720P",
"size": "16:9",
"duration": 5
}
```
***
## Choosing a mode
| Need | Send |
| ----------------------------------------- | ------------------------------------------------------------------- |
| Generate from text | `prompt` only (T2V) |
| Animate a still | `prompt + first_frame_image` (I2V) |
| Compose a new scene from reference images | `prompt + image_urls` 1–9 (R2V) |
| Restyle / rewrite an existing clip | `video_url` (+ `prompt`, optional refs ≤ 5, `audio_setting`) (EDIT) |
| Cut spend in half | Set `resolution: "720P"` |
***
## Polling pattern
The task endpoint behaves identically to image tasks — the only
difference is the completed `output` shape (`video_urls` instead of
`image_urls`). A pragmatic schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in a few minutes. The worker's wall-clock cap
is **48 hours**, comfortably above any realistic queue.
***
## Channels
`happyhorse-1.0` ships in three channels. The wire schema is identical;
you pick a channel by switching the `model` id you POST. Pricing,
upstream routing, and a few feature corners differ — same input you
send to any of them, just at different cost / quality / latency.
| Channel | `model` id | Pricing | 4K | mask | negative prompt |
| -------------------- | ------------------------- | -------- | -- | ---- | --------------- |
| Standard *(default)* | `happyhorse-1.0` | baseline | — | — | — |
| Official *(-10%)* | `happyhorse-1.0-official` | 0.9 × | ✓ | — | — |
Both accept the **same request body** documented in this page —
`prompt`, `first_frame_image`, `image_urls`, `video_url`,
`audio_setting`, `resolution`, `size`, `duration`, `seed`, `watermark`.
The only client-visible difference at the schema level is:
* **Official channel**: adds 2 extra aspect ratios (`4:5`, `5:4`) and
supports `4K` resolution. `watermark` is forced `false`.
Submit/poll envelope is identical across channels — task id format,
`status` enum, `output.video_urls` shape. Polling rate, retention
(7 days for video URLs), and validation error codes are the same.
Pick by use case:
* **Standard** is the default for new integrations — predictable
latency, full feature surface (only one that supports R2V image
cardinality up to 9).
* **Official** if you need 4K output, the wider aspect-ratio set, or
the 10% discount applies to your volume.
***
## Pricing
Per-second × resolution. Mode does **not** change the rate. Both channels
share the same parameter shape:
* **Standard** — `happyhorse-1.0`, full feature surface.
* **Official** — `happyhorse-1.0-official`, cheaper than Standard,
supports 4K (see official-only doc).
See current 720P / 1080P / 4K rates per channel on the
[Happy Horse model page](https://reapi.ai/models/happyhorse-1-0).
**Bill formula** (1 credit = $0.001):
| Mode | Billable seconds |
| --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| T2V / I2V / R2V | `duration` you sent (default 5) |
| EDIT | `ceil(server_probed_source_seconds) + min(ceil(server_probed_source_seconds), 15)` — input and capped output both billable. `duration` is **ignored** in EDIT; server measures the uploaded clip. Failed probe → `400 PRICING_UNAVAILABLE`, no charge |
Final bill: `ceil(per_second_usd × billable_seconds × 1000)` credits.
Failed jobs refund automatically.
***
## Tips
* **Prompt motion, not just scene.** "Slow push-in, warm tones, shallow
depth of field" outperforms a pure noun-list of what's on screen.
* **Sweet-spot duration: 5–10 seconds.** Below 5s motion looks choppy;
above 10s upstream wall-time grows fast.
* **First-frame quality matters.** Subject centered, clear composition,
no heavy filters — I2V output quality tracks input quality directly.
* **Pre-trim EDIT sources.** > 15s clips get auto-truncated to the
first 15s upstream **and capped at 15s for billing**. Slice the input
yourself if you want a different segment.
* **EDIT cost = source length.** A 5s source bills 5s; an 8s source
bills 8s; a 60s source bills 15s (the cap). The playground's draft
estimate may show a default before the upload finishes — the
authoritative number is computed at submit after the server probes
the file.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# happyhorse-1-1 (https://reapi.ai/docs/happyhorse-1-1)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Alibaba HappyHorse 1.1 — a single async video endpoint that auto-routes
> between **T2V / I2V / R2V** based on which media field your request carries.
> 720p or 1080p, 3–15 second outputs, billed only by resolution × duration
> regardless of mode. See current pricing on the
> [model page](https://reapi.ai/models/happyhorse-1-1).
Generation is **asynchronous**: the POST returns a task id, then poll
[`GET /api/v1/tasks/{id}`](/docs/api/tasks) until `status` is `completed`.
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-1-1",
"prompt": "A coastal road at sunset, slow-motion camera push-in, cinematic feel",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 5
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "happyhorse-1-1",
"prompt": "A coastal road at sunset, slow-motion camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 5,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "happyhorse-1-1",
prompt: "A coastal road at sunset, slow-motion camera push-in",
resolution: "1080p",
aspect_ratio: "16:9",
duration: 5,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "happyhorse-1-1",
"prompt": "A coastal road at sunset, slow-motion camera push-in",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var out map[string]any
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out)
}
```
## Endpoint
```http
POST /api/v1/videos/generations
Authorization: Bearer rk_live_xxx
Content-Type: application/json
```
Submitting returns a task id; poll [`GET /api/v1/tasks/{id}`](/docs/api/tasks)
for the result. Polling does not consume credits.
## Parameters
| Parameter | Type | Required | Default | Description |
| ---------------------- | --------- | ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model` | string | yes | — | `happyhorse-1-1` |
| `prompt` | string | T2V / R2V: yes · I2V: no | — | Any language. ≤5000 non-CJK / ≤2500 CJK characters (over-length is auto-truncated upstream). In R2V, reference images with `[Image 1]`, `[Image 2]`, … |
| `image_urls` | string\[] | I2V | — | First-frame image, **exactly 1** public URL. Presence selects **image-to-video**. JPEG/PNG/WEBP, short side ≥300px, ratio 1:2.5–2.5:1, ≤20MB. |
| `reference_image_urls` | string\[] | R2V | — | **1–9** reference image URLs. Presence selects **reference-to-video**. JPEG/PNG/WEBP, short side ≥400px, ≤20MB. |
| `resolution` | enum | no | `720p` | `720p` · `1080p` |
| `aspect_ratio` | enum | no | `16:9` | `16:9` `9:16` `3:4` `4:3` `4:5` `5:4` `1:1` `9:21` `21:9`. **T2V / R2V only** — in I2V the orientation is derived from the source image. |
| `duration` | integer | no | `5` | Output length in seconds, `3`–`15`. |
`image_urls` (I2V) and `reference_image_urls` (R2V) are **mutually exclusive**.
All media inputs must be public HTTP(S) URLs — base64 / `data:` URIs are rejected.
## Modes
Mode is implicit — selected by which inputs you send:
| Mode | Trigger | prompt | aspect\_ratio |
| ---------------------------- | ---------------------------- | -------- | --------------- |
| **Text-to-video (T2V)** | no media | required | yes |
| **Image-to-video (I2V)** | `image_urls` (1) | optional | no (from image) |
| **Reference-to-video (R2V)** | `reference_image_urls` (1–9) | required | yes |
```json
// I2V — animate a first frame
{ "model": "happyhorse-1-1", "image_urls": ["https://…/frame.jpg"], "resolution": "1080p", "duration": 5 }
// R2V — keep subjects consistent across the clip
{ "model": "happyhorse-1-1", "prompt": "the woman in [Image 1] walks through [Image 2]", "reference_image_urls": ["https://…/a.jpg", "https://…/b.jpg"], "resolution": "1080p", "duration": 5 }
```
## Pricing
Billed by **per-second rate × resolution × duration**; the routing mode does
not change the rate. Two tiers: `720p` and `1080p`. See the
[model page](https://reapi.ai/models/happyhorse-1-1) for current rates.
**Bill formula** (`1 credit = $0.001`):
```
credits = ceil(per_second_usd × duration × 1000)
```
## Output
On success, `GET /api/v1/tasks/{id}` returns:
```json
{
"id": "task_…",
"model": "happyhorse-1-1",
"status": "completed",
"output": { "video_urls": ["https://cdn.reapi.ai/media/tasks/…/0.mp4"] },
"error": null
}
```
## Errors
| HTTP | `code` | When |
| ---- | ----------------- | ------------------------------------------------------------------------------------ |
| 400 | `20002` | Missing / invalid parameter (e.g. prompt required in T2V/R2V, both image fields set) |
| 401 | `10001` – `10005` | Auth missing / invalid / revoked |
| 402 | `30001` | Insufficient credits |
| 429 | `50001` | Per-user rate limit exceeded |
Failed generations are surfaced under `error` in the polling response and are
refunded automatically. Full catalog: [Errors](/docs/api/errors).
## Tips
* **Pick the mode by inputs, not a flag.** Sending `reference_image_urls`
switches to R2V; sending `image_urls` switches to I2V; neither → T2V.
* In **R2V**, name each subject explicitly and reference it by `[Image N]` in
the prompt, matching the array order, for the strongest identity retention.
* `aspect_ratio` is ignored in I2V — crop your source image to the orientation
you want instead.
* Longer `duration` scales the bill linearly; start at 5s while iterating.
## Related
* [Tasks](/docs/api/tasks) — universal polling endpoint
* [Errors](/docs/api/errors) — full error catalog
* [happyhorse-1-0](/docs/happyhorse-1-0) — the previous generation
---
# humanize (https://reapi.ai/docs/humanize)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> `humanize` is a single async endpoint that takes AI-generated text and returns
> a rewrite that reads as human-written — varied rhythm, natural transitions, no
> tell-tale AI cadence — built to pass common AI detectors. Tune the rewrite
> with `readability`, `purpose`, `strength`, and `model_version`. See current
> pricing on the [model page](https://reapi.ai/models/humanize).
## Quick example
```bash
curl https://reapi.ai/api/v1/humanize \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "humanize",
"content": "Artificial intelligence has fundamentally transformed the way modern enterprises approach data-driven decision making.",
"readability": "University",
"purpose": "General Writing",
"strength": "Balanced",
"model_version": "v2"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/humanize",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "humanize",
"content": "Artificial intelligence has fundamentally transformed the way modern enterprises approach data-driven decision making.",
"readability": "University",
"purpose": "General Writing",
"strength": "Balanced",
"model_version": "v2",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/humanize", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "humanize",
content:
"Artificial intelligence has fundamentally transformed the way modern enterprises approach data-driven decision making.",
readability: "University",
purpose: "General Writing",
strength: "Balanced",
model_version: "v2",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "humanize",
"content": "Artificial intelligence has fundamentally transformed the way modern enterprises approach data-driven decision making.",
"readability": "University",
"purpose": "General Writing",
"strength": "Balanced",
"model_version": "v2",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/humanize", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "humanize",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` until `status === "completed"`. The completed
payload's `output.humanized_text` holds the rewritten text. A rewrite usually
finishes in a few seconds.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/humanize
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; polling
the task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Request body
### `model` — required
Always `"humanize"`.
### `content` — required (string)
The AI-generated text to humanize. Minimum **50 characters**. Longer text
takes proportionally longer to return.
### `readability` — required (string)
Target reading level of the rewrite. One of:
| Value | Register |
| ------------- | --------------------------------- |
| `High School` | Simple, accessible prose. |
| `University` | Standard academic / professional. |
| `Doctorate` | Dense, formal, advanced. |
| `Journalist` | Punchy, news-style. |
| `Marketing` | Persuasive, brand-friendly. |
### `purpose` — required (string)
What the text is for — tunes tone and structure. One of: `General Writing`,
`Essay`, `Article`, `Marketing Material`, `Story`, `Cover Letter`, `Report`,
`Business Material`, `Legal Material`.
### `strength` — optional (string)
How aggressively to rewrite. Default: `"Balanced"`.
| Value | Behavior |
| ------------ | ----------------------------------------------------------- |
| `Quality` | Lightest touch; stays closest to the source wording. |
| `Balanced` | Default trade-off between fidelity and humanization. |
| `More Human` | Most aggressive rewrite for the strongest detector evasion. |
### `model_version` — optional (string)
Which humanizer model handles the rewrite. Default: `"v2"`.
| Value | Best for |
| ------- | -------------------------------------------------- |
| `v2` | All languages, medium humanization. |
| `v11` | English, high humanization. |
| `v11sr` | English, strongest humanization (slightly slower). |
`model_version` maps to the underlying engine's model selector. reAPI uses
`model` at the top level to route the request, so this knob is exposed as a
separate `model_version` field to avoid the name collision — its values are
unchanged.
***
## Output
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "humanize",
"status": "completed",
"created_at": 1735000000,
"output": {
"humanized_text": "AI has reshaped how today's companies make decisions with data..."
},
"error": null
}
```
`output.humanized_text` is plain text — drop it straight back into your
document or CMS.
***
## Pricing
Billed **per word** of the `content` you submit, with a **50-word floor**
(text under 50 words is charged as 50 words). Polling the task does not add
any charge. Failed and rejected requests are refunded automatically.
`1 credit = $0.001 USD`. See the live banner on the
[model page](https://reapi.ai/models/humanize) for the current per-1,000-words
rate in credits.
***
## Errors
Standard envelope:
```json
{
"error": {
"code": 20003,
"message": "content must be at least 50 characters",
"request_id": "req_..."
}
}
```
Common cases:
| Code | When |
| ------- | -------------------------------------------------------------------------------- |
| `20002` | `content`, `readability`, or `purpose` missing. |
| `20003` | Invalid enum value, or `content` under 50 characters. |
| `30001` | Insufficient credits for the submitted word count. |
| `80001` | Provider rejected the submission — includes insufficient upstream credits/words. |
| `80003` | Provider failed while processing the rewrite. |
***
## Related
* [AI Text Detector](/docs/ai-text-detector) — score text 0–100 for AI authorship.
Pair it with `humanize` for a detect → rewrite → detect QA loop.
---
# imagen-4-0 (https://reapi.ai/docs/imagen-4-0)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Google's Imagen 4 model on reAPI. **Text-to-image only** — no reference
> images, no inpainting masks. Five aspect ratios at a **flat per-image
> rate**. Async-first: submit returns a `task_id`; poll until ready. See
> current pricing on the [model page](https://reapi.ai/models/imagen-4-0).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "imagen-4-0",
"prompt": "A corgi wearing an astronaut helmet on the lunar surface, Earth in the background, cinematic lighting, 8k",
"size": "16:9"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "imagen-4-0",
"prompt": "A corgi wearing an astronaut helmet on the lunar surface, Earth in the background, cinematic lighting, 8k",
"size": "16:9",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "imagen-4-0",
prompt:
"A corgi wearing an astronaut helmet on the lunar surface, Earth in the background, cinematic lighting, 8k",
size: "16:9",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "imagen-4-0",
"prompt": "A corgi wearing an astronaut helmet on the lunar surface, Earth in the background, cinematic lighting, 8k",
"size": "16:9",
})
req, _ := http.NewRequest("POST", "https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
The response carries a `task_id`. Poll
[`GET /api/v1/tasks/{task_id}`](/docs/api/tasks) until `status` is
`completed`, then read `result.images[].url`.
## Request body
| Field | Type | Required | Default | Description |
| -------- | ------- | -------- | ------- | --------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | — | Must be `imagen-4-0`. |
| `prompt` | string | yes | — | Image description. English or Chinese both supported. Up to 4000 characters. |
| `n` | integer | no | `1` | Number of images per request. Only `1` is accepted. |
| `size` | string | no | `16:9` | Aspect ratio. One of `1:1` / `4:3` / `3:4` / `16:9` / `9:16`. Passing an unsupported ratio returns a 400. |
`imagen-4-0` is **text-to-image only**. The endpoint rejects `image_urls`,
`image`, `mask`, and similar reference fields. For image-to-image, see
[seedream-5-0-lite](/docs/seedream-5-0-lite) or the
[gemini-3-pro-image-preview](/docs/gemini-3-pro-image-preview) family.
## Response
Submission returns the standard async envelope:
```json
{
"code": 200,
"data": [
{
"status": "submitted",
"task_id": "task_01K8AYYM6R03TGZ3Q2P0TZVNPX"
}
]
}
```
Polled completion (`GET /api/v1/tasks/{task_id}`):
```json
{
"code": 200,
"data": {
"status": "completed",
"result": {
"images": [{ "url": "https://cdn.reapi.ai/..." }]
}
}
}
```
Image URLs are valid for 24 hours from completion. Re-host the asset to
your own storage if you need durable access.
## Pricing
Flat per-image rate. `n` is fixed at `1`, so total charge is exactly one
image per request. See the
[model page](https://reapi.ai/models/imagen-4-0) for the live rate.
## Errors
`imagen-4-0` shares the platform-wide [error envelope](/docs/api/errors).
The validation surface is small:
* `prompt` missing or empty → `400 INVALID_REQUEST`.
* `size` outside the five accepted ratios → `400 INVALID_REQUEST`.
* `n` set to any value other than `1` → `400 INVALID_REQUEST`.
* Any reference media (`image_urls`, `image`, `mask`) supplied → `400
INVALID_REQUEST` with a text-to-image-only message.
---
# Welcome (https://reapi.ai/docs)
reAPI is an aggregator that runs every leading image, video, chat, and code
model behind a single standard endpoint. Pay-as-you-go via credits,
no per-provider account juggling.
## Where to start
* **[Quickstart](/docs/api/quickstart)** — first call in 5 minutes.
* **[Authentication](/docs/api/authentication)** — create and use API keys.
* **[Tasks](/docs/api/tasks)** — `GET /api/v1/tasks/{id}` polling reference (image, video, and audio).
* **[Errors](/docs/api/errors)** — error codes and how to handle them.
Per-model request schemas (parameters, sizes, resolutions, examples) live
on each model's own page — see the **Models** sidebar.
---
# kimi-k3 (https://reapi.ai/docs/kimi-k3)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Moonshot's Kimi K3 — the flagship for long-horizon coding and end-to-end
> knowledge work — exposed through api.reapi.ai as a drop-in OpenAI-compatible
> Chat Completions endpoint. A **1,048,576-token** context window, output
> defaulting to **131,072** tokens and raisable to the full window, **reasoning
> that is always on** with a three-rung `reasoning_effort` dial, native **image
> and video** input, tool calling with a `required` tool choice, strict JSON
> Schema output, and automatic context caching. The wire `model` id is
> `kimi-k3`. Current rates live on the
> [model page](https://reapi.ai/models/kimi-k3) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kimi-k3",
"messages": [
{ "role": "user", "content": "Refactor this module and add tests." }
],
"reasoning_effort": "high",
"stream": true
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="kimi-k3",
messages=[{"role": "user", "content": "Refactor this module and add tests."}],
reasoning_effort="high",
stream=True,
)
for chunk in stream:
delta = chunk.choices[0].delta
reasoning = getattr(delta, "reasoning_content", None)
if reasoning:
print(reasoning, end="")
if delta.content:
print(delta.content, end="")
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "kimi-k3",
messages: [{ role: "user", content: "Refactor this module and add tests." }],
reasoning_effort: "high",
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "kimi-k3",
"messages": []map[string]string{
{"role": "user", "content": "Refactor this module and add tests."},
},
"reasoning_effort": "high",
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Chat models are served from the `api.reapi.ai` gateway, which has its own console
and its own key. Create the key there, then send it as a bearer token:
```http
Authorization: Bearer YOUR_API_KEY
```
The gateway key is **not** the same credential as a media-generation key used
against `reapi.ai/api/v1`. Sign in at
[api.reapi.ai](https://api.reapi.ai/) to create one.
***
## Endpoint
```http
POST /v1/chat/completions
```
Base URL `https://api.reapi.ai`. The wire format is OpenAI-compatible, so the
same SDKs (`openai-python`, `openai-node`, `openai-go`, …) work once you swap the
base URL, the key and the model string. Moonshot's own quickstart drives this
model through the OpenAI SDK, so the compatibility is the vendor's design rather
than a translation layer.
***
## Request body
### `model` — string, required
Must be `kimi-k3` exactly — no date suffix, no version decimal.
### `messages` — array, required
Conversation history, each entry an object with `role` and `content`. Text, image
and video parts are supported — see [Visual input](#visual-input).
**Echo the assistant message back unchanged.** In multi-turn conversations and
tool-call loops, the complete assistant message returned by the API has to go
back into `messages` as-is, including `reasoning_content` and `tool_calls`.
Keeping only `content` — the habit most OpenAI-shaped code has — breaks this
model's Preserved Thinking.
### `max_completion_tokens` — integer
Upper bound on output tokens for this response. Moonshot documents a **default of
131,072**, raisable to **1,048,576** — the size of the whole context window.
Reasoning runs before the answer, so budget for both.
### `reasoning_effort` — string, default `"max"`
Top-level field controlling how hard the model thinks. See
[Reasoning](#reasoning).
### `stream` — boolean, default `false`
When `true`, tokens arrive as server-sent events. Reasoning and answer come as
**separate deltas** — `reasoning_content` and `content` — so a client can render
the reasoning live and still parse the answer on its own.
### `tools` / `tool_choice` — optional
Tool definitions and the strategy for picking among them. `tool_choice` accepts
`auto`, `none` and `required`. See [Tool calling](#tool-calling).
### `response_format` — object, optional
A JSON Schema with `strict: true` constrains the **answer** field. Parse that
field, not `reasoning_content`, which is prose.
### `group` — string, default `"default"`
Token group on the gateway. Leave it at `default` unless your account has been
given another group to route through.
***
## Reasoning
**This model always reasons.** Moonshot's own FAQ answers "how do I turn off the
chain of thought" with a flat *you can't*. There is no thinking toggle; the lever
is effort.
`reasoning_effort` takes **three** rungs and defaults to the most expensive one:
| Level | Use it for |
| ------ | --------------------------------------------------------------------------------------------- |
| `max` | **The default.** The hardest reasoning, where correctness outweighs cost. |
| `high` | A step down that still reasons hard. |
| `low` | Short, scoped, latency-sensitive work — and the answer to "the reasoning is taking too long". |
There is no `medium`.
**Pick the rung before the conversation starts.** Moonshot documents that
switching effort levels **invalidates prefix-cache hits**, so flipping it
mid-session silently throws away your cache.
The K2-era `thinking` object is **not** a parameter on this model. If you are
migrating from `kimi-k2.6` or `kimi-k2.7-code`, delete it and use top-level
`reasoning_effort` instead. Code that already sends OpenAI's `reasoning_effort`
needs no change beyond the accepted values.
***
## Parameters that are fixed
Moonshot pins all five of these on this model. Passing any other value returns an
error, so leave them out of the request entirely:
| Parameter | Fixed at |
| ------------------- | -------- |
| `temperature` | `1.0` |
| `top_p` | `0.95` |
| `n` | `1` |
| `presence_penalty` | `0` |
| `frequency_penalty` | `0` |
Steer with prompting and the `reasoning_effort` rung instead.
***
## Tool calling
Two controls are specific to this generation.
**`tool_choice: "required"`** forces at least one tool call on the turn — the way
to stop an agent answering from memory when it was supposed to look something up.
The K2 models reject this value; this one accepts it.
**Dynamic tool loading** introduces a tool partway through a conversation: put
its complete definition in a `system` message that carries `tools` and **no**
`content`. It becomes available from that position onward.
```json
{
"role": "system",
"tools": [
{
"type": "function",
"function": {
"name": "calculate",
"description": "Evaluate an arithmetic expression",
"parameters": {
"type": "object",
"properties": { "expression": { "type": "string" } },
"required": ["expression"]
}
}
}
]
}
```
The server does **not** retain a dynamically loaded tool. Keep that `system`
message in your request history or the tool disappears on the next turn.
Include the complete `name`, `description` and `parameters` in the definition,
and after executing calls, append one `tool` message per call with the matching
`tool_call_id`.
***
## Visual input
| Modality | Supported |
| -------- | :-------: |
| Text | ✅ |
| Image | ✅ |
| Video | ✅ |
Output is **text only**. Vision is native to this model rather than a separate
variant, which is what makes screenshot-driven work — frontend layout, game
development, CAD — practical: it can look at what it just produced.
**Public image URLs are not accepted.** Moonshot documents base64 data URIs, or a
file reference from its own upload API, as the two ways in. `content` must also
be an **array of parts**, not a serialized string.
```json
{
"role": "user",
"content": [
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,iVBORw0..." } },
{ "type": "text", "text": "Why does this layout break at 768px?" }
]
}
```
Note that file references come from Moonshot's Files API, which the Chat
Completions endpoint does not itself provide.
***
## Context caching
Caching is **automatic** — no cache id, no TTL, no extra parameter. Keep a long
prefix (system prompt, knowledge document, tool definitions) stable and repeat
requests attempt a hit on their own. Two conditions decide whether that happens:
* The **previous** request's prompt tokens must exceed **256**. Below that the
request is not cached at all.
* The `reasoning_effort` rung must not change — switching it invalidates the
prefix cache.
Put the fixed bulk at the front of `messages` and append the varying question
after it.
***
## Pricing dimensions
Billing is **per token**, in USD, against your `api.reapi.ai` balance, with
separate **input** and **output** rates. Three things to keep in mind:
* **Reasoning tokens bill as output.** Since this model always reasons, that is
the usual reason a bill exceeds an estimate built from visible answer length.
* **The rate is flat across context length.** Moonshot documents no long-context
tier, so a million-token prompt costs the same per token as a short one.
* **Effort moves the bill.** It is the only reasoning lever, and it defaults to
the most expensive rung — so it is the first thing to tune.
Both token rates sit **below Moonshot's published per-token rate**, output by a
full fifth. Current numbers are on the
[model page](https://reapi.ai/models/kimi-k3) and
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) — those tables are the
canonical source, not this page.
Chat models bill in USD on the gateway balance. They do **not** draw down the
integer credits used by the media-generation endpoints on `reapi.ai/api/v1`.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-...",
"object": "chat.completion",
"created": 1785000000,
"model": "kimi-k3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"reasoning_content": "...",
"content": "..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 1204,
"completion_tokens": 860,
"total_tokens": 2064
}
}
```
`reasoning_content` is a sibling of `content`, not a replacement for it — and it
is the field you must carry into the next request.
### Streaming (`stream: true`)
Server-sent events, each `data:` line carrying a `chat.completion.chunk`.
Reasoning arrives in `choices[0].delta.reasoning_content` and the answer in
`choices[0].delta.content`, terminated by `data: [DONE]`.
***
## Errors
Failures use the gateway's standard envelope. See the
[errors catalog](/docs/api/errors) for the full code list. Common cases:
| Trigger | What to do |
| ------------------------------------------------------ | ---------------------------------------------------- |
| Missing or invalid bearer token | Create a key in the api.reapi.ai console |
| Unknown `model` value | Send `kimi-k3` exactly |
| `temperature` / `top_p` / `n` / penalty params present | Remove them; they are fixed on this model |
| K2-era `thinking` object present | Remove it and use `reasoning_effort` |
| `reasoning_effort` set to `medium` | Not a valid rung — use `low`, `high` or `max` |
| A public image URL in an `image_url` part | Send a base64 data URI or an uploaded file reference |
| `content` sent as a string for a multimodal turn | Make it an array of parts |
| `max_completion_tokens` above the ceiling | Lower it; the documented ceiling is 1,048,576 |
| Insufficient balance | Top up on the gateway |
| Upstream rate limit | Retry with backoff, or route through another model |
Moonshot flags its own `web_search` tool as **being updated** and does not
recommend using it in the near term. Treat web search as unavailable on this
model for now and supply fresh context yourself.
***
## Tips
* **Give the whole task up front.** This model is built for long-horizon work
under minimal supervision; a complete specification beats a drip-feed of short
turns.
* **Drop your sampling parameters first.** The single most common migration error
here is a carried-over `temperature`.
* **Tune effort before anything else.** The default is `max`; `low` exists
precisely for the case where reasoning is costing more than the answer is
worth.
* **Budget for reasoning.** If responses truncate on hard prompts, the reasoning
pass consumed the allowance — raise `max_completion_tokens`.
* **Stream anything interactive**, and render `reasoning_content` separately from
the answer rather than concatenating them.
* **Keep the assistant message intact** in history. Stripping `reasoning_content`
is silent — it does not error, it just degrades multi-turn behaviour.
* **Front-load the stable bulk** of your prompt so automatic caching can hit, and
do not change the effort rung mid-conversation.
***
## Related
* [Kimi K3 model page](https://reapi.ai/models/kimi-k3) — current rates
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# kling-3-0-turbo (https://reapi.ai/docs/kling-3-0-turbo)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Kuaishou's Kling 3.0 Turbo on reAPI — the fast tier of Kling 3.0 for
> **text-to-video** and **image-to-video** (first frame), at **720p** or
> **1080p**. One async endpoint: submit returns a `task_id`; poll until ready.
> See current pricing on the [model page](https://reapi.ai/models/kling-3-0-turbo).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3-0-turbo",
"prompt": "a corgi running on the beach, cinematic, golden hour",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 5
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "kling-3-0-turbo",
"prompt": "a corgi running on the beach, cinematic, golden hour",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 5,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "kling-3-0-turbo",
prompt: "a corgi running on the beach, cinematic, golden hour",
resolution: "1080p",
aspect_ratio: "16:9",
duration: 5,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "kling-3-0-turbo",
"prompt": "a corgi running on the beach, cinematic, golden hour",
"resolution": "1080p",
"aspect_ratio": "16:9",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "kling-3-0-turbo",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
generated video URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Modes
There is no separate model id per modality — the modality is implicit in the
request shape:
* **Text-to-video** — no `first_frame_image`. `prompt` is required, and
`aspect_ratio` applies.
* **Image-to-video** — pass `first_frame_image` (a public HTTPS URL). The clip
starts from that frame, `prompt` is optional, and `aspect_ratio` is ignored
(the frame fixes the ratio).
Multi-shot direction is expressed inside the `prompt` (describe each shot);
there is no dedicated multi-shot field.
***
## Request body
### `model` — string, required
Must be `kling-3-0-turbo`.
### `prompt` — string
Up to **3,072 characters** (recommended ≤ 2,500). Required for text-to-video;
optional in image-to-video (omit it to animate purely from the first frame).
### `first_frame_image` — string, optional
Start frame for image-to-video. **Public HTTPS URL only** — base64 / `data:`
URIs are rejected at the gateway. JPG / JPEG / PNG, ≤ 50 MB, ≥ 300 px, with an
aspect ratio between 1:2.5 and 2.5:1.
### `resolution` — string, default `720p`
Output clarity tier: `720p` or `1080p`. Billed per second at the tier's rate.
### `duration` — integer, default `5`
Total video length in seconds, range `3–15`. Billed per second.
### `aspect_ratio` — string, default `16:9`
One of `16:9`, `9:16`, `1:1`. **Text-to-video only.** When `first_frame_image`
is supplied (image-to-video), this field has **no effect** — the output ratio is
determined by the first frame. Set it only for text-to-video.
### `watermark` — boolean, optional
Add a watermark to the output video. Omitted by default (no watermark).
Modality is auto-detected: supply `first_frame_image` for image-to-video, omit
it for text-to-video. You never set a mode flag.
***
## Pricing
Kling 3.0 Turbo bills **per second**, by resolution tier (`resolution`):
```
credits = ceil(per_second_usd × seconds × 1000)
```
where `1 credit = $0.001 USD` and `seconds` is `duration`. The 1080p tier
carries a higher per-second rate than 720p. Failed and rejected requests are not
charged.
The exact per-second credit cost for each tier surfaces on the
[model page](https://reapi.ai/models/kling-3-0-turbo) and through the playground
estimator before submit.
***
## Response
The poll envelope returns the video URL in `output.video_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "kling-3-0-turbo",
"status": "completed",
"output": {
"video_urls": [
"https://cdn.reapi.ai/...mp4"
]
}
}
```
Generated URLs expire — mirror them to your own storage if you need long-term
retention.
***
## Errors
Failures return the standard reAPI envelope `{ error: { code, message,
request_id } }`. Common cases:
* Invalid input (prompt over 3072 chars, out-of-range `duration`, a non-HTTPS
`first_frame_image`, an invalid `resolution` or `aspect_ratio`) → `400`.
* Insufficient credits → `402`.
* Rate limited → `429`.
See the full catalog at [/docs/api/errors](/docs/api/errors).
***
## Tips
* Use cinematic language in the prompt (shot type, camera move, lighting) —
Kling 3.0 Turbo reads it; there is no dedicated camera-control parameter.
* Draft at `720p` to iterate cheaply, then re-run the winner at `1080p` for
delivery — same call, one parameter change.
* For image-to-video, the first frame fixes the aspect ratio, so you can skip
`aspect_ratio` in that mode.
* Reach for the full [Kling 3.0](/docs/kling-3-0) when you need multi-shot
control, native audio, or 4K; Kling 3.0 Turbo trades those for speed and a
lower per-second cost.
***
## Related
* [Kling 3.0](/docs/kling-3-0)
* [Video generation models](/docs/seedance-2-0)
* [Tasks API](/docs/api/tasks)
* [Error codes](/docs/api/errors)
---
# kling-3-0 (https://reapi.ai/docs/kling-3-0)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Kuaishou's Kling 3.0 on reAPI — one async endpoint for **text-to-video**,
> **image-to-video** (first/last frame), and **multi-shot** cinematic
> sequences, with optional **native multilingual audio** and up to **4K**.
> Submit returns a `task_id`; poll until ready. See current pricing on the
> [model page](https://reapi.ai/models/kling-3-0).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3-0",
"prompt": "A soft anime-style girl in a field, warm sunset glow, smooth loop",
"duration": 6,
"aspect_ratio": "16:9",
"mode": "pro"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "kling-3-0",
"prompt": "A soft anime-style girl in a field, warm sunset glow, smooth loop",
"duration": 6,
"aspect_ratio": "16:9",
"mode": "pro",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "kling-3-0",
prompt: "A soft anime-style girl in a field, warm sunset glow, smooth loop",
duration: 6,
aspect_ratio: "16:9",
mode: "pro",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "kling-3-0",
"prompt": "A soft anime-style girl in a field, warm sunset glow, smooth loop",
"duration": 6,
"aspect_ratio": "16:9",
"mode": "pro",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "kling-3-0",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
generated video URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Modes
There is no separate model id per modality — the modality is implicit in the
request shape:
* **Text-to-video** — no `image_urls`. `prompt` is required.
* **Image-to-video** — pass `image_urls`: index 0 is the start frame, index 1
(optional) is the end frame. Aspect ratio auto-adapts to the images.
* **Multi-shot** — set `multi_shots: true` and provide `multi_prompt` (per-shot
prompts + durations).
The resolution tier (`std` 720p / `pro` 1080p / `4K`) is the `mode` field;
native audio is the `sound` toggle.
***
## Request body
### `model` — string, required
Must be `kling-3-0`.
### `prompt` — string
Up to **2,500 characters**. Required for single-shot; superseded by
`multi_prompt` in multi-shot mode.
### `image_urls` — array, optional
First/last frame for image-to-video. **Public HTTPS URLs only** — base64 /
`data:` URIs are rejected at the gateway. Up to **2** (index 0 = first frame,
index 1 = last frame).
### `duration` — integer, default `5`
Total video length in seconds, range `3–15`. Billed per second.
### `aspect_ratio` — string, default `16:9`
One of `16:9`, `9:16`, `1:1`. Ignored (auto-adapted) when `image_urls` is
supplied.
### `mode` — string, default `pro`
Resolution tier: `std` (720p), `pro` (1080p), or `4K`.
### `sound` — boolean, default `false`
Generate synchronized native audio (dialogue, lip sync, ambient sound). Audio
tiers cost more per second.
### `multi_shots` — boolean, default `false`
Enable multi-shot mode (a connected sequence of shots).
### `multi_prompt` — array, optional (required when `multi_shots: true`)
Up to **5** shots; each item is `{ "prompt": string, "duration": int }` where
per-shot duration is **1–12s** and prompt is up to **500 chars**.
```json
{
"multi_shots": true,
"multi_prompt": [
{ "prompt": "a chef plating a dish, close-up", "duration": 4 },
{ "prompt": "pull back to a busy restaurant", "duration": 5 }
]
}
```
### `kling_elements` — array, optional
Up to **3** reusable `@`-referenced elements. Each item is
`{ "name": string, "description"?: string, "element_input_urls": [string] }`
with **2–4** image URLs (JPG/PNG). Reference an element in the prompt with
`@name`.
Multi-shot bills the **sum of the per-shot durations**; single-shot bills the
top-level `duration`.
***
## Pricing
Kling 3.0 bills **per second**, by resolution tier (`mode`) and whether audio
is on (`sound`):
```
credits = ceil(per_second_usd × seconds × 1000)
```
where `1 credit = $0.001 USD`, and `seconds` is `duration` (single-shot) or the
sum of `multi_prompt` durations (multi-shot). Higher tiers and audio carry a
higher per-second rate. Failed and rejected requests are not charged.
The exact per-second credit cost for each tier surfaces on the
[model page](https://reapi.ai/models/kling-3-0) and through the playground
estimator before submit.
***
## Response
The poll envelope returns the video URL in `output.video_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "kling-3-0",
"status": "completed",
"output": {
"video_urls": [
"https://cdn.reapi.ai/...mp4"
]
}
}
```
Generated URLs expire — mirror them to your own storage if you need long-term
retention.
***
## Errors
Failures return the standard reAPI envelope `{ error: { code, message,
request_id } }`. Common cases:
* Invalid input (prompt over 2500 chars, out-of-range `duration`, a non-HTTPS
media URL, more than 2 `image_urls`, more than 5 shots) → `400`.
* Insufficient credits → `402`.
* Rate limited → `429`.
See the full catalog at [/docs/api/errors](/docs/api/errors).
***
## Tips
* Use cinematic language in the prompt (shot type, camera move, lighting) —
Kling 3.0 reads it; there is no dedicated camera-control parameter.
* For sequences, prefer multi-shot over stitching separate requests — characters
stay consistent across cuts.
* For image-to-video, supply both a first and last frame to control the start
and end of the motion.
* Reach for `mode: "4K"` only when the destination needs it — the per-second
rate is highest there.
***
## Related
* [Video generation models](/docs/seedance-2-0)
* [Grok Imagine Video 1.5](/docs/grok-imagine-video-1-5)
* [Tasks API](/docs/api/tasks)
* [Error codes](/docs/api/errors)
---
# kling-motion-control (https://reapi.ai/docs/kling-motion-control)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Kling Motion Control transfers the motion from a **reference video** onto a
> subject taken from a **reference image**. One async endpoint, two version
> tiers (`kling-v3-motion-control`, `kling-v2-6-motion-control`), `std` or
> `pro` quality. Billing is per second of the source video's server-probed
> length. See current pricing on the
> [model page](https://reapi.ai/models/kling-motion-control).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v3-motion-control",
"prompt": "Keep the subject consistent, follow the reference motion, cinematic lighting",
"image_url": "https://your-cdn.com/subject.png",
"video_url": "https://your-cdn.com/motion-8s.mp4",
"character_orientation": "image",
"mode": "std",
"keep_original_sound": "yes"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "kling-v3-motion-control",
"prompt": "Follow the reference motion, keep the subject consistent",
"image_url": "https://your-cdn.com/subject.png",
"video_url": "https://your-cdn.com/motion-8s.mp4",
"character_orientation": "image",
"mode": "std",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "kling-v3-motion-control",
prompt: "Follow the reference motion, keep the subject consistent",
image_url: "https://your-cdn.com/subject.png",
video_url: "https://your-cdn.com/motion-8s.mp4",
character_orientation: "image",
mode: "std",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "kling-v3-motion-control",
"prompt": "Follow the reference motion, keep the subject consistent",
"image_url": "https://your-cdn.com/subject.png",
"video_url": "https://your-cdn.com/motion-8s.mp4",
"character_orientation": "image",
"mode": "std",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "kling-v3-motion-control",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
generated MP4 URLs.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Version tiers
Two `model` ids share the **exact same request shape** — pick one by the
`model` string you POST:
| `model` id | Tier | Notes |
| --------------------------- | ---- | --------------------------------------------- |
| `kling-v3-motion-control` | v3 | Latest — higher motion fidelity & consistency |
| `kling-v2-6-motion-control` | v2.6 | Budget tier, lower per-second rate |
Everything else in this page applies identically to both ids. See current
per-second rates for each tier on the
[model page](https://reapi.ai/models/kling-motion-control).
***
## Request body
### `model` — required
`string`. One of `"kling-v3-motion-control"` or `"kling-v2-6-motion-control"`.
### `prompt` — required
`string`. Refines the motion, camera, and style. The API rejects requests
without a prompt — more specific prompts yield more stable results.
### `image_url` — required
`string`. Public HTTP(S) URL of the **reference image** — the subject whose
appearance and identity the output keeps.
**No `data:` URIs.** reApi rejects base64 inputs platform-wide. Upload to public
storage (your own CDN, S3, R2…) and pass the URL.
### `video_url` — required
`string`. Public HTTP(S) URL of the **reference video** — the clip whose motion
is transferred onto the subject. MP4 / MOV recommended, ≤ 100MB.
The allowed source length depends on `character_orientation` (see below). The
**billable second count is the source video's server-probed duration**, not a
client estimate.
### `character_orientation` — required
`string`. Controls whose facing the subject follows.
| Value | Behavior | Source video length |
| --------- | -------------------------------------- | ------------------- |
| `"image"` | Facing follows the reference **image** | 3 – 10 seconds |
| `"video"` | Facing follows the reference **video** | 3 – 30 seconds |
### `mode` — required
`string`. Quality / cost tier.
| Value | Behavior |
| ------- | ------------------------------------------------- |
| `"std"` | Standard — balances speed and quality |
| `"pro"` | Pro — higher quality, usually slower; higher rate |
### `keep_original_sound` — string, default `"yes"`
Whether to retain the reference video's original audio track.
| Value | Behavior |
| ------- | --------------------------------- |
| `"yes"` | Keep the original audio (default) |
| `"no"` | Drop the original audio |
### `watermark_info` — object
Watermark control. Default disabled.
```json
{ "watermark_info": { "enabled": false } }
```
| Field | Type | Default | Behavior |
| --------- | ------- | ------- | --------------- |
| `enabled` | boolean | `false` | Add a watermark |
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "kling-v3-motion-control",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
***
## Pricing
Per-second × `mode`. The billable second count is the source video's
**server-probed** length — reApi measures the uploaded clip server-side rather
than trusting a client value. `kling-v3-motion-control` and
`kling-v2-6-motion-control` have separate per-second rates, and `pro` costs
more than `std`.
**Bill formula** (1 credit = $0.001):
```
billable_seconds = ceil(server_probed_source_seconds)
bill_usd = per_second_usd(tier, mode) × billable_seconds
credits = ceil(bill_usd × 1000)
```
See current per-second rates for each tier and mode on the
[model page](https://reapi.ai/models/kling-motion-control). Failed jobs refund
automatically. A probe failure returns `400 PRICING_UNAVAILABLE` (code `30002`)
with no charge.
The playground's draft estimate may show a default before the upload finishes —
the authoritative number is computed at submit, after the server probes the
file.
***
## Validation errors
All cases below return HTTP 400. Pattern-match on `code`, not `message` —
message strings carry request-specific context and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| -------------------------------------------------- | ------- | -------------------------------------------------------------- |
| `image_url` missing | `20002` | `kling-motion-control: image_url is required` |
| `video_url` missing | `20002` | `kling-motion-control: video_url is required` |
| `character_orientation` missing / invalid | `20003` | `kling-motion-control: invalid character_orientation` |
| `mode` missing / invalid | `20003` | `kling-motion-control: invalid mode (allowed: std / pro)` |
| `keep_original_sound` invalid | `20003` | `kling-motion-control: invalid keep_original_sound` |
| `image_url` / `video_url` carrying a `data:` URI | `20003` | `kling-motion-control: image_url must be a public http(s) URL` |
| Source video probe fails (network / format) | `30002` | `Could not determine source video duration for billing: …` |
| Source video length outside the orientation window | `80007` | Provider rejected the request as invalid |
The full envelope is `{ "error": { "code", "message", "request_id" } }` — see
[Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Minimum request (image orientation)
```json
{
"model": "kling-v3-motion-control",
"prompt": "Follow the reference motion, keep the subject consistent",
"image_url": "https://your-cdn.com/subject.png",
"video_url": "https://your-cdn.com/motion-8s.mp4",
"character_orientation": "image",
"mode": "std"
}
```
### Video orientation, pro quality, up to 30s
```json
{
"model": "kling-v3-motion-control",
"prompt": "Follow the reference performance and rhythm, keep motion continuous",
"image_url": "https://your-cdn.com/subject.png",
"video_url": "https://your-cdn.com/motion-20s.mp4",
"character_orientation": "video",
"mode": "pro",
"keep_original_sound": "no"
}
```
### Budget tier, watermark off
```json
{
"model": "kling-v2-6-motion-control",
"image_url": "https://your-cdn.com/subject.png",
"video_url": "https://your-cdn.com/motion-6s.mp4",
"character_orientation": "image",
"mode": "std",
"watermark_info": { "enabled": false }
}
```
***
## Tips
* **Match the clip to the orientation.** `character_orientation: "image"` caps
the reference video at 10s; use `"video"` when you need up to 30s.
* **Cost tracks the source clip.** A 5s reference video bills \~5 seconds; trim
the input to control spend.
* **Prompt the motion, not just the scene.** Phrasing the action ("turns and
waves, smooth and continuous") sharpens the transfer.
* **Start on `std`, escalate to `pro`.** Validate the motion on the cheaper
mode, then re-run on `pro` for the final take.
* **Pick the tier by budget.** `kling-v2-6-motion-control` is the cheaper run;
`kling-v3-motion-control` maximizes fidelity and subject consistency.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# midjourney-v7 (https://reapi.ai/docs/midjourney-v7)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Midjourney V7 on reAPI. **Four images per request**, native MJ prompt
> syntax (`--ar`, `--s`, `--chaos`, `--sref`, `--oref`, …), and **eleven edit
> operations** that chain off any generated image. Async-first: submit
> returns a `task_id`; poll until ready. See current pricing on the
> [model page](https://reapi.ai/models/midjourney-v7).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "mj-v7",
"prompt": "a serene Japanese garden with cherry blossoms --ar 16:9 --s 500",
"model_params": { "speed": "fast" }
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "mj-v7",
"prompt": "a serene Japanese garden with cherry blossoms --ar 16:9 --s 500",
"model_params": {"speed": "fast"},
},
timeout=30,
)
task_id = resp.json()["id"]
```
```javascript
const resp = await fetch('https://reapi.ai/api/v1/images/generations', {
method: 'POST',
headers: {
Authorization: 'Bearer rk_live_xxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'mj-v7',
prompt: 'a serene Japanese garden with cherry blossoms --ar 16:9 --s 500',
model_params: { speed: 'fast' },
}),
});
const { id } = await resp.json();
```
```go
body := strings.NewReader(`{
"model": "mj-v7",
"prompt": "a serene Japanese garden with cherry blossoms --ar 16:9 --s 500",
"model_params": { "speed": "fast" }
}`)
req, _ := http.NewRequest("POST", "https://reapi.ai/api/v1/images/generations", body)
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
```
Generation is asynchronous. The call returns a `task_id`; poll
`GET /api/v1/tasks/:task_id` until `status` is `completed`, then read the
image URLs. Each request returns **1–4 images** (content review may filter
some) and is billed **once per request**.
## Endpoint
```
POST https://reapi.ai/api/v1/images/generations
GET https://reapi.ai/api/v1/tasks/:task_id
```
| Header | Value |
| --------------- | -------------------- |
| `Authorization` | `Bearer rk_live_xxx` |
| `Content-Type` | `application/json` |
reAPI accepts only **public http(s) image URLs** for every image input —
never base64 or `data:` URIs. For image-to-image, place the URL at the start
of the `prompt`; for the direct-image edits, pass it in `image_urls`.
## Models
The family is one base generator plus eleven edit operations. Each is a
separate `model` id on the same endpoint.
| `model` | Operation | References a prior task? |
| -------------------- | ------------------------------- | -------------------------------- |
| `mj-v7` | Text-to-image / image-to-image | — |
| `mj-v7-variation` | Subtle/strong variations | yes (`task_id` + `image_number`) |
| `mj-v7-upscale` | Upscale one image | yes |
| `mj-v7-remix` | Re-prompt an image | yes |
| `mj-v7-enhance` | Enhance a draft image | yes (draft tasks only) |
| `mj-v7-pan` | Extend the canvas directionally | yes |
| `mj-v7-outpaint` | Zoom out / extend the frame | yes |
| `mj-v7-inpaint` | Repaint a masked region | yes |
| `mj-v7-edit` | Canvas edit (reposition + fill) | yes |
| `mj-v7-remove-bg` | Remove background | no (`image_urls`) |
| `mj-v7-retexture` | Re-texture / restyle | no (`image_urls`) |
| `mj-v7-upload-paint` | Upload + masked repaint | no (`image_urls`) |
The eight parent-referencing edits take the reAPI `task_id` of a **completed
Midjourney V7 generation** in `model_params.task_id`, plus
`model_params.image_number` (0–3) to pick which of the four grid images to act
on. reAPI resolves your task id to the upstream reference automatically.
## Base generation — `mj-v7`
| Field | Type | Required | Default | Notes |
| -------------------- | ------ | -------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | — | `mj-v7` |
| `prompt` | string | yes | — | Native MJ syntax, ≤ 8192 chars. Image-to-image: place public image URL(s) at the start. A single image with no text is rejected. |
| `model_params.speed` | string | no | `fast` | `draft` / `fast` / `turbo` |
Native MJ parameters are written **inside the prompt**, not as JSON fields:
| Flag | Range | Default | Meaning |
| ----------------- | ------------ | ------- | ------------------------------ |
| `--ar W:H` | any ratio | `1:1` | Aspect ratio |
| `--s` | 0–1000 | 100 | Stylize |
| `--chaos` / `--c` | 0–100 | 0 | Variation spread |
| `--no` | keywords | — | Negative prompt |
| `--seed` | 0–4294967295 | random | Reproducible seed |
| `--weird` / `--w` | 0–3000 | 0 | Unconventional degree |
| `--exp` | 0–100 | 0 | Experimental aesthetic (V7) |
| `--raw` | flag | off | Disable default beautification |
| `--tile` | flag | off | Seamless tile |
| `--sref [URL]` | URL | — | Style Reference |
| `--sw` | 0–1000 | 100 | Style weight |
| `--sv` | 1–6 | 4 | Style version |
| `--oref [URL]` | URL | — | Omni Reference (doubles cost) |
| `--ow` | 1–1000 | 100 | Omni Reference weight |
| `--iw` | 0–3 | 1 | Image-prompt weight |
`--v` / `--version` / `--niji` are ignored (the version is locked to V7).
Speed is set via `model_params.speed`, not a prompt flag.
## Edit operations
All edits share the endpoint and the async `task_id` lifecycle. `speed`
(where present) is `fast` / `turbo` (default `fast`).
### Parent-referencing edits
Common `model_params`: `task_id` (required, the reAPI task id of a completed
Midjourney V7 generation), `image_number` (0–3, default 0).
| `model` | Extra `model_params` | Top-level `prompt` |
| ----------------- | ------------------------------------------------------------------------------------------------------- | ------------------ |
| `mj-v7-variation` | `type`: `subtle` \| `strong` (default `subtle`); `speed` | optional |
| `mj-v7-upscale` | `type`: `standard` \| `creative` (default `standard`) | — |
| `mj-v7-remix` | `mode`: `strong` \| `subtle` (default `strong`); `speed` | **required** |
| `mj-v7-enhance` | — (parent must be a `draft` task) | — |
| `mj-v7-pan` | `direction`: `down` \| `right` \| `up` \| `left` (default `down`); `scale` 1.1–3 (default 1.5); `speed` | optional |
| `mj-v7-outpaint` | `scale` 1.1–2 (default 1.5); `speed` | optional |
| `mj-v7-inpaint` | `mask` (required); `speed` | optional |
| `mj-v7-edit` | `canvas` (required); `img_pos` (required); `mask` (optional); `speed` | **required** |
`mask` is one of two shapes (mutually exclusive):
```json
{ "mask": { "areas": [ { "width": 200, "height": 200, "points": [50,50,50,250,250,250,250,50] } ] } }
```
```json
{ "mask": { "url": "https://example.com/mask.png" } }
```
`points` is a flat `[x1,y1,x2,y2,…]` closed polygon; for the URL form, white =
repaint region, black = preserve. `canvas` is `width` / `height` (px);
`img_pos` is `width` / `height` / `x` / `y` (placement of the source image on
the canvas).
Example — upscale image 0 of a prior generation:
```json
{
"model": "mj-v7-upscale",
"model_params": { "task_id": "task_xxx", "image_number": 0, "type": "standard" }
}
```
### Direct-image edits
These take the source image in `image_urls` (exactly one public http(s) URL)
and do not reference a prior task.
| `model` | Required | `model_params` |
| -------------------- | ---------------------- | --------------------------------------------------------------------- |
| `mj-v7-remove-bg` | `image_urls` | — |
| `mj-v7-retexture` | `prompt`, `image_urls` | `speed` (optional) |
| `mj-v7-upload-paint` | `prompt`, `image_urls` | `mask` (required), `canvas` (required), `img_pos` (required), `speed` |
## Pricing
Midjourney V7 bills **per request** (four images), not per image. Cost scales
with:
* **Speed tier** — `draft` \< `fast` \< `turbo`.
* **Operation** — base generation and lightweight edits (remove-bg, pan,
outpaint, enhance) cost less than heavy edits (upscale, variation, remix,
inpaint, canvas edit, retexture, upload-paint).
* **`--oref`** — Omni Reference in the prompt **doubles** the cost.
Integer credits, `1 credit = $0.001`:
```
credits = ceil(request_price_usd × 1000)
```
where `request_price_usd` is the (model, speed) rate, doubled when the prompt
carries `--oref`. See the
[model page](https://reapi.ai/models/midjourney-v7) for current rates.
## Output
Poll `GET /api/v1/tasks/:task_id`. On `completed`:
```json
{
"id": "task_xxx",
"status": "completed",
"output": { "image_urls": ["https://cdn.reapi.ai/.../0.png", "https://cdn.reapi.ai/.../1.png"] }
}
```
`output.image_urls` holds 1–4 URLs (content review may filter some of the
four). Save them promptly.
## Errors
Customer-facing errors use the standard envelope
`{ error: { code, message, request_id } }`. Common cases:
| Situation | Code |
| ----------------------------------------------------- | -------------------------- |
| Prompt rejected by content review | `CONTENT_POLICY_VIOLATION` |
| Malformed / out-of-range request | `INVALID_REQUEST` |
| Referenced task not found / not yours / not completed | `INVALID_REQUEST` |
| Insufficient credits | `INSUFFICIENT_CREDITS` |
| Upstream generation failed | `UPSTREAM_ERROR` |
See the full catalog at [/docs/api/errors](/docs/api/errors).
## Tips
* A single reference image with no text is rejected — add a short description.
* Use `--oref` for subject consistency and `--sref` for style transfer; they
combine. `--oref` doubles the bill, so reserve it for shots that need it.
* Generate with `mj-v7` first, then chain `mj-v7-upscale` / `mj-v7-variation`
/ `mj-v7-inpaint` off the returned `task_id` — no re-upload needed.
* `mj-v7-enhance` only accepts a parent created with `speed: "draft"`.
* Keep the version implicit — `--v` / `--niji` are ignored on V7.
## Related
* [Model page & live playground](https://reapi.ai/models/midjourney-v7)
* [API quickstart](/docs/api/quickstart)
* [Error codes](/docs/api/errors)
---
# midjourney-v8 (https://reapi.ai/docs/midjourney-v8)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Midjourney V8 on reAPI. One `midjourney` model with an `action` parameter
> covering generation, blend, edit, and the upscale / variation / zoom / pan /
> reroll / reshape toolkit; a separate `midjourney-video` model animates a
> still into a short clip. Async-first: submit returns a `task_id`; poll until
> ready. See current pricing on the
> [model page](https://reapi.ai/models/midjourney-v8).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "midjourney",
"prompt": "a serene Japanese garden with cherry blossoms, painterly",
"version": "8.1",
"size": "16:9",
"speed": "fast"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "midjourney",
"prompt": "a serene Japanese garden with cherry blossoms, painterly",
"version": "8.1",
"size": "16:9",
"speed": "fast",
},
timeout=30,
)
task_id = resp.json()["data"][0]["task_id"]
```
```javascript
const resp = await fetch('https://reapi.ai/api/v1/images/generations', {
method: 'POST',
headers: {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'midjourney',
prompt: 'a serene Japanese garden with cherry blossoms, painterly',
version: '8.1',
size: '16:9',
speed: 'fast',
}),
});
const { data } = await resp.json();
```
```go
body := strings.NewReader(`{
"model": "midjourney",
"prompt": "a serene Japanese garden with cherry blossoms, painterly",
"version": "8.1",
"size": "16:9",
"speed": "fast"
}`)
req, _ := http.NewRequest("POST", "https://reapi.ai/api/v1/images/generations", body)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
```
Generation is asynchronous. The call returns a `task_id`; poll
`GET /api/v1/tasks/:task_id` until `status` is `completed`, then read the
image URLs. Generation returns **1–4 images** (content review may filter some)
and is billed **once per request**.
## Endpoint
```
POST https://reapi.ai/api/v1/images/generations # midjourney (image)
POST https://reapi.ai/api/v1/videos/generations # midjourney-video
GET https://reapi.ai/api/v1/tasks/:task_id
```
| Header | Value |
| --------------- | --------------------- |
| `Authorization` | `Bearer YOUR_API_KEY` |
| `Content-Type` | `application/json` |
reAPI accepts only **public http(s) URLs** for every media input
(`image_urls`, `cref`, `sref`, `dref`, `mask_url`, …) — never base64 or
`data:` URIs.
## Operations — `action`
`midjourney` runs one operation per request, selected by `action` (default
`imagine`). All share the images endpoint.
| `action` | Operation | References a prior task? |
| ---------------- | ------------------------------- | ------------------------- |
| `imagine` | Text-to-image / image-to-image | — |
| `blend` | Fuse 2–4 images | — |
| `edits` | Rewrite an image from a prompt | — |
| `upscale` | Upscale one tile | yes (`task_id` + `index`) |
| `variation` | Variations of one tile | yes |
| `high_variation` | Strong variation | yes |
| `low_variation` | Subtle variation | yes |
| `reroll` | Re-roll the whole grid | yes (`task_id`) |
| `zoom` | Zoom out / outpaint | yes |
| `pan` | Extend the canvas directionally | yes |
| `remix_strong` | Strong reshape (v8.1 / v8.2) | yes (`task_id` + `index`) |
| `remix_subtle` | Subtle reshape (v8.1 / v8.2) | yes |
The parent-referencing actions take the reAPI `task_id` of a **completed
`midjourney` task** plus `index` (1–4) to pick which grid image to act on.
reAPI resolves your task id to the upstream reference automatically.
## Generate — `action: "imagine"`
| Field | Type | Required | Default | Notes |
| ------------ | --------- | -------- | --------- | --------------------------- |
| `model` | string | yes | — | `midjourney` |
| `action` | string | no | `imagine` | Operation selector |
| `prompt` | string | yes | — | Image description |
| `image_urls` | string\[] | no | — | Image-to-image reference(s) |
| `speed` | string | no | `relax` | `relax` / `fast` / `turbo` |
The structured parameters below are the JSON form of Midjourney's native
`--flags`. You may also type the flags into `prompt`; a body field overrides
the matching flag. Ranges mirror Midjourney exactly.
| Field | Flag | Range / values | Notes |
| ----------------- | ----------- | ------------------------------------------- | --------------------------------------- |
| `size` | `--ar` | e.g. `16:9`, `1:1`, `9:16` | Aspect ratio |
| `quality` | `--q` | `0.25` / `0.5` / `1` / `2` | Render quality |
| `style` | `--style` | e.g. `raw` | Style preset |
| `version` | `--v` | `8.2` / `8.1` / `7` / `6.1` / `5.2` / `5.1` | Model version |
| `seed` | `--seed` | integer | Reproducible seed |
| `negative_prompt` | `--no` | keywords | What to avoid |
| `stylize` | `--s` | 0–1000 | Stylization strength |
| `chaos` | `--c` | 0–100 | Grid variety |
| `weird` | `--w` | 0–3000 | Unconventional degree |
| `tile` | `--tile` | boolean | Seamless tile |
| `niji` | `--niji` | boolean | Anime model (pair with `version` 7 / 6) |
| `iw` | `--iw` | 0–3 | Image-prompt weight |
| `cw` | `--cw` | 0–100 | Character-reference weight |
| `sw` | `--sw` | 0–1000 | Style-reference weight |
| `cref` | `--cref` | URL | Character reference |
| `sref` | `--sref` | URL | Style reference |
| `dref` | `--dref` | URL | Depth reference |
| `dw` | `--dw` | 0–100 | Depth-reference weight |
| `repeat` | `--repeat` | 2–40 | Repeat the prompt |
| `raw` | `--raw` | boolean | Raw mode (v5.1+) |
| `draft` | `--draft` | boolean | Draft mode (v7+) |
| `hd` | `--hd` | boolean | HD (v8.1 / v8.2) |
| `stop` | `--stop` | 10–100 | Stop early (v5–6.1 / niji 5–6) |
| `extra` | any `--xxx` | string | Appended to the prompt verbatim |
| `metadata` | — | object | Opaque, stored with the task |
Online-verified versions: `8.2`, `8.1`, `7`, `6.1`, `5.2`, `5.1`, plus niji 7 /
niji 6 (set `niji: true` with `version: "7"` or `"6"`). The version does not
change the price.
## Follow-up operations
All follow-ups share the images endpoint and the async `task_id` lifecycle.
Common fields: `task_id` (required, a completed `midjourney` task), `index`
(1–4, which grid image), `speed`.
| `action` | Extra fields | Notes |
| ------------------------------------------------ | ---------------------------- | ---------------------------------------------------------------------------------------------- |
| `upscale` | `index` | Upscale one tile |
| `variation` / `high_variation` / `low_variation` | `index` | Variation strength differs |
| `reroll` | — | Re-rolls the whole grid (no `index`) |
| `zoom` | `index`; `zoom_ratio` | Below 2 = 1.5× outpaint, 2 and up = 2× custom zoom |
| `pan` | `index`; `direction` | `direction`: `left` / `right` / `up` / `down`; v6+ / niji 6 parents only (v5.x fails upstream) |
| `remix_strong` / `remix_subtle` | `index` (required); `prompt` | v8.1 / v8.2 only |
`blend` takes `image_urls` (2–4) and an optional aspect ratio: free-form
`size` (any `w:h`) or three-step `dimensions`
(`SQUARE` / `PORTRAIT` / `LANDSCAPE`); `size` overrides `dimensions`.
`edits` takes `prompt` + `image_urls` plus the same structured params as
`imagine`.
Example — upscale tile 1 of a prior generation:
```json
{
"model": "midjourney",
"action": "upscale",
"task_id": "task_xxx",
"index": 1
}
```
## Image-to-video — `midjourney-video`
Animate a still — or one image of a completed `midjourney` grid — into a
short (\~5 second) clip on the videos endpoint. Supply exactly one of
`image_urls` (a start frame; `prompt` required) or `task_id` (a completed
`midjourney` task of yours; reAPI resolves it upstream automatically).
| Field | Type | Required | Default | Notes |
| -------------- | --------- | -------- | ----------------- | ---------------------------------------------------------------- |
| `model` | string | yes | — | `midjourney-video` |
| `image_urls` | string\[] | one of | — | Start frame (exactly one URL); requires `prompt` |
| `task_id` | string | one of | — | Completed `midjourney` task to animate |
| `index` | integer | no | — | `0`–`3` — which grid image (`task_id` path) |
| `prompt` | string | no | parent prompt | Required with `image_urls`; optional with `task_id` |
| `video_type` | string | no | `vid_1.1_i2v_480` | `vid_1.1_i2v_480` / `_720` / `_start_end_480` / `_start_end_720` |
| `animate_mode` | string | no | `manual` | `manual` / `auto`; `auto` requires `task_id` + `index` |
| `motion` | string | no | `high` | `low` / `high` |
| `batch_size` | integer | no | `1` | `1` / `2` / `4` (clips returned) |
| `end_url` | string | no | — | End frame (upgrades to a start-end type) |
## Pricing
`midjourney` bills **per request** (a 4-image grid for generation; one image
for upscale and the like). The bill depends only on:
* **Operation tier** — generation vs every other image action.
* **Speed** — `relax` / `fast` / `turbo`. The model version never changes the
price.
`midjourney-video` bills per resolution (480p / 720p) × `batch_size`.
Integer credits, `1 credit = $0.001`:
```
credits = ceil(request_price_usd × 1000) # midjourney
credits = ceil(clip_price_usd × batch_size × 1000) # midjourney-video
```
See the [model page](https://reapi.ai/models/midjourney-v8) for current rates.
## Output
Poll `GET /api/v1/tasks/:task_id`. On `completed`:
```json
{
"id": "task_xxx",
"status": "completed",
"output": { "image_urls": ["https://cdn.reapi.ai/.../0.png", "https://cdn.reapi.ai/.../1.png"] }
}
```
`output.image_urls` holds 1–4 URLs (content review may filter some).
`midjourney-video` returns `output.video_urls`. Save them promptly.
## Errors
Customer-facing errors use the standard envelope
`{ error: { code, message, request_id } }`. Common cases:
| Situation | Code |
| ----------------------------------------------------- | -------------------------- |
| Prompt rejected by content review | `CONTENT_POLICY_VIOLATION` |
| Malformed / out-of-range request | `INVALID_REQUEST` |
| Referenced task not found / not yours / not completed | `INVALID_REQUEST` |
| Insufficient credits | `INSUFFICIENT_CREDITS` |
| Upstream generation failed | `UPSTREAM_ERROR` |
See the full catalog at [/docs/api/errors](/docs/api/errors).
## Tips
* Generate with `imagine` first, then chain `upscale` / `variation` / `zoom` /
`pan` / `remix_strong` off the returned `task_id` — no re-upload needed.
* A body field overrides the matching `--flag` in the prompt, so pick one.
* `relax` is the cheapest and the default; `fast` / `turbo` only change queue
priority and price, not the visual result.
* `remix_strong` / `remix_subtle` require a v8.1 / v8.2 parent; use the
variation actions for older versions.
* All references (`cref` / `sref` / `dref`, blend inputs) must be public http(s)
URLs.
## Related
* [Model page & live playground](https://reapi.ai/models/midjourney-v8)
* [API quickstart](/docs/api/quickstart)
* [Error codes](/docs/api/errors)
---
# minimax-m3 (https://reapi.ai/docs/minimax-m3)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> MiniMax M3 is an open-weight model that pairs frontier coding and agentic
> benchmarks with a 1M-token context window and native multimodal input —
> exposed through api.reapi.ai as a drop-in OpenAI-compatible Chat
> Completions endpoint. 1M context, 512K max output, native thinking, image
> and video input, prompt caching, and tool use. The wire `model` id is
> `minimax/minimax-m3`. Current rates live on the
> [model page](https://reapi.ai/models/minimax-m3) and on
> [api.reapi.ai/pricing](https://api.reapi.ai/pricing).
## Quick example
```bash
curl https://api.reapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "minimax/minimax-m3",
"group": "default",
"messages": [
{ "role": "user", "content": "Hello" }
],
"stream": true,
"max_tokens": 4096,
"temperature": 1.0
}'
```
```python
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_KEY",
base_url="https://api.reapi.ai/v1",
)
stream = client.chat.completions.create(
model="minimax/minimax-m3",
messages=[{"role": "user", "content": "Hello"}],
stream=True,
max_tokens=4096,
temperature=1.0,
extra_body={"group": "default"},
)
for chunk in stream:
delta = chunk.choices[0].delta.content or ""
print(delta, end="", flush=True)
```
```js
import OpenAI from "openai";
const client = new OpenAI({
apiKey: "YOUR_API_KEY",
baseURL: "https://api.reapi.ai/v1",
});
const stream = await client.chat.completions.create({
model: "minimax/minimax-m3",
messages: [{ role: "user", content: "Hello" }],
stream: true,
max_tokens: 4096,
temperature: 1.0,
// `group` is an api.reapi.ai-specific extension; pass via extra body.
// @ts-expect-error — not part of the OpenAI types
group: "default",
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "minimax/minimax-m3",
"group": "default",
"messages": []map[string]string{
{"role": "user", "content": "Hello"},
},
"stream": true,
"max_tokens": 4096,
"temperature": 1.0,
})
req, _ := http.NewRequest("POST",
"https://api.reapi.ai/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Authentication
Every request needs a Bearer token. The MiniMax M3 chat workspace lives on
the `api.reapi.ai` platform — sign in there to create a key and top up tokens.
1. Open [api.reapi.ai](https://api.reapi.ai/) and sign in
(or create an account).
2. Generate an API key under **API Keys**.
3. Top up tokens under **Top Up** (pay-as-you-go, billed in USD per 1M
tokens — see [api.reapi.ai/pricing](https://api.reapi.ai/pricing)).
```http
Authorization: Bearer YOUR_API_KEY
```
The chat surface (api.reapi.ai) is a **separate workspace** from the
image/video/audio task gateway at `reapi.ai/api/v1/*`. Keys and balances
do not cross over — a key issued on `reapi.ai/settings/apikeys` will not
authenticate against `api.reapi.ai/v1/chat/completions`, and vice versa.
***
## Endpoint
```http
POST https://api.reapi.ai/v1/chat/completions
```
Drop-in for the OpenAI SDKs — same request shape, same SSE wire format. Set
`base_url` to `https://api.reapi.ai/v1` and `model` to `minimax/minimax-m3`.
***
## Request body
### `model` — string, required
Must be `"minimax/minimax-m3"`. Echoed back in the response envelope.
### `messages` — array, required
Conversation history as an array of message objects. Same shape as the
OpenAI Chat Completions spec, plus content-parts for image and video input:
```json
{
"role": "system" | "user" | "assistant" | "tool",
"content": "string OR content-parts array (text + image_url + video_url parts)"
}
```
Multi-turn history is sent in chronological order — the last message is the
one the model responds to. Strip a prior turn's reasoning content before
re-sending it in `messages`.
### `max_tokens` — integer, default `4096`
Upper bound on output tokens for this response, **including the
chain-of-thought when MiniMax M3 is thinking**. The synchronous API supports
up to **512K** output tokens (128K recommended) — set it generously for
long-form or reasoning-heavy outputs.
### `stream` — boolean, default `false`
When `true`, the response is streamed as server-sent events (SSE) with
`Content-Type: text/event-stream`. Each event is a JSON delta in the OpenAI
format, terminated by a `data: [DONE]` line.
### `temperature` — number, default `1`
Sampling temperature. Lower values produce more deterministic output.
### `top_p` — number, default `0.95`
Nucleus sampling cutoff.
### `tools` / `tool_choice` — optional
Standard OpenAI tool-calling parameters. MiniMax M3 is tuned for agentic,
multi-step workflows with reliable function calling and JSON output, and it
can interleave reasoning with tool calls across a long run.
### `group` — string, default `"default"`
api.reapi.ai-specific extension. Selects a token group on the gateway, which
routes the request to a specific upstream channel pool. Omit if default
routing is fine.
***
## Thinking
MiniMax M3 is a native thinking model: it reasons before it answers and can
interleave reasoning with tool calls during a multi-step run. Thinking is
**adaptive by default** — the model reasons on hard tasks and answers
directly on simple ones. When the model thinks, the chain-of-thought is
returned in a `reasoning_content` field alongside `content`:
```json
{
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"reasoning_content": "Let me work through this step by step...",
"content": "The final answer."
},
"finish_reason": "stop"
}
]
}
```
For latency-sensitive or simple calls you can disable thinking for faster,
cheaper responses.
Strip `reasoning_content` from assistant messages before sending them back
in a follow-up request — the chain-of-thought from a previous turn is not
meant to be re-fed as input.
***
## Multimodal input
MiniMax M3 is natively multimodal — send images and video alongside text via
OpenAI content-parts:
```json
{
"model": "minimax/minimax-m3",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What does this chart show?" },
{
"type": "image_url",
"image_url": { "url": "https://example.com/chart.png" }
}
]
}
]
}
```
Video frames are passed the same way via `video_url` content-parts. Each
image or video counts toward the input token budget based on its resolution
and length.
***
## Prompt caching
MiniMax M3 caches stable prompt prefixes. When a request reuses a cached
prefix, those input tokens bill at a small fraction of the standard input
rate — a big saving for agent loops and chatbots that replay long system
prompts and tool schemas. The
`usage.prompt_tokens_details.cached_tokens` field reports how many input
tokens were served from cache.
***
## Response shape
### Non-streaming (`stream: false`)
```json
{
"id": "chatcmpl-018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"object": "chat.completion",
"created": 1735000000,
"model": "minimax/minimax-m3",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you today?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 9,
"total_tokens": 21,
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
```
When the model thinks, `message.reasoning_content` carries the
chain-of-thought alongside `content`.
### Streaming (`stream: true`)
`Content-Type: text/event-stream`. Each `data:` line is a JSON delta in the
OpenAI chunk format; the final event before `[DONE]` carries the
`finish_reason` (`stop` / `length` / `tool_calls` / `content_filter`).
***
## Pricing
MiniMax M3 is billed **pay-as-you-go in USD** against your api.reapi.ai
token balance. It bills along three dimensions — input tokens, output
tokens, and cache-read tokens. Current rates live on
[api.reapi.ai/pricing](https://api.reapi.ai/pricing) and in the pricing card
at the top of the [model page](https://reapi.ai/models/minimax-m3).
Per-call bill:
```
billable_input = (prompt_tokens - cached_tokens) × input_rate / 1,000,000
cache_read_bill = cached_tokens × cache_read_rate / 1,000,000
output_bill = completion_tokens × output_rate / 1,000,000
```
Output tokens include the chain-of-thought when the model is thinking.
Failed requests are not charged.
***
## Limits
| Limit | Value |
| ------------------- | ----------- |
| Context window | 1M tokens |
| Max output per call | 512K tokens |
Streams that hit the output cap finish with `finish_reason: "length"`;
call again with a continuation message if you need more text.
***
## Errors
The error envelope follows the OpenAI shape — HTTP status, plus a JSON body:
```json
{
"error": {
"message": "...",
"type": "invalid_request_error",
"code": "..."
}
}
```
Common cases:
| Status | When | Notes |
| ------ | ------------------------------------------ | -------------------------------------------- |
| `400` | Bad request shape, unsupported param combo | Check the `messages` array and `model` id |
| `401` | Missing / invalid API key | Re-issue a key at api.reapi.ai |
| `402` | Insufficient balance | Top up at api.reapi.ai |
| `429` | Per-group rate limit hit | Back off, or move to a different `group` |
| `500` | Upstream / gateway error | Safe to retry — failed calls are not charged |
api.reapi.ai does **not** internally retry chat requests. Every customer
call maps to exactly one upstream POST. If a network error reaches you, that
is a one-for-one wire failure and a retry from your side is safe; the gateway
will not double-bill.
***
## Recipes
### Minimum request
```json
{
"model": "minimax/minimax-m3",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "Summarise this in three sentences." }
]
}
```
### Tool use (function calling)
```json
{
"model": "minimax/minimax-m3",
"max_tokens": 4096,
"messages": [
{ "role": "user", "content": "What's the weather in Tokyo today?" }
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Look up the current weather for a city.",
"parameters": {
"type": "object",
"properties": { "city": { "type": "string" } },
"required": ["city"]
}
}
}
],
"tool_choice": "auto"
}
```
### Vision
```json
{
"model": "minimax/minimax-m3",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "Read the error in this screenshot and suggest a fix." },
{
"type": "image_url",
"image_url": { "url": "https://your-cdn.com/screenshot.png" }
}
]
}
]
}
```
### Long-context analysis
```json
{
"model": "minimax/minimax-m3",
"max_tokens": 8192,
"messages": [
{ "role": "system", "content": "" },
{ "role": "user", "content": "List every mention of constraint X with line numbers." }
]
}
```
Keep the long reference block stable across calls so the cache-read rate
applies on subsequent requests.
***
## When to pick MiniMax M3
Pick MiniMax M3 when you want frontier coding and agentic capability at
open-weight pricing:
* **Long-horizon agentic coding** — multi-file refactors, tool-using agents,
and runs that must stay on-task across many steps.
* **Million-token analysis** — whole repositories, long research packs, and
multi-document review in a single call.
* **Multimodal workflows** — tasks that mix screenshots, diagrams, video,
and code in one conversation.
Route lighter traffic (classification, short replies, tight loops) to a
cheaper model on the same key.
***
## Tips
* **Set `max_tokens` generously when the task is hard.** The chain-of-thought
counts toward the output budget; a low cap can truncate before the final
answer.
* **Strip `reasoning_content` before the next turn.** Re-feeding a prior
turn's chain-of-thought as input is not supported.
* **Stream by default for chat UX.** Streaming cuts perceived latency.
* **Cache stable prefixes.** Reuse the same system prompt and tool schemas
across calls to bill repeated input at the low cache-read rate.
* **Disable thinking for simple, latency-sensitive calls.** Adaptive thinking
already skips reasoning on easy prompts, but you can force it off when you
never need the chain-of-thought.
***
## Related
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Errors catalog](/docs/api/errors)
---
# Multistem Splitter API (https://reapi.ai/docs/multistem-splitter)
# Multistem Splitter API
Use `audio-multistem` to extract up to six supported stems from one public
audio URL.
## Endpoint
```http
POST /api/v1/audio/generations
```
Poll completion with:
```http
GET /api/v1/tasks/{id}
```
## Example
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer $REAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "audio-multistem",
"audio_url": "https://cdn.example.com/song.wav",
"stem_list": ["vocals", "drum", "bass"],
"encoder_format": "mp3"
}'
```
## Parameters
| Field | Type | Required | Notes |
| ------------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Use `audio-multistem`. |
| `audio_url` | URL | yes | Public HTTP(S) source audio URL. |
| `stem_list` | enum\[] | yes | 1-6 values: `vocals`, `drum`, `piano`, `bass`, `electric_guitar`, `acoustic_guitar`. |
| `splitter` | enum | no | `auto`, `andromeda`, `perseus`, `orion`, `phoenix`, `lyra`, or `lynx`. `auto` is omitted upstream so the default engine is used. |
| `extraction_level` | enum | no | `deep_extraction` or `clear_cut`. Defaults to `deep_extraction`. |
| `dereverb_enabled` | boolean | no | Enable dereverb cleanup. |
| `encoder_format` | enum | no | `mp3`, `wav`, `flac`, `aac`, or `ogg`. |
## Output
Completed tasks return `output.audio_urls` and, when available, labeled
`output.tracks`.
Multistem jobs are billed per rounded-up source minute, multiplied by the
selected stem count. See current rate on the
[Multistem Splitter model page](https://reapi.ai/models/multistem-splitter).
---
# Mureka V9 Song API (https://reapi.ai/docs/mureka-v9-song)
# Mureka V9 Song API
Use `mureka-v9-song` to turn written lyrics into one or more fully
produced songs. Each call is asynchronous: submit the task, poll for
completion, then fetch the rendered audio from the returned URLs.
## Endpoint
```http
POST /api/v1/audio/generations
```
Poll completion with:
```http
GET /api/v1/tasks/{id}
```
The task stays in `processing` until the song is rendered; final URLs
land in `output.audio_urls` when the status flips to `completed`.
## Example
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer $REAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "mureka-v9-song",
"lyrics": "Verse 1...\nChorus...\nVerse 2...",
"prompt": "Emotional pop ballad with piano, warm strings, cinematic chorus, expressive female vocal, polished modern production",
"number_of_songs": 1,
"output_format": "mp3"
}'
```
## Parameters
| Field | Type | Required | Notes |
| ----------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `mureka-v9-song`. |
| `lyrics` | string | yes | Lyrics for the song. Up to 3000 characters. |
| `prompt` | string | no | Optional style prompt — genre, mood, instrumentation, vocal tone, tempo, production feel. Up to 1024 characters. |
| `number_of_songs` | integer | no | `1`, `2`, or `3`. Defaults to `1`. Each additional song multiplies the per-request cost. |
| `output_format` | enum | no | `mp3`, `wav`, or `flac`. Defaults to `mp3`. |
| `reference_id` | string | no | Optional Mureka reference-track ID for tighter style guidance. |
| `vocal_id` | string | no | Optional Mureka vocal-reference ID. |
| `melody_id` | string | no | Optional Mureka melody-reference ID. |
The three reference IDs are minted by Mureka's own upload system. ReAPI
forwards them verbatim and does not proxy uploads — bring an ID you
already have, or omit them entirely for fully generated songs.
## Output
Successful tasks return one URL per generated song in
`output.audio_urls`. With `number_of_songs: 3` the array contains three
URLs, all in the same format selected by `output_format`.
## Pricing
Billed per generated song. Only `number_of_songs` affects pricing —
`prompt`, `output_format`, and the three reference IDs are free to use.
See current rates on the
[Mureka V9 model page](https://reapi.ai/models/mureka-v9-song).
## Tips
* Structure lyrics with clear `Verse` / `Chorus` / `Bridge` blocks for
better arrangement.
* Use the style prompt to pin genre, tempo, and vocal character —
Mureka follows these strongly.
* Set `number_of_songs > 1` when you want quick variations from the
same lyrics.
* Use a reference ID only when you want the model to track a specific
vocal or melody — without one it composes freely from the lyrics +
prompt.
* Stick with `mp3` while iterating; switch to `wav` or `flac` for
final mastering.
---
# Music Extractor API (https://reapi.ai/docs/music-extractor)
# Music Extractor API
Use `audio-music-extractor` to extract background music from a public source
audio URL.
## Endpoint
```http
POST /api/v1/audio/generations
```
Poll completion with:
```http
GET /api/v1/tasks/{id}
```
## Example
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer $REAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "audio-music-extractor",
"audio_url": "https://cdn.example.com/mix.wav",
"encoder_format": "mp3"
}'
```
## Parameters
| Field | Type | Required | Notes |
| ------------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Use `audio-music-extractor`. |
| `audio_url` | URL | yes | Public HTTP(S) source audio URL. |
| `splitter` | enum | no | `auto`, `andromeda`, `perseus`, `orion`, `phoenix`, `lyra`, or `lynx`. `auto` is omitted upstream so the default engine is used. |
| `dereverb_enabled` | boolean | no | Enable dereverb cleanup. |
| `encoder_format` | enum | no | `mp3`, `wav`, `flac`, `aac`, or `ogg`. |
## Output
Completed tasks return `output.audio_urls` and, when available, labeled
`output.tracks`.
Audio jobs are billed per rounded-up source minute. See current rate on
the [Music Extractor model page](https://reapi.ai/models/music-extractor).
---
# music-video-1-0 (https://reapi.ai/docs/music-video-1-0)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Music Video 1.0 turns a **song** into a **music video**. Pass an audio
> track plus **1-7 reference images** (the visual style/model) and the API
> returns a video synced to the song. One async endpoint, five aspect ratios,
> up to 1080P, optional burned-in subtitles. Billing is per second of the
> song's server-probed length. See current pricing on the
> [model page](https://reapi.ai/models/music-video-1-0).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "music-video-1-0",
"reference_image_urls": ["https://your-cdn.com/style.png"],
"audio_url": "https://your-cdn.com/song.mp3",
"aspect_ratio": "16:9",
"resolution": "540P"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "music-video-1-0",
"reference_image_urls": ["https://your-cdn.com/style.png"],
"audio_url": "https://your-cdn.com/song.mp3",
"aspect_ratio": "16:9",
"resolution": "540P",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "music-video-1-0",
reference_image_urls: ["https://your-cdn.com/style.png"],
audio_url: "https://your-cdn.com/song.mp3",
aspect_ratio: "16:9",
resolution: "540P",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "music-video-1-0",
"reference_image_urls": []string{"https://your-cdn.com/style.png"},
"audio_url": "https://your-cdn.com/song.mp3",
"aspect_ratio": "16:9",
"resolution": "540P",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "music-video-1-0",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
generated MP4 URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Request body
### `model` — required
`string`. Must be `"music-video-1-0"`.
### `reference_image_urls` — required
`string[]`. **1 to 7** public HTTP(S) image URLs (JPEG / PNG / WebP). These are
the style / model references that fix the visual identity of the music video.
**No `data:` URIs.** reApi rejects base64 inputs platform-wide. Upload to public
storage (your own CDN, S3, R2…) and pass the URL.
### `audio_url` — required
`string`. Public HTTP(S) URL of the **song** (MP3). The audio must be **10 to
300 seconds** (up to 5 minutes). The output music video length tracks the song,
and the **billable second count is the song's server-probed duration**, not a
client estimate.
### `aspect_ratio` — string, default `"16:9"`
Output frame shape.
| Value | Orientation |
| -------- | ------------------- |
| `"1:1"` | Square |
| `"16:9"` | Landscape (default) |
| `"9:16"` | Portrait |
| `"4:3"` | Landscape |
| `"3:4"` | Portrait |
### `resolution` — string, default `"540P"`
Output clarity. Higher resolutions take longer to render.
| Value | Notes |
| --------- | ----------------- |
| `"540P"` | Fastest (default) |
| `"720P"` | Balanced |
| `"1080P"` | Highest clarity |
### `name` — string, optional
A label for the track. Shown on subtitles when `add_subtitle` is enabled.
### `prompt` — string, optional
Up to **3000 characters**. Steers the visual style, mood, and scene direction
of the music video.
### `add_subtitle` — boolean, default `false`
Burn lyric subtitles into the music video. With `add_subtitle: true` and no
`srt_url`, subtitles are generated from the song.
### `subtitle_color` — string, default `"#FFFFFF"`
Hex color for burned-in subtitles (e.g. `"#FF0000"`).
### `srt_url` — string, optional
Public HTTP(S) URL of a `.srt` subtitle file. Overrides auto-generated
subtitles when `add_subtitle` is on.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "music-video-1-0",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ---------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds the MP4 |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
***
## Pricing
Flat **per second of the song**. The billable second count is the audio's
**server-probed** length — reApi measures the uploaded track server-side rather
than trusting a client value.
**Bill formula** (1 credit = $0.001):
```
billable_seconds = min(ceil(server_probed_song_seconds), 300)
bill_usd = per_second_usd × billable_seconds
credits = ceil(bill_usd × 1000)
```
See the current per-second rate on the
[model page](https://reapi.ai/models/music-video-1-0). Failed jobs refund
automatically. A probe failure returns `400 PRICING_UNAVAILABLE` (code `30002`)
with no charge.
The playground's draft estimate may show a default before the upload finishes —
the authoritative number is computed at submit, after the server probes the
song.
***
## Validation errors
All cases below return HTTP 400. Pattern-match on `code`, not `message` —
message strings carry request-specific context and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| -------------------------------------- | ------- | --------------------------------------------------------------- |
| `reference_image_urls` missing / empty | `20002` | `reference_image_urls is required` |
| More than 7 reference images | `20003` | `reference_image_urls accepts at most 7 items` |
| `audio_url` missing | `20002` | `audio_url is required` |
| `aspect_ratio` / `resolution` invalid | `20003` | `invalid aspect_ratio (allowed: 1:1 / 16:9 / 9:16 / 4:3 / 3:4)` |
| `subtitle_color` not a hex color | `20003` | `subtitle_color must be a hex color like #FFFFFF` |
| Any media field carrying a `data:` URI | `20003` | `audio_url must be a public http(s) URL` |
| Song probe fails (network / format) | `30002` | `Could not determine source audio duration for billing: …` |
| Song length outside the 10-300s window | `80007` | Provider rejected the request as invalid |
The full envelope is `{ "error": { "code", "message", "request_id" } }` — see
[Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Minimum request
```json
{
"model": "music-video-1-0",
"reference_image_urls": ["https://your-cdn.com/style.png"],
"audio_url": "https://your-cdn.com/song.mp3",
"aspect_ratio": "16:9",
"resolution": "540P"
}
```
### Portrait lyric video, 1080P, with subtitles
```json
{
"model": "music-video-1-0",
"reference_image_urls": [
"https://your-cdn.com/style-1.png",
"https://your-cdn.com/style-2.png"
],
"audio_url": "https://your-cdn.com/song.mp3",
"aspect_ratio": "9:16",
"resolution": "1080P",
"prompt": "Dreamy pastel anime style, soft lighting, visuals shift with each chorus",
"add_subtitle": true,
"subtitle_color": "#FFE0F0"
}
```
### Bring your own subtitle file
```json
{
"model": "music-video-1-0",
"reference_image_urls": ["https://your-cdn.com/style.png"],
"audio_url": "https://your-cdn.com/song.mp3",
"aspect_ratio": "16:9",
"resolution": "720P",
"add_subtitle": true,
"srt_url": "https://your-cdn.com/lyrics.srt"
}
```
***
## Tips
* **Cost tracks the song.** A 90-second track bills \~90 seconds; trim the audio
to control spend. Songs must be 10 to 300 seconds.
* **Use reference images to lock the look.** Pass 1-7 style/model images;
reuse the same set across tracks for a consistent channel aesthetic.
* **Prompt the mood, not just the scene.** Phrasing the energy ("dynamic lights
pulsing on the downbeat") sharpens the result.
* **Subtitles, three ways.** Leave `add_subtitle` off for a clean cut, on for
auto-generated lyrics, or supply your own `srt_url` for exact timing.
* **Start at 540P.** Validate the look on the fastest tier, then re-run at
720P / 1080P for the final take.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# nano-banana-2-lite (https://reapi.ai/docs/nano-banana-2-lite)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Google DeepMind's Nano Banana 2 Lite (aka **Gemini 3.1 Flash-Lite Image**) on
> reAPI — the speed-focused member of the Nano Banana family: fast **1K
> text-to-image** and **prompt-based image editing** with up to 10 reference
> images. Submit returns a `task_id`; poll until ready. See current pricing on
> the [model page](https://reapi.ai/models/nano-banana-2-lite).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana-2-lite",
"prompt": "a pig on the grass, cinematic light",
"aspect_ratio": "16:9"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "nano-banana-2-lite",
"prompt": "a pig on the grass, cinematic light",
"aspect_ratio": "16:9",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "nano-banana-2-lite",
prompt: "a pig on the grass, cinematic light",
aspect_ratio: "16:9",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "nano-banana-2-lite",
"prompt": "a pig on the grass, cinematic light",
"aspect_ratio": "16:9",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_019f25700fef760e879fc22ad9de512c",
"model": "nano-banana-2-lite",
"status": "processing",
"created_at": 1783039530
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls` holds the
generated image URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
The generation mode is implicit — there is no `mode` field:
* **No `image_urls` (or an empty array)** → text-to-image.
* **`image_urls` set** → prompt-based editing / reference-driven generation
using up to 10 input images.
Each request produces **exactly one 1K image** (no `n` parameter, no
resolution control).
***
## Request body
### `model` — string, required
Must be `nano-banana-2-lite`.
### `prompt` — string, required
Up to **20,000 characters**. For text-to-image, describe subject, scene, style
and composition. For editing, state the change you want ("replace the
background with…", "make it nighttime, keep the subject unchanged").
### `image_urls` — string\[], optional
Up to **10 public http(s) image URLs** used as edit sources or visual
references. Accepted types: JPEG, PNG, WebP; up to 30 MB per image. Omit it
(or pass an empty array) for pure text-to-image.
Base64 / `data:` URIs are rejected platform-wide — host the image on a
public URL first.
### `aspect_ratio` — string, default `auto`
One of `1:1`, `1:4`, `1:8`, `2:3`, `3:2`, `3:4`, `4:1`, `4:3`, `4:5`, `5:4`,
`8:1`, `9:16`, `16:9`, `21:9`, `auto`. With `auto` the model chooses the
ratio itself (for edits it follows the source image).
***
## Pricing
Nano Banana 2 Lite bills a flat rate **per generated image**, independent of
aspect ratio and mode (text-to-image and editing cost the same). Each request
produces one image:
```
credits = ceil(per_image_usd × 1000)
```
where `1 credit = $0.001 USD`. It is one of the lowest per-image rates on the
platform. Failed and rejected requests are not charged.
The exact per-image credit cost surfaces on the
[model page](https://reapi.ai/models/nano-banana-2-lite) and through the
playground estimator before submit.
***
## Response
The poll envelope returns the image URL in `output.image_urls`:
```json
{
"id": "task_019f25700fef760e879fc22ad9de512c",
"model": "nano-banana-2-lite",
"status": "completed",
"output": {
"image_urls": [
"https://cdn.reapi.ai/...jpeg"
]
}
}
```
Generated URLs expire — mirror them to your own storage if you need long-term
retention.
***
## Errors
Failures return the standard reAPI envelope `{ error: { code, message,
request_id } }`. Common cases:
* Invalid input (prompt over 20,000 characters, more than 10 `image_urls`, an
unsupported `aspect_ratio`) → `400`.
* Insufficient credits → `402`.
* Rate limited → `429`.
See the full catalog at [/docs/api/errors](/docs/api/errors).
***
## Tips
* Nano Banana 2 Lite is the **speed tier** of the Nano Banana family — pick it
for drafts, previews and high-volume 1K assets; step up to
[Nano Banana 2](/docs/gemini-3-1-flash-image-preview) when you need 2K/4K
output or maximum quality.
* For edits, reference what should **stay unchanged** ("keep the furniture and
composition unchanged") — the model is strong at targeted, consistent edits.
* `aspect_ratio: "auto"` follows the source image on edits; set an explicit
ratio when you need a fixed canvas (e.g. `16:9` thumbnails).
***
## Related
* [Nano Banana 2](/docs/gemini-3-1-flash-image-preview)
* [Nano Banana Pro](/docs/gemini-3-pro-image-preview)
* [Z-Image](/docs/z-image)
* [Tasks API](/docs/api/tasks)
* [Error codes](/docs/api/errors)
---
# pixverse-v6 (https://reapi.ai/docs/pixverse-v6)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Pixverse v6 — async video generation. **One model id** (`pixverse-v6`) and
> one endpoint cover five modes: **text-to-video**, **image-to-video**,
> **first/last-frame** transition, **multi-reference fusion**, and **video
> extend**. Mode is implicit — the fields you send pick it. 1–15 second
> outputs at 360p / 540p / 720p / 1080p, optional generated audio. See current
> pricing on the [model page](https://reapi.ai/models/pixverse-v6).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "pixverse-v6",
"prompt": "A neon-lit Tokyo alley at night, light rain, anamorphic flare",
"size": "21:9",
"resolution": "720p",
"duration": 8
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "pixverse-v6",
"prompt": "A neon-lit Tokyo alley at night, light rain, anamorphic flare",
"size": "21:9",
"resolution": "720p",
"duration": 8,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "pixverse-v6",
prompt: "A neon-lit Tokyo alley at night, light rain, anamorphic flare",
size: "21:9",
resolution: "720p",
duration: 8,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "pixverse-v6",
"prompt": "A neon-lit Tokyo alley at night, light rain, anamorphic flare",
"size": "21:9",
"resolution": "720p",
"duration": 8,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "pixverse-v6",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls`
holds the generated MP4 URL, valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Mode routing
`pixverse-v6` picks its mode from **which fields you send** — there is no
`mode` parameter. Matching is by priority; the first satisfied condition wins:
| Send | Mode | What it does |
| ---------------------------------------- | -------------------- | -------------------------------------------------------- |
| `prompt` only | **Text-to-video** | Generate from text. `size` applies. |
| `image_urls` | **Image-to-video** | Animate from a single starting frame (first used). |
| `first_frame_image` + `last_frame_image` | **First/Last-frame** | Interpolate a transition between two frames. |
| `img_references` | **Multi-reference** | Fuse 1–7 reference images into one clip. `size` applies. |
| `extend_from_task_id` | **Video extend** | Continue a completed `pixverse-v6` clip. |
**Field rules.**
* `prompt` is **required** in every mode.
* `first_frame_image` and `last_frame_image` must be sent **together**.
* `img_references` accepts **1 to 7** entries.
* First/Last-frame mode accepts only **5 or 8** second `duration`.
* `size` is honored only in text-to-video and multi-reference fusion.
***
## Request body
### `model` — required
`string`. Always `"pixverse-v6"`.
### `prompt` — string, required
Up to **5,000 characters**. Required in every mode. Empty / whitespace-only
prompts are treated as missing.
### `resolution` — string, default `"540p"`
One of `360p` / `540p` / `720p` / `1080p`. Drives pricing.
### `duration` — integer, default `5`
Output length in seconds. Any integer in `[1, 15]`. First/Last-frame mode
accepts only `5` or `8`. Drives pricing linearly:
`ceil(per_second_usd × duration × 1000)` credits (1 credit = $0.001).
### `size` — string, default `"16:9"`
Output aspect ratio. Honored in **text-to-video** and **multi-reference
fusion** only (other modes derive the ratio from the source media). One of:
| Value | Shape |
| ------ | -------------- |
| `16:9` | Landscape |
| `4:3` | Landscape 4:3 |
| `1:1` | Square |
| `3:4` | Portrait 3:4 |
| `9:16` | Portrait |
| `2:3` | Portrait 2:3 |
| `3:2` | Landscape 3:2 |
| `21:9` | Cinematic wide |
### `seed` — integer, default `0`
Reproducibility hint. Range `[0, 2147483647]`. Same prompt plus seed returns
a similar (not bit-for-bit identical) result.
### `negative_prompt` — string
Up to **2,048 characters**. Describes content to keep out of the frame.
### `audio` — boolean, default `false`
When `true`, the model synthesizes a soundtrack alongside the video. Audio
**raises the per-second rate** (see Pricing). Default `false` is silent.
### `watermark` — boolean, default `false`
When `true`, adds a watermark to the bottom-right corner.
### `motion_mode` — string
`pixverse-v6` supports only `"normal"`. Other values are rejected upstream.
### `generate_multi_clip_switch` — boolean, default `false`
When `true`, generates a multi-segment continuous video. Supported in
**text-to-video** and **image-to-video** only.
### `image_urls` — string\[] (image-to-video)
Public HTTP(S) URL array. The model uses the **first** entry as the starting
frame.
### `first_frame_image` / `last_frame_image` — string (first/last-frame)
Public HTTP(S) URLs. Must be sent **as a pair**; the model interpolates motion
from the first frame to the last over a 5 or 8 second clip.
### `img_references` — string\[] (multi-reference fusion)
**1 to 7** public HTTP(S) URLs. The model fuses subjects from each reference
into one clip.
### `extend_from_task_id` — string (video extend)
The reAPI task id (`task_...`) of one of **your own completed `pixverse-v6`**
videos. Do not pass the upstream provider task id — reAPI resolves that
internally before sending the extend request. The model continues the scene
from where that clip ended.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — every URL
field on this endpoint must be a public HTTP(S) URL. Upload to your own object
storage (S3, R2, OSS, …) and pass the URL.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in
over time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "pixverse-v6",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
`output.video_urls` URLs are valid for **7 days**. Re-host to your own
storage if you need them longer. To extend a clip later, keep the task `id` —
pass it as `extend_from_task_id`.
***
## Validation errors
All cases below return HTTP 400. Pattern-match on `code`, not `message` —
message strings carry request-specific context (field names, observed values)
and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| ---------------------------------------------------- | ------- | ------------------------------------------------------------------ |
| `prompt` missing / empty | `20002` | `prompt is required` |
| `prompt` longer than 5,000 chars | `20007` | `prompt exceeds 5000 characters (got N)` |
| Unknown `resolution` | `20003` | `invalid resolution "X" (allowed: 360p / 540p / 720p / 1080p)` |
| `duration` outside `[1, 15]` | `20003` | `duration must be 1-15 seconds, got N` |
| `duration` not 5/8 in first/last-frame mode | `20003` | `first-last-frame transition supports only 5 or 8 second duration` |
| Only one of `first_frame_image` / `last_frame_image` | `20003` | `first_frame_image and last_frame_image must be provided together` |
| `img_references` length > 7 | `20003` | `img_references supports at most 7 images, got N` |
| `motion_mode` not `normal` | `20003` | `motion_mode must be "normal"` |
| A URL field carrying a `data:` URI or non-http(s) | `20003` | `image_urls entries must be public http(s) URLs` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` —
see [Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Text-to-video — minimum request
```json
{
"model": "pixverse-v6",
"prompt": "A corgi running through a sunflower field at golden hour"
}
```
### Text-to-video — full parameters
```json
{
"model": "pixverse-v6",
"prompt": "A neon-lit Tokyo alley at night, light rain, anamorphic lens flare",
"size": "21:9",
"resolution": "720p",
"duration": 8,
"seed": 42,
"audio": true
}
```
### Image-to-video — animate a single frame
```json
{
"model": "pixverse-v6",
"prompt": "Camera slowly zooms in, gentle wind moves the leaves",
"image_urls": ["https://your-cdn.com/first_frame.jpg"],
"resolution": "540p",
"duration": 5
}
```
### First/Last-frame transition
```json
{
"model": "pixverse-v6",
"prompt": "Transform smoothly from a puppy to a cat",
"first_frame_image": "https://your-cdn.com/puppy.jpg",
"last_frame_image": "https://your-cdn.com/cat.jpg",
"resolution": "540p",
"duration": 5
}
```
### Multi-reference fusion
```json
{
"model": "pixverse-v6",
"prompt": "A girl wearing the outfit from image 2, holding the cat from image 3",
"img_references": [
"https://your-cdn.com/character.jpg",
"https://your-cdn.com/outfit.jpg",
"https://your-cdn.com/cat.jpg"
],
"size": "9:16",
"resolution": "720p",
"duration": 5
}
```
### Video extend
```json
{
"model": "pixverse-v6",
"prompt": "The character now walks into a forest",
"extend_from_task_id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"resolution": "540p",
"duration": 5
}
```
***
## Choosing a mode
| Need | Send |
| ------------------------------------ | -------------------------------------------------------- |
| Generate from text | `prompt` only — `size` applies |
| Animate a still | `prompt + image_urls` |
| Smooth transition between two frames | `first_frame_image + last_frame_image` (duration 5/8) |
| Combine subjects from several images | `prompt + img_references` (1–7) |
| Continue an existing clip | `prompt + extend_from_task_id` |
| Cut spend | Drop `resolution`, shorten `duration`, leave `audio` off |
***
## Polling pattern
The task endpoint behaves identically to other video tasks — the completed
`output` carries `video_urls`. A pragmatic schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in a few minutes.
***
## Pricing
Per-second × resolution. Turning **audio** on raises the per-second rate at
every tier; every other parameter is free. See current rates on the
[Pixverse v6 model page](https://reapi.ai/models/pixverse-v6).
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_second_usd × duration × 1000)
```
Failed jobs refund automatically.
***
## Tips
* **Prompt motion, not just scene.** "Slow push-in, warm tones, shallow depth
of field" outperforms a pure noun-list of what's on screen.
* **Keep references on-topic.** Multi-reference fusion works best when each
image contributes a distinct, clearly-framed subject (a face, an outfit, a
prop) rather than overlapping whole scenes.
* **Two frames need to make sense as endpoints.** First/Last interpolation is
smoothest when the two frames share enough subject and composition.
* **Chain extends one beat at a time.** Build a longer sequence by extending
the latest clip's task id repeatedly instead of asking for one long shot.
* **Audio is opt-in here.** Leave `audio` off unless you need a soundtrack —
it raises the per-second rate.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# qwen-image-2 (https://reapi.ai/docs/qwen-image-2)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Alibaba's Qwen Image 2 on reAPI — one async endpoint for **generation and
> editing**. Write a prompt for **text-to-image**, or attach one source image
> to **edit it in plain language**. Known for **structured in-image text**
> (posters, slides, infographics, comics). Submit returns a `task_id`; poll
> until ready. See current pricing on the
> [model page](https://reapi.ai/models/qwen-image-2).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-image-2",
"prompt": "a vintage travel poster that reads VISIT MARS in bold retro type",
"image_size": "16:9"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "qwen-image-2",
"prompt": "a vintage travel poster that reads VISIT MARS in bold retro type",
"image_size": "16:9",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "qwen-image-2",
prompt: "a vintage travel poster that reads VISIT MARS in bold retro type",
image_size: "16:9",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "qwen-image-2",
"prompt": "a vintage travel poster that reads VISIT MARS in bold retro type",
"image_size": "16:9",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "qwen-image-2",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls` holds the
generated image URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Modes
There is no `mode` field — the modality is implicit in the request shape:
* **Text-to-image** — no `image_url`. `prompt` is required.
* **Image editing** — pass a single `image_url`. `prompt` describes the change
(background swap, object edit, restyle, in-frame text rewrite).
Each request produces **exactly one image** — there is no `n` parameter.
***
## Request body
### `model` — string, required
Must be `qwen-image-2`.
### `prompt` — string, required
Up to **800 characters**. Required for both text-to-image and editing.
### `image_url` — string, optional
A single source image for editing. **Public HTTPS URL only** — base64 / `data:`
URIs are rejected at the gateway. Passing it switches the request to editing
mode; omit it for text-to-image.
### `image_size` — string, optional, default `16:9`
Output aspect ratio.
* **Text-to-image** accepts: `1:1`, `3:4`, `4:3`, `9:16`, `16:9`.
* **Editing** additionally accepts: `2:3`, `3:2`, `21:9`.
`2:3`, `3:2`, and `21:9` are **editing-only**. Requesting one without an
`image_url` (text-to-image) is rejected with `400`.
### `output_format` — string, optional, default `png`
One of `png` or `jpeg`.
### `seed` — integer, optional
The same seed with the same prompt yields a repeatable result.
### `nsfw_checker` — boolean, optional, default `false`
Enables upstream content moderation. Off by default.
***
## Pricing
Qwen Image 2 bills a flat rate **per generated image**, independent of
modality, resolution, and aspect ratio. Each request produces one image:
```
credits = ceil(per_image_usd × 1000)
```
where `1 credit = $0.001 USD`. Failed and rejected requests are not charged.
The exact per-image credit cost surfaces on the
[model page](https://reapi.ai/models/qwen-image-2) and through the playground
estimator before submit.
***
## Response
The poll envelope returns the image URL in `output.image_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "qwen-image-2",
"status": "completed",
"output": {
"image_urls": [
"https://cdn.reapi.ai/...png"
]
}
}
```
Generated URLs expire — mirror them to your own storage if you need long-term
retention.
***
## Errors
Failures return the standard reAPI envelope `{ error: { code, message,
request_id } }`. Common cases:
* Invalid input (prompt over 800 characters, an editing-only `image_size` in
text-to-image, a non-HTTPS `image_url`) → `400`.
* Insufficient credits → `402`.
* Rate limited → `429`.
See the full catalog at [/docs/api/errors](/docs/api/errors).
***
## Tips
* The modality is decided by your inputs, not a flag — omit `image_url` for
generation, include it to edit.
* Put the literal text you want rendered **in the prompt** — Qwen Image 2 is
tuned for legible in-image typography, so spelling out headline copy works.
* For wide layouts, `21:9` is available only when editing (pass an `image_url`).
* To produce a set of images, send multiple requests — each is billed as one
image.
***
## Related
* [Image generation models](/docs/gpt-image-2)
* [Wan 2.7 Image](/docs/wan-2-7-image)
* [Tasks API](/docs/api/tasks)
* [Error codes](/docs/api/errors)
---
# seedance-2-0-mini (https://reapi.ai/docs/seedance-2-0-mini)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> ByteDance Seedance 2.0 **Mini** — the low-cost tier of the Seedance 2.0
> family. A single async video endpoint that auto-routes between
> **T2V / I2V / R2V** based on which media field your request carries.
> 480p or 720p, 4–15 second outputs. See current pricing on the
> [model page](https://reapi.ai/models/seedance-2-0-mini).
Generation is **asynchronous**: the POST returns a task id, then poll
[`GET /api/v1/tasks/{id}`](/docs/api/tasks) until `status` is `completed`.
**URL-only media.** All media inputs must be public `http(s)` URLs.
base64 / `data:` URI media is **not accepted** by ReAPI — even where
upstream docs show base64 examples.
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0-mini",
"prompt": "A kitten yawning at the camera, soft morning light",
"resolution": "720p",
"aspect_ratio": "16:9",
"duration": 5
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "seedance-2.0-mini",
"prompt": "A kitten yawning at the camera, soft morning light",
"resolution": "720p",
"aspect_ratio": "16:9",
"duration": 5,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "seedance-2.0-mini",
prompt: "A kitten yawning at the camera, soft morning light",
resolution: "720p",
aspect_ratio: "16:9",
duration: 5,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "seedance-2.0-mini",
"prompt": "A kitten yawning at the camera, soft morning light",
"resolution": "720p",
"aspect_ratio": "16:9",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var out map[string]any
json.NewDecoder(resp.Body).Decode(&out)
fmt.Println(out)
}
```
## Endpoint
```http
POST /api/v1/videos/generations
Authorization: Bearer rk_live_xxx
Content-Type: application/json
```
Submitting returns a task id; poll [`GET /api/v1/tasks/{id}`](/docs/api/tasks)
for the result. Polling does not consume credits.
## Parameters
Exactly 12 input fields. The mode is implicit — which media fields you set
decides text-to-video, image-to-video, or reference-driven generation.
| Parameter | Type | Required | Default | Description |
| ---------------------- | --------- | ------------------------ | ------- | ------------------------------------------------------------------------------------------------------------ |
| `prompt` | string | T2V: yes · I2V / R2V: no | — | Text prompt. Required for text-to-video; optional when a `first_frame_url` or `reference_image_urls` is set. |
| `first_frame_url` | string | I2V | — | First-frame image, a single public `http(s)` URL. Presence selects **image-to-video**. |
| `last_frame_url` | string | no | — | Optional last-frame image, a public `http(s)` URL. |
| `reference_image_urls` | string\[] | R2V | — | **1–9** reference image URLs. Presence selects **reference-to-video**. |
| `reference_video_urls` | string\[] | no | — | **Up to 3** reference video URLs. |
| `reference_audio_urls` | string\[] | no | — | **Up to 3** reference audio URLs. |
| `generate_audio` | boolean | no | `false` | Generate an audio track for the output video. |
| `resolution` | enum | no | `720p` | `480p` · `720p`. |
| `aspect_ratio` | enum | no | `16:9` | `16:9` `9:16` `1:1` `4:3` `3:4` `21:9` `adaptive`. |
| `duration` | integer | no | `5` | Output length in seconds, `4`–`15`. |
| `web_search` | boolean | no | `false` | Allow the model to ground the prompt with web search. |
| `nsfw_checker` | boolean | no | `true` | Run the upstream NSFW content check. |
All media inputs (`first_frame_url`, `last_frame_url`,
`reference_image_urls`, `reference_video_urls`, `reference_audio_urls`)
must be public HTTP(S) URLs — base64 / `data:` URIs are rejected.
## Modes
Mode is implicit — selected by which inputs you send:
| Mode | Trigger | prompt |
| ---------------------------- | ----------------------------------------------- | -------- |
| **Text-to-video (T2V)** | no media | required |
| **Image-to-video (I2V)** | `first_frame_url` (optionally `last_frame_url`) | optional |
| **Reference-to-video (R2V)** | `reference_image_urls` (1–9) | optional |
```json
// I2V — animate a first frame
{ "model": "seedance-2.0-mini", "first_frame_url": "https://…/frame.jpg", "resolution": "720p", "duration": 5 }
// R2V — keep subjects consistent across the clip
{ "model": "seedance-2.0-mini", "prompt": "the woman walks through the plaza", "reference_image_urls": ["https://…/a.jpg", "https://…/b.jpg"], "resolution": "720p", "duration": 5 }
```
## More examples
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0-mini",
"first_frame_url": "https://example.com/frame.jpg",
"resolution": "720p",
"duration": 5
}'
```
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.0-mini",
"prompt": "the subject walks through a neon-lit street at night",
"reference_image_urls": [
"https://example.com/a.jpg",
"https://example.com/b.jpg"
],
"resolution": "720p",
"duration": 5
}'
```
## Pricing
Per-second, billed by **resolution × whether a reference video is uploaded**.
A request that carries `reference_video_urls` bills at a cheaper reference
tier. See current 480p / 720p rates on the
[model page](https://reapi.ai/models/seedance-2-0-mini).
**Bill formula** (1 credit = $0.001):
| Input | Billable seconds |
| ----------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| No `reference_video_urls` (text / image / first-last-frame / reference-image) | `duration` you sent (default 5) |
| With `reference_video_urls` | `duration` + `ceil(sum of source video seconds)` — the vendor processes the input clip(s) AND produces the output; both are billable. Failed probe → `400 PRICING_UNAVAILABLE`, no charge. |
Final bill: `ceil(per_second_usd × billable_seconds × 1000)` credits.
Failed jobs refund automatically.
## Output
On success, `GET /api/v1/tasks/{id}` returns:
```json
{
"id": "task_…",
"model": "seedance-2.0-mini",
"status": "completed",
"output": { "video_urls": ["https://cdn.reapi.ai/media/tasks/…/0.mp4"] },
"error": null
}
```
## Errors
| HTTP | `code` | When |
| ---- | ----------------- | ----------------------------------------------------------------------------- |
| 400 | `20002` | Missing / invalid parameter (e.g. prompt required in T2V, value out of range) |
| 400 | `80007` | Upstream content rejection (e.g. a flagged source image) |
| 401 | `10001` – `10005` | Auth missing / invalid / revoked |
| 402 | `30001` | Insufficient credits |
| 429 | `50001` | Per-user rate limit exceeded |
Pattern-match on the numeric `code`, not the message string. Failed
generations are surfaced under `error` in the polling response and are
refunded automatically. Full catalog: [Errors](/docs/api/errors).
## Tips
* **Pick the mode by inputs, not a flag.** Sending `reference_image_urls`
switches to R2V; sending `first_frame_url` switches to I2V; neither → T2V.
* Seedance 2.0 Mini is the low-cost tier — for higher resolutions (1080p / 4k)
use [`seedance-2-0`](/docs/seedance-2-0) or its
[official channel](/docs/seedance-2-0-official).
* Longer `duration` scales the bill linearly; start at 5s while iterating.
## Related
* [`seedance-2-0` — full reference](/docs/seedance-2-0)
* [`seedance-2-0-official` — Official channel](/docs/seedance-2-0-official)
* [Tasks](/docs/api/tasks) — universal polling endpoint
* [Errors](/docs/api/errors) — full error catalog
---
# doubao-seedance-2.0-official (https://reapi.ai/docs/seedance-2-0-official)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Doubao Seedance 2.0 **Official Channel** — direct first-party routing,
> distinct from the standard [`doubao-seedance-2.0`](/docs/seedance-2-0)
> channel. Two model ids: `doubao-seedance-2.0-official` (480p / 720p /
> 1080p / 4k) and `doubao-seedance-2.0-fast-official` (480p / 720p only). Same
> async endpoint, same parameter shape as the standard channel — pick the
> channel via the `model` id.
**Official channel, ReAPI boundary.** The upstream is Volcengine Ark
(火山方舟) first-party. Media inputs must still be public `http(s)` URLs;
base64 / data URI media is not accepted by ReAPI even where upstream docs
show base64 examples.
**No real-person inputs.** This channel rejects source images / videos that
appear to contain a real person (upstream privacy policy) — the request
fails with `400` (code `80007`). For real-person uploads use a Face variant
on the standard channel (`doubao-seedance-2.0-face` / `-fast-face`).
**Tip — synthetic faces pass.** The filter blocks only real persons, not
AI-generated faces. A portrait generated with
[Seedream 5.0](/docs/seedream-5-0-lite) (a face-bearing synthetic image)
is accepted as a source image on this channel.
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2.0-official",
"prompt": "A kitten yawning at the camera, soft morning light",
"size": "16:9",
"resolution": "720p",
"duration": 5
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "doubao-seedance-2.0-official",
"prompt": "A kitten yawning at the camera, soft morning light",
"size": "16:9",
"resolution": "720p",
"duration": 5,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "doubao-seedance-2.0-official",
prompt: "A kitten yawning at the camera, soft morning light",
size: "16:9",
resolution: "720p",
duration: 5,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "doubao-seedance-2.0-official",
"prompt": "A kitten yawning at the camera, soft morning light",
"size": "16:9",
"resolution": "720p",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
***
## Request surface
The public endpoint and async response envelope are identical to the
standard channel:
```txt
POST /api/v1/videos/generations
```
Mode is implicit — which media fields you set decides text-to-video,
image-to-video, first/last-frame, or reference-driven generation:
| Field | Official-channel behavior |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | `"doubao-seedance-2.0-official"` or `"doubao-seedance-2.0-fast-official"` |
| `prompt` | Required on every request (min 3 chars). |
| `size` | `"16:9"`, `"9:16"`, `"1:1"`, `"4:3"`, `"3:4"`, `"21:9"`, `"adaptive"`. Default `"adaptive"`. |
| `resolution` | `"480p"`, `"720p"`, `"1080p"`, `"4k"`. Default `"720p"`. `4k` is `doubao-seedance-2.0-official` only; `-fast-official` rejects `1080p` / `4k` (480p / 720p only). |
| `duration` | 4–15 seconds. Default `5`. |
| `generate_audio` | Boolean. Default `true`. |
| `return_last_frame` | Boolean. Default `false`. |
| `tools` | `[{"type": "web_search"}]` to enable web search. |
| `image_urls` | Public `http(s)` URLs, up to 9. Mutually exclusive with `image_with_roles`. |
| `image_with_roles` | First / last-frame slots (`role`: `first_frame` or `last_frame`). Cannot combine with `video_urls` or `audio_urls`. |
| `video_urls` | Public `http(s)` URLs, up to 3. Presence switches billing to the cheaper reference tier (see Pricing). |
| `audio_urls` | Public `http(s)` URLs, up to 3. Must be used together with `image_urls` or `video_urls`. |
reAPI never silently substitutes one variant for another. Sending
`resolution: "1080p"` to `doubao-seedance-2.0-fast-official` returns `400`
(code `20003`), never an auto-downgraded clip.
## Differences from `doubao-seedance-2.0`
| Aspect | `doubao-seedance-2.0` (standard) | `doubao-seedance-2.0-official` |
| ----------------------- | ----------------------------------------- | ------------------------------------------------- |
| Upstream channel | Standard | Volcengine Ark (火山方舟) official |
| Wire `model` value | `doubao-seedance-2.0-face` / `-fast-face` | `doubao-seedance-2.0-official` / `-fast-official` |
| Real-person inputs | Accepted only on Face variants | Not accepted (no Face variant) |
| Per-second rate | Baseline | Lower — direct official pricing |
| Validation error prefix | `seedance:` | `seedance-official:` |
***
## Pricing
Per-second × resolution, with a cheaper reference tier when a source video
is uploaded. The official channel is priced **below** the standard channel
at the same resolution. See current 480p / 720p / 1080p rates on the
[Seedance 2.0 model page](https://reapi.ai/models/seedance-2-0).
**Bill formula** (1 credit = $0.001):
| Input | Billable seconds |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| No `video_urls` (text / image / first-last-frame / audio reference) | `duration` you sent (default 5) |
| With `video_urls` | `duration` + `ceil(sum of source video seconds)` — the vendor processes the input clip(s) AND produces the output; both are billable. Failed probe → `400 PRICING_UNAVAILABLE`, no charge. |
Final bill: `ceil(per_second_usd × billable_seconds × 1000)` credits.
Failed jobs refund automatically.
***
## Validation errors
The response envelope and numeric error-code system match the standard
channel. Official-channel validation messages use the `seedance-official:`
prefix. Upstream content rejections (e.g. a real-person source image) surface
as `400` with code `80007`.
Pattern-match on the numeric `code`, not the message string.
***
## When to pick which channel
| Need | Channel |
| ------------------------------------ | ---------------------------------------------------- |
| Lowest unit price | `doubao-seedance-2.0-official` / `-fast-official` |
| Official Volcengine Ark direct route | `doubao-seedance-2.0-official` / `-fast-official` |
| Real-person source images / videos | `doubao-seedance-2.0-face` / `-fast-face` (standard) |
Switching channels = changing the `model` string in your request body.
The response envelope and polling flow are shared; provider-specific
validation and pricing differ as documented above.
***
## Related
* [`doubao-seedance-2.0` — full reference](/docs/seedance-2-0)
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
---
# doubao-seedance-2.0 (https://reapi.ai/docs/seedance-2-0)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> ByteDance's async video model on reAPI. **Four variants** share one
> endpoint and one parameter shape — pick the variant via `model`. Mode
> is implicit: which media fields you set (`prompt`, `image_urls`,
> `image_with_roles`, `video_urls`, `audio_urls`) decides whether the
> request runs as text-to-video, image-to-video, first/last-frame
> transition, or reference-driven generation. 4–15 second outputs at
> 480p / 720p / 1080p / 4k. See current pricing on the
> [model page](https://reapi.ai/models/seedance-2-0).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedance-2.0-face",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "doubao-seedance-2.0-face",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "doubao-seedance-2.0",
prompt: "A kitten yawning at the camera, cinematic warm tones",
resolution: "720p",
size: "16:9",
duration: 5,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "doubao-seedance-2.0-face",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "doubao-seedance-2.0-face",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls`
holds the generated MP4 URL, valid for 7 days. `output.last_frame_url`
is present when the request set `return_last_frame: true`.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Variants
`doubao-seedance-2.0` is a family of two variants sharing one
parameter shape. Pick via `model`:
| Variant | Speed | 1080p / 4k | Real-person uploads |
| ------------------------------- | -------- | :------------------: | :-----------------: |
| `doubao-seedance-2.0-face` | standard | ✅ | ✅ |
| `doubao-seedance-2.0-fast-face` | faster | ❌ (480p / 720p only) | ✅ |
reAPI never silently substitutes one variant for another. Sending
`resolution: "1080p"` to the Fast variant returns `400`, never an
auto-downgraded clip.
**Real-person uploads** — both variants accept real-person source
images / videos.
***
## Channels
The variants above ship across two channels — same async endpoint,
selected by the `model` id you send:
| Channel | Model ids | Notes |
| ------------ | ------------------------------------------------------------------- | --------------------------------------------------------------------------- |
| **Standard** | `doubao-seedance-2.0-face`, `doubao-seedance-2.0-fast-face` | Face variants accept real-person inputs. |
| **Official** | `doubao-seedance-2.0-official`, `doubao-seedance-2.0-fast-official` | Official direct channel — lower price. Real-person inputs are not accepted. |
Standard and Official share the parameter shape documented below.
***
## Mode routing
`doubao-seedance-2.0` picks its mode from which media fields you set —
there is no `mode` parameter:
| Fields you send | Mode | What it does |
| ----------------------------------------------------------------------- | ---------- | ---------------------------------------- |
| `prompt` only | **T2V** | Generate from text |
| `prompt` + `image_urls` (1–9) | **I2V** | Animate / extend from reference images |
| `prompt` + `image_with_roles` (1–2 frames) | **FRAMES** | First / last frame transition |
| `prompt` + `video_urls` and / or `audio_urls` (+ optional `image_urls`) | **REF** | Reference-driven, optionally multi-modal |
**Mutex rules.** Most field combinations are illegal. The single legal
multi-field shape is `image_urls + video_urls + audio_urls` (REF mode,
multi-modal). Any other combination is rejected with `400` (code
`20003`).
* `prompt` is required on every request (all four modes carry it)
* `image_urls` ⊕ `image_with_roles` — never together
* `image_with_roles` cannot be combined with `video_urls` or `audio_urls`
* `audio_urls` requires `image_urls` or `video_urls`
***
## Request body
### `model` — required
`string`. One of the four variants in the table above.
### `prompt` — string, required
**Required on every request**, min **3** characters, up to **4,000**
(≤ **500 recommended** — quality drops past \~500 chars on the upstream
model). Applies to all modes — T2V, I2V, FRAMES, REF.
Best results come from naming, in order, the **subject**, the
**action**, the **camera move**, and the **style**. e.g. `"A kitten,
yawning into the camera, slow push-in, cinematic warm tones"`.
**Failure modes.**
* Missing / empty → `400` (code `20002`).
* Shorter than 3 chars → `400` (code `20003`).
* Longer than 4,000 chars → `400` (code `20003`).
### `duration` — integer, default `5`
Output length in seconds. Any integer in `[4, 15]`. Out-of-range →
`400`.
**Billable seconds = `sum(video_urls clip lengths)` + `duration`.** The
input reference clips and the generated output both contribute to cost.
Image and audio references don't carry a billable time component —
only `video_urls` adds. reAPI probes `video_urls` server-side via
ffmpeg metadata; the value reported by your client is never trusted
for billing. See [Pricing](#pricing) for the full formula.
### `size` — string, default `"adaptive"`
Output ratio. One of:
| Value | Shape |
| ---------- | ------------------------------------- |
| `16:9` | Landscape |
| `9:16` | Portrait |
| `1:1` | Square |
| `4:3` | Traditional landscape |
| `3:4` | Traditional portrait |
| `21:9` | Cinematic ultrawide |
| `adaptive` | Match the input image / video's ratio |
Invalid values → `400` (no silent fallback).
### `resolution` — string, default `"720p"`
`480p` / `720p` / `1080p` / `4k` — lowercase only. Drives pricing.
Uppercase forms like `1080P` are rejected with `400`.
**`1080p` and `4k` are variant-gated.** Only `doubao-seedance-2.0` and
`doubao-seedance-2.0-face` accept `1080p` / `4k`. The Fast variants
(`-fast`, `-fast-face`) cap at 720p — sending a higher resolution returns
`400 resolution= is not supported by ` (code `20003`),
no auto-downgrade.
### `generate_audio` — boolean, default `true`
When `true` (the default), the model synthesizes an audio track that plays
alongside the generated video; pass `false` to get a silent clip. Independent
of `audio_urls` (which is a **reference** for the model to align with — not a
synthesis toggle).
### `return_last_frame` — boolean, default `false`
When `true`, the completed task carries an extra `output.last_frame_url`
holding the final frame as a still image. Pass it as `image_urls` of
the next request to chain continuous video without prompt drift.
### `tools` — object\[]
Per-tool capability list. Today only one type is recognized:
```json
"tools": [{ "type": "web_search" }]
```
`web_search` lets the model query the web during generation — useful
for current events or named brands. Unknown `type` values are rejected
with `400 tools[i].type must be "web_search"`.
### `nsfw_checker` — boolean, default `true`
Safety checking is enabled by default. Direct API callers can pass
`"nsfw_checker": false` on the Standard model ids in this page:
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "Your prompt",
"resolution": "720p",
"size": "16:9",
"duration": 5,
"nsfw_checker": false
}
```
When set to `false`, reAPI sends the task directly through the Flexible
channel when that channel is available and compatible with the request, and
does not attach fallback to that task. If no compatible Flexible channel is
available, reAPI silently uses the selected Standard channel instead; Standard
channel generation remains safety-checked. In both cases, no fallback is
attached for that request.
### `image_urls` — string\[]
Array of public HTTP(S) URLs. Up to **9** entries. Triggers I2V (when
sent without other media fields) or augments REF (when combined with
`video_urls` / `audio_urls`).
Mutually exclusive with `image_with_roles`. Sending more than 9 is
rejected with `400 at most 9 image_urls allowed`.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — every
URL field on this endpoint must be a public HTTP(S) URL. Upload to
your own object storage (S3, R2, OSS, …) and pass the URL.
### `image_with_roles` — object\[]
First / last frame interpolation. Each entry is a `{url, role}` object:
```json
"image_with_roles": [
{ "url": "https://your-cdn.com/day.jpg", "role": "first_frame" },
{ "url": "https://your-cdn.com/night.jpg", "role": "last_frame" }
]
```
`role` is one of `first_frame` / `last_frame`. Up to **9** entries
(typical use: 1 or 2 — one first frame and one last frame).
**Cannot be combined** with `image_urls`, `video_urls`, or
`audio_urls` — these modes are exclusive.
### `video_urls` — string\[]
Reference video clips for REF mode. Up to **3** entries; each clip
**2–15 s** long, **combined ≤ 15 s**. Each clip's frame must be
**300–6000 px** on each side, **0.41–8.3 MP** total, aspect ratio
**0.4–2.5**. Public HTTP(S) URLs only.
**No real people on standard / fast variants.** Use the Face variants
(`-face`, `-fast-face`) when the reference clip features identifiable
real people — the non-Face variants reject them upstream.
reAPI probes each clip's resolution and duration server-side via
ffmpeg metadata. Out-of-spec assets surface as a `400`:
* Frame outside 300–6000 px/side, 0.41–8.3 MP, or aspect 0.4–2.5 → `400 video_urls[i] resolution WxH is out of range` (code `20003`)
* Each clip outside 2–15s, or combined > 15s → `400 video_urls total duration X.XXs exceeds the 15s limit` (code `20003`)
* Probe failure (network / format) → `400 Could not determine source video duration for billing` (code `30002`) — no charge
Mutually exclusive with `image_with_roles`.
### `audio_urls` — string\[]
Reference audio for REF mode. Up to **3** entries; **combined duration
≤ 15 seconds**. Public HTTP(S) URLs only.
**Must accompany `image_urls` OR `video_urls`** — a request with
`audio_urls` and no visual reference is rejected with `400 audio_urls
must be used together with image_urls or video_urls`.
Mutually exclusive with `image_with_roles`.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill
in over time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "doubao-seedance-2.0-face",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"],
"last_frame_url": "https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png"
},
"error": null
}
```
| Field | Type | Notes |
| ----------------------- | -------------- | ----------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` (the variant you picked) |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion |
| `output.video_urls` | string\[] | Generated MP4 URL(s) — valid for 7 days |
| `output.last_frame_url` | string \| null | Present only when the request set `return_last_frame: true` |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
***
## Validation errors
All cases below return HTTP 400 with the noted code. Pattern-match on
`code`, not `message` — message strings carry request-specific context
(field names, observed values) and are not a stable contract.
| Trigger | Code | Message |
| ------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| Missing `prompt` | `20002` | `prompt: Invalid input: expected string, received undefined` |
| `prompt` shorter than 3 chars | `20003` | `prompt: Too small: expected string to have >=3 characters` |
| `prompt` longer than 4,000 chars | `20003` | `prompt: Too big: expected string to have <=4000 characters` |
| `image_urls` and `image_with_roles` together | `20003` | `image_urls and image_with_roles cannot be used simultaneously` |
| `image_with_roles` + `video_urls` or `audio_urls` | `20003` | `image_with_roles cannot be combined with video_urls or audio_urls` |
| `audio_urls` without visual reference | `20003` | `audio_urls must be used together with image_urls or video_urls` |
| `image_urls` > 9 | `20003` | `at most 9 image_urls allowed, got N` |
| `image_with_roles` > 9 | `20003` | `at most 9 image_with_roles allowed, got N` |
| `image_with_roles[i].role` invalid | `20003` | `image_with_roles[i].role must be first_frame or last_frame, got "..."` |
| `video_urls` > 3 | `20003` | `at most 3 video_urls allowed, got N` |
| `video_urls` clip frame out of range (300–6000px / 0.41–8.3MP / aspect 0.4–2.5) | `20003` | `video_urls[i] resolution WxH is out of range` |
| `video_urls` combined > 15s | `20003` | `video_urls total duration X.XXs exceeds the 15s limit` |
| `audio_urls` > 3 | `20003` | `at most 3 audio_urls allowed, got N` |
| `audio_urls` combined > 15s | `20003` | `audio_urls total duration X.XXs exceeds the 15s limit` |
| `duration` outside 4–15 | `20003` | `duration must be 4-15 seconds, got N` |
| Invalid `size` value | `20005` | `invalid size "..." (allowed: 16:9 / 9:16 / 1:1 / 4:3 / 3:4 / 21:9 / adaptive)` |
| Invalid `resolution` value | `20003` | `invalid resolution "..." (allowed: 480p / 720p / 1080p / 4k)` |
| `1080p` on Fast variant | `20003` | `resolution=1080p is not supported by (use doubao-seedance-2.0 or doubao-seedance-2.0-face)` |
| `tools[i].type` not `web_search` | `20003` | `tools[i].type must be "web_search", got "..."` |
| Any URL field carrying a `data:` URI | `20003` | ` entries must be public URLs; base64 data URIs are not supported` |
| Reference video probe fails | `30002` | `Could not determine source video duration for billing: ...` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` —
see [Errors catalog](/docs/api/errors) for wire format and request\_id
correlation.
***
## Recipes
### T2V — text-to-video
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "A kitten yawning at the camera, slow push-in, warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5,
"fallback": { "enabled": false }
}
```
### I2V — single reference image
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "The kitten stands up and walks toward the camera",
"image_urls": ["https://your-cdn.com/cat.jpg"],
"duration": 5
}
```
### FRAMES — first / last frame transition
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "Smooth transition from day to night",
"image_with_roles": [
{ "url": "https://your-cdn.com/day.jpg", "role": "first_frame" },
{ "url": "https://your-cdn.com/night.jpg", "role": "last_frame" }
],
"duration": 5
}
```
### REF — reference video (style transfer)
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "Restylize the reference clip into anime aesthetics",
"video_urls": ["https://your-cdn.com/reference.mp4"]
}
```
### REF — reference video + reference audio
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "A scene of a person speaking",
"video_urls": ["https://your-cdn.com/reference.mp4"],
"audio_urls": ["https://your-cdn.com/speech.wav"],
"size": "16:9",
"duration": 11
}
```
### Voiced video (synthesized audio)
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "A man calls out to a woman: \"Remember — never point at the moon with your finger.\"",
"generate_audio": true
}
```
### Continuous video chain
Step 1 — produce a 5s clip and ask for the last-frame URL:
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "The kitten approaches the camera",
"image_urls": ["https://your-cdn.com/kitten-start.png"],
"return_last_frame": true
}
```
Step 2 — feed `output.last_frame_url` as `image_urls` of the next call:
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "The kitten turns and walks away",
"image_urls": [""]
}
```
### Fast variant — quick timelapse
```json
{
"model": "doubao-seedance-2.0-fast-face",
"prompt": "City nightscape timelapse",
"size": "21:9",
"duration": 8
}
```
### Multi-modal — images + reference video + reference audio
The full REF surface — combine all three reference types for tightly
directed product / brand spots.
```json
{
"model": "doubao-seedance-2.0-face",
"prompt": "First-person POV product ad with dynamic camera moves",
"image_urls": [
"https://your-cdn.com/product-1.jpg",
"https://your-cdn.com/product-2.jpg"
],
"video_urls": ["https://your-cdn.com/style-ref.mp4"],
"audio_urls": ["https://your-cdn.com/bgm.mp3"],
"generate_audio": true,
"size": "16:9",
"duration": 11
}
```
***
## Choosing a variant
| Need | Pick |
| -------------------------------------- | ------------------------------- |
| Highest quality, full resolution range | `doubao-seedance-2.0-face` |
| Cheaper / faster, 720p ceiling | `doubao-seedance-2.0-fast-face` |
Variants are independent products — reAPI never rewrites your selected
`model` on the primary attempt. Standard variants can still use the
`fallback` policy above after an eligible generation-side failure.
***
## Polling pattern
The task endpoint behaves identically to image tasks — only the
completed `output` shape differs (`video_urls` / `last_frame_url`
instead of `image_urls`). A pragmatic schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in a few minutes. A single generation attempt can run
for up to **48 hours**; when fallback is enabled, the overall task window can
cover two generation attempts.
***
## Pricing
Per-second × **billable seconds**, where:
```
billable_seconds = sum(video_urls clip lengths, server-probed)
+ duration
```
`video_urls` clip lengths are measured server-side via ffmpeg metadata
— **client-stated values are never trusted for billing**. Image and
audio references don't add to billable time. T2V / I2V / FRAMES
requests (no `video_urls`) bill on `duration` alone.
The **per-second rate** depends on three axes:
* **Variant** (2 options)
* **Resolution** (`480p` / `720p` / `1080p` / `4k`)
* **Mode** — `text` (no media references) vs. `ref` (any of `image_urls`,
`image_with_roles`, `video_urls`, `audio_urls` is set)
REF rates are lower than text rates at every cell. See live numbers on
the [model page](https://reapi.ai/models/seedance-2-0#pricing) — that
table is dynamic and always reflects the current rate.
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_second_usd × billable_seconds × 1000)
```
Charge on submit; refund automatically on `failed`. Probe failures
(unreachable / unreadable `video_urls`) return `400 PRICING_UNAVAILABLE`
with no charge.
When fallback is enabled, reAPI reserves the larger of the primary and
fallback attempt prices. The final successful task is settled to the winning
attempt's price and the difference is refunded automatically. If both attempts
fail, the full reserve is refunded.
**Worked example.** `doubao-seedance-2.0` at `720p`, REF mode, with a
5-second reference video and `duration: 6`:
* `billable_seconds = 5 + 6 = 11`
* `credits = ceil(per_second_usd × 11 × 1000)`
The same `duration: 6` request without `video_urls` would bill 6
seconds at the (higher) text rate.
***
## Tips
* **Prompt motion, not just scene.** "Slow push-in, warm tones,
shallow depth of field" outperforms a noun-list of what's on screen.
* **Sweet-spot duration: 5–10 seconds.** Below 5s motion looks choppy;
above 10s generation time grows fast.
* **Trim reference clips before upload.** Both their actual length
AND your `duration` count toward the bill. A 2-second style snippet
is usually enough to convey style — there's no quality bonus for
uploading a 15s reference.
* **Pick `doubao-seedance-2.0-fast` for iteration.** Fast variants
cost noticeably less and miss only the 1080p tier — perfect for
prompt-tuning loops where final quality comes later.
* **Real people → Face variants.** The non-Face variants reject
identifiable real-person assets during generation; switching is
a one-character change to `model`.
* **Chain continuous video with `return_last_frame`.** Pass the
returned URL as `image_urls` of the next request. No prompt drift
between segments.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Pricing — Seedance 2.0](https://reapi.ai/models/seedance-2-0#pricing)
---
# Seedance 2.5 (https://reapi.ai/docs/seedance-2-5)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
**Coming soon.** The Seedance 2.5 API is not available yet. The endpoint,
parameters, and pricing below are a **preview** seeded from the current
Seedance generation and may change when the model ships. Watch the
[model page](https://reapi.ai/models/seedance-2-5) for launch.
> ByteDance's next-generation async video model, coming to reAPI. Seedance 2.5
> turns text, photos, clips, or audio into short video. Like the current
> Seedance family, mode is implicit: which media fields you set (`prompt`,
> `image_urls`, `image_with_roles`, `video_urls`, `audio_urls`) decides whether
> the request runs as text-to-video, image-to-video, first/last-frame
> transition, or reference-driven generation. See pricing on the
> [model page](https://reapi.ai/models/seedance-2-5).
## Status
Seedance 2.5 is **coming soon**. This documentation is a preview so you can
plan an integration ahead of launch. When the model ships:
* The `model` ids, full parameter set, constraints, and per-second rates are
finalized here.
* The playground on the [model page](https://reapi.ai/models/seedance-2-5)
becomes live.
* This banner is removed.
The shape below mirrors the current Seedance generation. Treat every field as
provisional until launch.
## Quick example (preview)
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "seedance-2.5",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "seedance-2.5",
prompt: "A kitten yawning at the camera, cinematic warm tones",
resolution: "720p",
size: "16:9",
duration: 5,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "seedance-2.5",
"prompt": "A kitten yawning at the camera, cinematic warm tones",
"resolution": "720p",
"size": "16:9",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
These calls will return `404 model not found` until Seedance 2.5 launches.
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate project
header.
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
## Variants (preview)
Seedance 2.5 is expected to ship as a family of variants sharing one parameter
shape, picked via `model`:
| Variant | Speed | 1080p | Real-person uploads |
| ------------------------ | -------- | :------------------: | :-----------------: |
| `seedance-2.5` | standard | ✅ | — |
| `seedance-2.5-fast` | faster | ❌ (480p / 720p only) | — |
| `seedance-2.5-face` | standard | ✅ | ✅ |
| `seedance-2.5-fast-face` | faster | ❌ (480p / 720p only) | ✅ |
reAPI never silently substitutes one variant for another. Final variant ids
are confirmed at launch.
## Request body (preview)
Mode is implicit — which media fields you set decides text-to-video,
image-to-video, first/last-frame, or reference-driven generation.
| Field | Type | Default | Notes |
| ------------------- | --------- | ------- | -------------------------------------------------------------------- |
| `model` | string | — | One of the variants above. **Required.** |
| `prompt` | string | — | Describe the video. Optional when a reference field carries content. |
| `duration` | integer | `5` | Output length in seconds, `4`–`15`. |
| `size` | string | `16:9` | `16:9` / `9:16` / `1:1` / `4:3` / `3:4` / `21:9` / `adaptive`. |
| `resolution` | string | `480p` | `480p` / `720p` / `1080p`. `1080p` on standard / face variants only. |
| `seed` | integer | — | Reproducibility hint. |
| `generate_audio` | boolean | `false` | Synthesize an audio track to play with the video. |
| `return_last_frame` | boolean | `false` | Return `output.last_frame_url` for continuous chaining. |
| `tools` | object\[] | — | `[{ "type": "web_search" }]` to let the model query the web. |
| `image_urls` | string\[] | — | Up to 9 reference images (image-to-video). Public HTTP(S) URLs. |
| `image_with_roles` | object\[] | — | First / last frame interpolation (`{ url, role }`). |
| `video_urls` | string\[] | — | Up to 3 reference clips, combined ≤ 15s. |
| `audio_urls` | string\[] | — | Up to 3 reference audio tracks for lip-sync. |
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — every URL
field must be a public HTTP(S) URL. Upload to your own object storage (S3, R2,
OSS, …) and pass the URL.
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "seedance-2.5",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/.../0.mp4"],
"last_frame_url": "https://cdn.reapi.ai/media/tasks/.../0.png"
},
"error": null
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. `output.video_urls` holds the generated MP4 URL.
`output.last_frame_url` is present when the request set `return_last_frame: true`.
## Pricing
Seedance 2.5 will bill per-second, scaled by resolution and reference mode —
the same dimensions as the current Seedance family:
```
credits = ceil(per_second_usd × billable_seconds × 1000)
```
where `1 credit = $0.001`. Final per-second rates are published on the
[model page](https://reapi.ai/models/seedance-2-5) at launch — that table is
dynamic and always reflects the current rate. Failed jobs are refunded
automatically.
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
* [Seedance 2.0 — available now](/docs/seedance-2-0)
---
# doubao-seedream-5-0-lite (https://reapi.ai/docs/seedream-5-0-lite)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> ByteDance Doubao's lean image model on reAPI. **2K and 3K** resolution
> tiers, **nine aspect ratios**, **image-to-image** via reference URLs,
> and **batch generation** of up to four variations per call. Async-first:
> submit returns a `task_id`; poll until ready. See current pricing on the
> [model page](https://reapi.ai/models/seedream-5-0-lite).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedream-5-0-lite",
"prompt": "a kitten yawning into the camera, cinematic warm tones",
"size": "16:9",
"resolution": "2K",
"n": 1
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "doubao-seedream-5-0-lite",
"prompt": "a kitten yawning into the camera, cinematic warm tones",
"size": "16:9",
"resolution": "2K",
"n": 1,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "doubao-seedream-5-0-lite",
prompt: "a kitten yawning into the camera, cinematic warm tones",
size: "16:9",
resolution: "2K",
n: 1,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "doubao-seedream-5-0-lite",
"prompt": "a kitten yawning into the camera, cinematic warm tones",
"size": "16:9",
"resolution": "2K",
"n": 1,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "doubao-seedream-5-0-lite",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls`
holds the generated image URLs, valid for **72 hours**. Mirror to your
own storage if you need them longer.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Request body
### `model` — required
`string`. Must be `doubao-seedream-5-0-lite` exactly. The dotted /
capitalized aliases the upstream model card mentions are not accepted at
this gateway — send the canonical hyphen-only id.
### `prompt` — string, required
Up to **4,000 characters**. Free-form text describing the image. Detailed
prompts that name the **subject**, the **composition**, the **lighting**,
and the **style** consistently produce better results.
### `size` — string, default `"1:1"`
Output aspect ratio. One of:
| Value | Shape |
| ------ | --------------------------------------------------------------- |
| `1:1` | Square |
| `4:3` | Traditional landscape |
| `3:4` | Traditional portrait |
| `16:9` | Widescreen landscape |
| `9:16` | Widescreen portrait |
| `3:2` | Photo landscape |
| `2:3` | Photo portrait |
| `21:9` | Cinematic ultrawide |
| `auto` | Match the first reference image's ratio (requires `image_urls`) |
**`9:21` is not supported** on Seedream 5.0 Lite. Sending it returns
`400` — use `21:9` for ultrawide framing instead.
### `resolution` — string, default `"2K"`
Detail tier. Case-insensitive — `"2K"` and `"2k"` are equivalent.
| Value | 1:1 example | 16:9 example |
| ----- | ----------- | ------------ |
| `2K` | 2048 × 2048 | 2848 × 1600 |
| `3K` | 3072 × 3072 | 4096 × 2304 |
`1K` and `4K` are not supported on this model — only the two tiers
above.
### `n` — integer, default `1`
How many images to return per call. Range `[1, 4]`.
When `n > 1`, the upstream automatically promotes
`sequential_image_generation` to `"auto"` so the call returns a
consistent batch in one shot. You don't need to set the field yourself
unless you want to override the default.
### `image_urls` — array, optional
Reference images for image-to-image / multi-ref fusion. **Public HTTPS
URLs only** — base64 / `data:` URIs are rejected at the gateway.
Per-image constraints (from the upstream vendor doc):
* Format: jpeg or png
* Aspect ratio per image must be in `[1/3, 3]`
* Each image ≤ 10 MB; total pixels ≤ 6000 × 6000
Upload to your own R2 / S3 / OSS / equivalent first, then pass the
public URL.
### `output_format` — string, default `"jpeg"`
Encoding for the returned files.
* `jpeg` — small files, good for general use
* `png` — needed for transparent backgrounds
### `sequential_image_generation` — string, default `"disabled"`
Group-mode toggle. Two values:
* `disabled` — single-image mode
* `auto` — multi-image series mode
When `n > 1`, the upstream auto-sets this to `"auto"`. Most callers
should leave the field unset and let `n` drive the behavior.
### `sequential_image_generation_options` — object, optional
Tuning for group mode. Only meaningful under `"auto"`.
| Field | Type | Description |
| ------------ | ------- | ----------------------------------------- |
| `max_images` | integer | Cap on the number of images in the series |
Example:
```json
{
"sequential_image_generation": "auto",
"sequential_image_generation_options": { "max_images": 4 }
}
```
### `watermark` — boolean, default `false`
Set `true` to add a Doubao watermark to the output. Off by default.
***
## Pricing
Seedream 5.0 Lite charges a flat per-image rate at 2K and 3K.
Multiply the per-image credit cost by `n` to size a batch. Failed and
moderated requests are refunded automatically.
The exact credit cost surfaces on the
[model page](https://reapi.ai/models/seedream-5-0-lite) and through the
estimator before submit.
***
## Response
The poll envelope returns image URLs in `output.image_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "doubao-seedream-5-0-lite",
"status": "completed",
"output": {
"image_urls": [
"https://cdn.reapi.ai/...png"
]
}
}
```
URLs are valid for **72 hours**. Mirror to your own storage for longer
retention.
---
# Seedream 5.0 Pro (https://reapi.ai/docs/seedream-5-0-pro)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> ByteDance's flagship Doubao Seedream image model on reAPI. **1K and 2K**
> resolution tiers, **text-to-image**, **single and multi-reference
> image-to-image** (up to 10 reference images), and dependable **text
> rendering** for posters, covers, and ad creative. Async-first: submit
> returns a `task_id`; poll until ready. See current pricing on the
> [model page](https://reapi.ai/models/seedream-5-0-pro).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "doubao-seedream-5-0-pro",
"prompt": "a scarlet macaw on a mossy branch, cinematic god rays, magazine cover titled WILD BEAUTY",
"aspect_ratio": "16:9",
"quality": "high"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "doubao-seedream-5-0-pro",
"prompt": "a scarlet macaw on a mossy branch, cinematic god rays",
"aspect_ratio": "16:9",
"quality": "high",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "doubao-seedream-5-0-pro",
prompt: "a scarlet macaw on a mossy branch, cinematic god rays",
aspect_ratio: "16:9",
quality: "high",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "doubao-seedream-5-0-pro",
"prompt": "a scarlet macaw on a mossy branch, cinematic god rays",
"aspect_ratio": "16:9",
"quality": "high",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "doubao-seedream-5-0-pro",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls` holds the
generated image URL, valid for **72 hours**. Mirror to your own storage if you
need it longer.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate project
header.
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Request body
### `model` — required
`string`. Must be `doubao-seedream-5-0-pro` exactly.
### `prompt` — string, required
Up to **4,000 characters**. Free-form text describing the image. Detailed
prompts that name the **subject**, **composition**, **lighting**, and **style**
consistently produce better results. Seedream 5.0 Pro renders in-image text well
— put headline or logo copy directly in the prompt.
### `aspect_ratio` — string, required
Output aspect ratio. Accepted values:
* `1:1`
* `4:3`
* `3:4`
* `16:9`
* `9:16`
* `2:3`
* `3:2`
### `quality` — string, required
Output resolution tier:
* `basic` — 1K output
* `high` — 2K output
Seedream 5.0 Pro does not expose a 4K tier.
Generated images do not include a visible watermark by default.
### `image_urls` — array, optional
Reference images for image-to-image (1 image) or multi-reference fusion (up to
**10** images). **Public HTTP(S) URLs only** — base64 / `data:` URIs are
rejected at the gateway.
Upload to your own R2 / S3 / OSS / equivalent first, then pass the public URL.
### `nsfw_checker` — boolean, optional
Controls the final-image NSFW output check and defaults to `true`. Direct API
requests may pass `false` to skip this additional check. The hosted Playground
always keeps it enabled.
When enabled, a flagged image is hidden and the task returns a content-policy
error. Because the upstream generation already completed, that generation is
still charged. Avoid submitting NSFW prompts when using the Playground.
Seedream 5.0 Pro always returns **exactly one image** per call — there is no `n`
parameter. Send multiple requests when you need multiple images. Group-image,
web-search, and streaming options are not available on this model.
***
## Pricing
Seedream 5.0 Pro charges the official **per-output-image rate**, banded by
`quality` — the `high`/2K band costs more than `basic`/1K. For reference-image
requests, the first input image is included and each additional input image
(2nd through 10th) adds the official **¥0.02** reference surcharge (converted
at the configured 6.8 RMB/USD rate). Provider failures and requests rejected
before an image is generated are refunded. An image blocked by the final output
check is still charged because generation already completed.
```
credits = ceil((output_price_usd + additional_reference_price_usd) × 1000)
```
where `1 credit = $0.001`. The exact per-image credit cost surfaces on the
[model page](https://reapi.ai/models/seedream-5-0-pro) and reflects the current
rate. There is no per-token or subscription component.
***
## Response
The poll envelope returns the image URL in `output.image_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "doubao-seedream-5-0-pro",
"status": "completed",
"output": {
"image_urls": [
"https://cdn.reapi.ai/...jpg"
]
}
}
```
URLs are valid for **72 hours**. Mirror to your own storage for longer
retention.
***
## Errors
Failures use the standard envelope `{ error: { code, message, request_id } }`.
See the [errors catalog](/docs/api/errors) for the full code list. Common cases:
* Invalid parameters (bad `aspect_ratio` / `quality`, oversize `image_urls`) →
`4xx` invalid-input.
* A prompt or reference rejected before generation → content-policy error; the
task is refunded.
* A generated image blocked by the enabled output check → content-policy error
(`80006`); the image is hidden and the completed generation remains charged.
***
## Tips
* Choose `aspect_ratio` explicitly instead of relying on ratio wording in the
prompt.
* Multi-reference fusion keeps a character consistent — pass the same face /
wardrobe references across a series.
* Lean on Seedream 5.0 Pro's text rendering for posters, covers, and UI mockups;
spell the exact words you want in quotes inside the prompt.
***
## Related
* [Seedream 5.0 Lite](/docs/seedream-5-0-lite) — the lean, lower-cost tier
* [Tasks reference](/docs/api/tasks)
* [Errors catalog](/docs/api/errors)
---
# Suno API (https://reapi.ai/docs/suno)
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
# Suno API
The `suno-*` model family exposes the complete Suno music toolchain
through one endpoint. Generate a song from a text idea or exact lyrics,
then keep working on it: extend it, re-cover an uploaded track, layer
vocals or instrumentals, mash two songs together, replace a section,
separate stems, convert to WAV, extract MIDI, render a music video, or
clone a singing voice.
Current pricing for every operation is on the
[Suno model page](/models/suno).
## Endpoints
All 18 asynchronous operations share the audio task endpoint — the
`model` field picks the operation:
```http
POST /api/v1/audio/generations
```
Poll completion with:
```http
GET /api/v1/tasks/{id}
```
Four synchronous helpers answer inline (no task lifecycle):
```http
POST /api/v1/audio/suno/boost-style
POST /api/v1/audio/suno/timestamped-lyrics
POST /api/v1/audio/suno/persona
POST /api/v1/audio/suno/check-voice
```
## Quick example — generate a song
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-music",
"customMode": false,
"instrumental": false,
"version": "V5",
"prompt": "A dreamy synthwave track about driving through a neon city at midnight"
}'
```
```python
import requests, time
headers = {"Authorization": "Bearer YOUR_API_KEY"}
task = requests.post(
"https://reapi.ai/api/v1/audio/generations",
headers=headers,
json={
"model": "suno-music",
"customMode": False,
"instrumental": False,
"version": "V5",
"prompt": "A dreamy synthwave track about driving through a neon city at midnight",
},
).json()
while True:
r = requests.get(
f"https://reapi.ai/api/v1/tasks/{task['id']}", headers=headers
).json()
if r["status"] in ("completed", "failed"):
break
time.sleep(5)
print(r["output"]["tracks"])
```
```javascript
const headers = {
Authorization: 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json',
};
const task = await fetch('https://reapi.ai/api/v1/audio/generations', {
method: 'POST',
headers,
body: JSON.stringify({
model: 'suno-music',
customMode: false,
instrumental: false,
version: 'V5',
prompt:
'A dreamy synthwave track about driving through a neon city at midnight',
}),
}).then((r) => r.json());
let result;
do {
await new Promise((r) => setTimeout(r, 5000));
result = await fetch(`https://reapi.ai/api/v1/tasks/${task.id}`, {
headers,
}).then((r) => r.json());
} while (result.status === 'processing');
console.log(result.output.tracks);
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"time"
)
const key = "YOUR_API_KEY"
func main() {
body, _ := json.Marshal(map[string]any{
"model": "suno-music",
"customMode": false,
"instrumental": false,
"version": "V5",
"prompt": "A dreamy synthwave track about driving through a neon city at midnight",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/audio/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
var task struct {
ID string `json:"id"`
}
json.NewDecoder(resp.Body).Decode(&task)
for {
time.Sleep(5 * time.Second)
poll, _ := http.NewRequest("GET",
"https://reapi.ai/api/v1/tasks/"+task.ID, nil)
poll.Header.Set("Authorization", "Bearer "+key)
pr, err := http.DefaultClient.Do(poll)
if err != nil {
panic(err)
}
var result struct {
Status string `json:"status"`
Output struct {
Tracks []struct {
ID string `json:"id"`
URL string `json:"url"`
Title string `json:"title"`
} `json:"tracks"`
} `json:"output"`
}
json.NewDecoder(pr.Body).Decode(&result)
pr.Body.Close()
if result.Status != "processing" {
fmt.Printf("%+v\n", result.Output.Tracks)
return
}
}
}
```
One generation request returns **two tracks**. Each track carries an
`id` you reuse as `audioId` in every track-referencing operation below.
## Modes: inspiration vs custom
Generation-class operations (`suno-music`, `suno-upload-cover`,
`suno-mashup`) run in one of two modes:
* **`customMode: false` (inspiration)** — `prompt` (max 500 characters)
describes the song idea; Suno writes the lyrics.
* **`customMode: true` (custom)** — `prompt` is used **strictly as
lyrics**; `style` and `title` become required. With
`instrumental: true`, `prompt` is not needed.
Character caps scale with `version`: `prompt` 3000 (V4) / 5000
(others), `style` 200 (V4) / 1000 (others), `title` 80 (100 on some
extend variants).
## Versions
`version` is required on generation-class operations:
`V4`, `V4_5`, `V4_5PLUS`, `V4_5ALL`, `V5`, `V5_5`.
`suno-add-vocals` / `suno-add-instrumental` accept `V4_5PLUS` / `V5` /
`V5_5` (default `V4_5PLUS`); `suno-sounds` accepts `V5` / `V5_5`.
## Referencing earlier work: `taskId` + `audioId`
Operations that act on an existing song take:
* `taskId` — **your reAPI task id** (`task_...`) of the source task.
It must be your own completed task; reAPI resolves it internally.
* `audioId` — the track id from that task's `output.tracks[].id`.
Media inputs (`uploadUrl`, `uploadUrlList`, `voiceUrl`, `verifyUrl`)
accept public HTTP(S) URLs only — no base64 / data URIs.
## Operation reference
Every asynchronous operation posts to `/api/v1/audio/generations` and is
selected by the `model` field; poll `/api/v1/tasks/{id}` for the result.
The four synchronous helpers have their own routes and answer inline.
## Create
### Generate a song
`POST /api/v1/audio/generations` · `model: "suno-music"`
Generates a song from a text description — either a short idea the model turns into lyrics, or your own lyrics, style and title — and returns multiple finished audio variations.
**Request**
| Field | Type | Required | Notes |
| --------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-music`. |
| `customMode` | boolean | yes | `false` = inspiration mode (describe an idea, the model writes the lyrics). `true` = custom mode (you supply style, title and — for vocal tracks — the exact lyrics). |
| `instrumental` | boolean | yes | `true` generates a track with no lyrics. In custom mode it decides whether `prompt` is required; in inspiration mode it does not change which fields are required. |
| `version` | string | yes | Model version. One of `V4`, `V4_5`, `V4_5PLUS`, `V4_5ALL`, `V5`, `V5_5`. `V4` caps a track at \~4 min and the `V4_5*` line at \~8 min; `V5` / `V5_5` publish no length cap. Also selects the `prompt` / `style` character limits below. |
| `prompt` | string | conditional | Inspiration mode: the core idea, max 500 characters — required, and the lyrics are written from it rather than matching it word for word. Custom mode: used verbatim as the sung lyrics; required when `instrumental` is `false`; max 3000 characters on `V4`, 5000 on every other version. |
| `style` | string | conditional | Genre, mood or artistic direction (e.g. `Classical`, `Jazz, upbeat`). Required in custom mode; max 200 characters on `V4`, 1000 on every other version. Ignored in inspiration mode. |
| `title` | string | conditional | Track title. Required in custom mode; max 80 characters on all versions. Ignored in inspiration mode. |
| `negativeTags` | string | no | Comma-separated styles or traits to steer away from, e.g. `Heavy Metal, Upbeat Drums`. |
| `vocalGender` | string | no | `m` or `f`. Only effective when `customMode` is `true`, and it biases the voice rather than guaranteeing it. |
| `styleWeight` | number | no | How strictly the result adheres to `style`. `0`–`1`, up to 2 decimal places. |
| `weirdnessConstraint` | number | no | How far the result may deviate creatively. `0`–`1`, up to 2 decimal places. |
| `audioWeight` | number | no | Balance of audio features against the other inputs. `0`–`1`, up to 2 decimal places. |
| `personaId` | string | no | Persona id or voice id to apply to the generated music. Only available when `customMode` is `true`. Persona ids come from `POST /api/v1/audio/suno/persona`; voice ids come from `suno-voice-generate`. |
| `personaModel` | string | no | `style_persona` or `voice_persona` — which kind of persona `personaId` refers to. Only available on `V5` and `V5_5`. |
**Response** — on completion, `output.audio_urls[]` holds one URL per generated variation (usually two). `output.tracks[]` carries the same items with metadata, index-aligned with `audio_urls`: `type` (`audio`), `url`, `id` (the track's audio id — pass it as `audioId` to extend, WAV, stem-separation, MIDI, music-video and timestamped-lyrics operations), `title`, `duration` in seconds, `tags` (comma-separated style tags), `lyrics` and `image_url` (cover art, rehosted alongside the audio). Only `url` is guaranteed — metadata keys are omitted when upstream returns them empty.
**Notes** — the two modes have different required sets, and the API enforces them. Inspiration mode (`customMode: false`) needs only `prompt`, capped at 500 characters; `style` and `title` are ignored. Custom mode (`customMode: true`) always requires `style` and `title`, and additionally requires `prompt` when `instrumental` is `false` — an instrumental custom request is valid with just `style` and `title`. Character caps move with `version`: `prompt` 3000 on `V4` and 5000 elsewhere, `style` 200 on `V4` and 1000 elsewhere; `title` is 80 on every version. Pricing per generation is listed at [https://reapi.ai/models/suno](https://reapi.ai/models/suno).
### Generate lyrics
`POST /api/v1/audio/generations` · `model: "suno-lyrics"`
Writes song lyrics from a theme description and returns them as structured text — no audio is produced.
**Request**
| Field | Type | Required | Notes |
| -------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model` | string | yes | Must be `suno-lyrics`. |
| `prompt` | string | yes | Description of the lyrics you want — theme, mood, style or story elements. Max 200 characters. More specific prompts yield more targeted lyrics. |
**Response** — this task returns text, not audio: there is no `audio_urls` or `tracks`. On completion `output.result.lyrics[]` holds the generated variations (typically 2–3), each an object with `title` and `text`. The `text` body is formatted with standard section markers (`[Verse]`, `[Chorus]`, `[Bridge]`, …), so it can be passed straight into `suno-music` as `prompt` in custom mode.
```json
{
"output": {
"result": {
"lyrics": [
{ "title": "Small Town Lights", "text": "[Verse]\n...\n\n[Chorus]\n..." },
{ "title": "Back Then", "text": "[Verse]\n...\n\n[Chorus]\n..." }
]
}
}
}
```
**Notes** — variations that the upstream model failed to produce are dropped rather than returned as empty entries, so `lyrics[]` can be shorter than the usual 2–3 items. If none survive, the task fails and the reservation is refunded.
### Generate a sound
`POST /api/v1/audio/generations` · `model: "suno-sounds"`
Generates a short sound or sound bed — background music, ambience, game SFX — with optional looping, tempo and key control, and returns audio.
**Request**
| Field | Type | Required | Notes |
| ------------ | ------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-sounds`. |
| `prompt` | string | yes | Description of the sound to generate. Max 500 characters. |
| `version` | string | yes | Model version. `V5` or `V5_5` only — the older versions do not support sound generation. |
| `soundLoop` | boolean | no | Generate the result as a seamless loop, for background music and ambient beds. Defaults to `false`. |
| `soundTempo` | integer | no | Target tempo in BPM, `1`–`300`. Omit to let the model choose. |
| `soundKey` | string | no | Musical key: `Cm`, `C#m`, `Dm`, `D#m`, `Em`, `Fm`, `F#m`, `Gm`, `G#m`, `Am`, `A#m`, `Bm`, `C`, `C#`, `D`, `D#`, `E`, `F`, `F#`, `G`, `G#`, `A`, `A#`, `B`. Omit for any key. |
| `grabLyrics` | boolean | no | Capture lyric subtitles for the generated audio. Defaults to `false`. |
**Response** — same shape as a song: `output.audio_urls[]` with one URL per generated variation, and `output.tracks[]` index-aligned with it, each entry carrying the same per-track fields — `type` (`audio`), `url`, `id`, `title`, `duration` in seconds, `tags`, `lyrics` and `image_url` (cover art) — with the empty ones omitted.
## Extend & rework
### Extend a track
`POST /api/v1/audio/generations` · `model: "suno-extend"`
Continues an existing track from a chosen time point, producing a new set of audio variants that keep the source track's style.
**Request**
| Field | Type | Required | Notes |
| --------------------- | ------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-extend`. |
| `defaultParamFlag` | boolean | yes | `true` — extend using the parameters in this request (`prompt`, `style`, `title`, `continueAt` all become required). `false` — inherit the source track's parameters; only `audioId` and `version` are needed. |
| `audioId` | string | yes | The track to extend. Take it from the parent task's `output.tracks[].id`. |
| `version` | enum | yes | One of `V4`, `V4_5`, `V4_5PLUS`, `V4_5ALL`, `V5`, `V5_5`. Must match the source track's version. |
| `prompt` | string | conditional | Required when `defaultParamFlag` is `true`. Describes how the music should continue or change across the extension. Max 3000 chars on `V4`, 5000 on every other version. |
| `style` | string | conditional | Required when `defaultParamFlag` is `true`. Style of the extended audio; align it with the source track's style for the most consistent result. Max 200 chars on `V4`, 1000 on every other version. |
| `title` | string | conditional | Required when `defaultParamFlag` is `true`. Title of the extended track. Max 80 chars on `V4` and `V4_5ALL`, 100 on every other version. |
| `continueAt` | number | conditional | Required when `defaultParamFlag` is `true`. Seconds into the source track where the extension begins. Must be greater than 0 and less than the source track's duration (the upper bound is checked upstream). |
| `negativeTags` | string | no | Styles or traits to keep out of the extension, e.g. `Heavy Metal, Upbeat Drums`. |
| `vocalGender` | enum | no | `m` or `f`. Raises the probability of that vocal gender; it is not a guarantee. |
| `styleWeight` | number | no | 0–1, up to 2 decimal places. Strength of adherence to `style`. |
| `weirdnessConstraint` | number | no | 0–1, up to 2 decimal places. Controls experimental/creative deviation. |
| `audioWeight` | number | no | 0–1, up to 2 decimal places. Balance of audio features against the other inputs. |
| `personaId` | string | no | Persona ID or Voice ID to apply. Only takes effect when `defaultParamFlag` is `true`. |
| `personaModel` | enum | no | `style_persona` or `voice_persona`. Only available on `V5` and `V5_5`. |
**Response** — on completion, `output.audio_urls` holds the generated audio URLs and `output.tracks[]` carries one index-aligned entry per variant, each with `id` (pass this back as `audioId` in follow-up operations), `url`, `type` (`audio`), `title`, `duration` in seconds, `tags` (comma-separated style tags), `lyrics` and `image_url` (cover art).
**Notes** — the `defaultParamFlag` matrix is strict: with `true`, all four of `prompt`, `style`, `title` and `continueAt` must be present, and the per-version character caps above apply; with `false`, everything except `audioId` and `version` is inherited from the source track. `continueAt` must be greater than 0.
### Cover an uploaded track
`POST /api/v1/audio/generations` · `model: "suno-upload-cover"`
Re-records audio you supply in a new style while keeping its core melody, returning the reworked variants.
**Request**
| Field | Type | Required | Notes |
| --------------------- | ------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model` | string | yes | Must be `suno-upload-cover`. |
| `uploadUrl` | string | yes | Public `http(s)` URL of the source audio. Maximum 8 minutes. Base64 and `data:` URIs are rejected. |
| `customMode` | boolean | yes | `true` — supply your own `style`/`title` (and `prompt` when the result has vocals). `false` — only `prompt` is used and lyrics are generated from it. |
| `instrumental` | boolean | yes | `true` produces a track with no lyrics. In custom mode it decides whether `prompt` is required; in non-custom mode it does not change which fields are required. |
| `version` | enum | yes | One of `V4`, `V4_5`, `V4_5PLUS`, `V4_5ALL`, `V5`, `V5_5`. |
| `prompt` | string | conditional | Required in custom mode when `instrumental` is `false`, where it is used verbatim as the lyrics (max 3000 chars on `V4`, 5000 on every other version). Required in non-custom mode, where it is the core idea lyrics are written from (max 500 chars). |
| `style` | string | conditional | Required in custom mode; the genre or style of the cover, e.g. `Jazz`, `Classical`. Max 200 chars on `V4`, 1000 on every other version. Leave empty in non-custom mode. |
| `title` | string | conditional | Required in custom mode. Max 80 chars on `V4` and `V4_5ALL`, 100 on every other version. Leave empty in non-custom mode. |
| `negativeTags` | string | no | Styles or traits to exclude from the cover. |
| `vocalGender` | enum | no | `m` or `f`. Only effective when `customMode` is `true`, and only raises the probability of that vocal gender. |
| `styleWeight` | number | no | 0–1, up to 2 decimal places. Strength of adherence to `style`. |
| `weirdnessConstraint` | number | no | 0–1, up to 2 decimal places. Controls experimental/creative deviation. |
| `audioWeight` | number | no | 0–1, up to 2 decimal places. Balance of audio features against the other inputs. |
| `personaId` | string | no | Persona ID or Voice ID to apply. Only available when `customMode` is `true`. |
| `personaModel` | enum | no | `style_persona` or `voice_persona`. Only available on `V5` and `V5_5`. |
**Response** — on completion, `output.audio_urls` holds the cover's audio URLs and `output.tracks[]` carries one index-aligned entry per variant, each with `id`, `url`, `type` (`audio`), `title`, `duration` in seconds, `tags`, `lyrics` and `image_url` (cover art).
**Notes** — the requirement matrix is `customMode` × `instrumental`: custom + instrumental needs `style` and `title`; custom + vocals needs `style`, `title` and `prompt`; non-custom needs only `prompt` (capped at 500 characters) regardless of `instrumental`, and `style`/`title` should be left empty. `uploadUrl` is always required and the audio behind it must not exceed 8 minutes.
### Extend an uploaded track
`POST /api/v1/audio/generations` · `model: "suno-upload-extend"`
Continues audio you supply from a chosen time point, returning a longer track that carries on the uploaded material's style.
**Request**
| Field | Type | Required | Notes |
| --------------------- | ------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-upload-extend`. |
| `uploadUrl` | string | yes | Public `http(s)` URL of the source audio. Maximum 8 minutes. Base64 and `data:` URIs are rejected. |
| `defaultParamFlag` | boolean | yes | `true` — extend using the parameters in this request (`style`, `title` and `continueAt` become required, plus `prompt` when `instrumental` is `false`). `false` — reuse the uploaded audio's own parameters; on top of the always-required `uploadUrl`, `instrumental` and `version`, only `prompt` is needed. |
| `instrumental` | boolean | yes | `true` extends without lyrics. When `defaultParamFlag` is `true` it decides whether `prompt` is required; when `false` it does not change which fields are required. |
| `version` | enum | yes | One of `V4`, `V4_5`, `V4_5PLUS`, `V4_5ALL`, `V5`, `V5_5`. Must match the source music's version. |
| `continueAt` | number | conditional | Required when `defaultParamFlag` is `true`. Seconds into the uploaded audio where the extension begins. Must be greater than 0 and less than the uploaded audio's duration (the upper bound is checked upstream). |
| `prompt` | string | conditional | Required when `defaultParamFlag` is `true` and `instrumental` is `false`, where it is used verbatim as the lyrics; also required when `defaultParamFlag` is `false`, where lyrics are generated from it. Max 3000 chars on `V4`, 5000 on every other version. |
| `style` | string | conditional | Required when `defaultParamFlag` is `true`; e.g. `Jazz`, `Classical`, `Electronic`. Max 200 chars on `V4`, 1000 on every other version. |
| `title` | string | conditional | Required when `defaultParamFlag` is `true`. Max 80 chars on `V4` and `V4_5ALL`, 100 on every other version. |
| `negativeTags` | string | no | Styles to exclude from the extension. |
| `vocalGender` | enum | no | `m` or `f`. Raises the probability of that vocal gender; it is not a guarantee. |
| `styleWeight` | number | no | 0–1, up to 2 decimal places. Strength of adherence to `style`. |
| `weirdnessConstraint` | number | no | 0–1, up to 2 decimal places. Controls experimental/creative deviation. |
| `audioWeight` | number | no | 0–1, up to 2 decimal places. Balance of audio features against the other inputs. |
| `personaId` | string | no | Persona ID or Voice ID to apply. Only available when `defaultParamFlag` is `true`. |
| `personaModel` | enum | no | `style_persona` or `voice_persona`. Only available on `V5` and `V5_5`. |
**Response** — on completion, `output.audio_urls` holds the extended audio URLs and `output.tracks[]` carries one index-aligned entry per variant, each with `id`, `url`, `type` (`audio`), `title`, `duration` in seconds, `tags`, `lyrics` and `image_url` (cover art).
**Notes** — the requirement matrix is `defaultParamFlag` × `instrumental`: flag `true` + instrumental needs `style`, `title` and `continueAt`; flag `true` + vocals needs `style`, `title`, `prompt` and `continueAt`; flag `false` needs only `prompt` beyond the always-required `uploadUrl`, `instrumental` and `version` (everything else is taken from the uploaded audio). `continueAt` must be greater than 0, and unlike the cover operation there is no 500-character short-prompt mode — the per-version caps apply in both modes.
## Layer & combine
### Add vocals to a track
`POST /api/v1/audio/generations` · `model: "suno-add-vocals"`
Takes an audio file you host and sings a new vocal line over it, returning the layered tracks as audio URLs.
**Request**
| Field | Type | Required | Notes |
| --------------------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-add-vocals`. |
| `uploadUrl` | string | yes | Public HTTP(S) URL of the source audio to add vocals to. Base64 and `data:` URIs are rejected. |
| `prompt` | string | yes | Lyric content and singing style to guide the vocal. Min 1 character. |
| `style` | string | yes | Musical style for the vocal, e.g. `Jazz`. Min 1 character. |
| `title` | string | yes | Track title, shown in players and used for the file name. Min 1 character. |
| `negativeTags` | string | yes | Comma-separated styles or elements to keep out of the result, e.g. `heavy metal, strong drum beats`. Min 1 character. |
| `version` | string | no | One of `V4_5PLUS`, `V5`, `V5_5`. Defaults to `V4_5PLUS`. |
| `vocalGender` | string | no | `m` or `f`. Biases the voice; it raises the probability rather than guaranteeing the gender. |
| `styleWeight` | number | no | Adherence to `style`. 0–1, up to 2 decimal places. |
| `weirdnessConstraint` | number | no | Experimental / creative deviation. 0–1, up to 2 decimal places. |
| `audioWeight` | number | no | Relative weight of the source audio's elements. 0–1, up to 2 decimal places. |
**Response** — on completion, `output.audio_urls[]` holds the generated audio URLs and `output.tracks[]` carries one entry per track with `id`, `url`, `type` (`audio`), `title`, `duration` (seconds), `tags` (comma-separated style tags), `lyrics` and `image_url` (cover art). The two arrays are index-aligned, one entry per generated variant. `tracks[].id` is the `audioId` you pass to track-referencing operations.
**Notes** — unlike the generate/extend operations, all five content fields (`prompt`, `style`, `title`, `negativeTags`, `uploadUrl`) are unconditionally required, and `version` is restricted to the three newest models: `V4`, `V4_5` and `V4_5ALL` are rejected. Pricing: [reapi.ai/models/suno](https://reapi.ai/models/suno).
```bash
curl -X POST https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-add-vocals",
"uploadUrl": "https://example.com/music.mp3",
"prompt": "A calm and relaxing piano track.",
"style": "Jazz",
"title": "Relaxing Piano",
"negativeTags": "heavy metal, strong drum beats",
"version": "V4_5PLUS"
}'
```
### Add instrumental to a track
`POST /api/v1/audio/generations` · `model: "suno-add-instrumental"`
Takes an audio file you host and generates an instrumental accompaniment for it, returning the layered tracks as audio URLs.
**Request**
| Field | Type | Required | Notes |
| --------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-add-instrumental`. |
| `uploadUrl` | string | yes | Public HTTP(S) URL of the source audio to accompany. Base64 and `data:` URIs are rejected. |
| `tags` | string | yes | Styles to include, e.g. `relaxing, piano, soothing`. Defines the character of the accompaniment. Min 1 character. |
| `title` | string | yes | Track title, shown in players and used for the file name. Min 1 character. |
| `negativeTags` | string | yes | Comma-separated styles or elements to keep out of the result. Min 1 character. |
| `version` | string | no | One of `V4_5PLUS`, `V5`, `V5_5`. Defaults to `V4_5PLUS`. |
| `vocalGender` | string | no | `m` or `f`. Biases the voice; it raises the probability rather than guaranteeing the gender. |
| `styleWeight` | number | no | Adherence to the requested style. 0–1, up to 2 decimal places. |
| `weirdnessConstraint` | number | no | Experimental / creative deviation. 0–1, up to 2 decimal places. |
| `audioWeight` | number | no | Relative weight of the source audio's elements. 0–1, up to 2 decimal places. |
**Response** — same shape as add-vocals: `output.audio_urls[]` plus `output.tracks[]` with `id`, `url`, `type`, `title`, `duration`, `tags` and `lyrics` per track.
**Notes** — this operation names the style field `tags`, not `style`; sending `style` is rejected. All four of `uploadUrl`, `tags`, `title` and `negativeTags` are unconditionally required, and `version` accepts only `V4_5PLUS`, `V5` and `V5_5`. Pricing: [reapi.ai/models/suno](https://reapi.ai/models/suno).
```bash
curl -X POST https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-add-instrumental",
"uploadUrl": "https://example.com/music.mp3",
"tags": "relaxing, piano, soothing",
"title": "Relaxing Piano",
"negativeTags": "heavy metal, strong drum beats",
"version": "V4_5PLUS"
}'
```
### Mash up two tracks
`POST /api/v1/audio/generations` · `model: "suno-mashup"`
Combines exactly two audio files you host into one new piece, returning the remixed tracks as audio URLs.
**Request**
| Field | Type | Required | Notes |
| --------------------- | --------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `model` | string | yes | Must be `suno-mashup`. |
| `uploadUrlList` | string\[] | yes | Exactly 2 public HTTP(S) audio URLs. Fewer or more is rejected; base64 and `data:` URIs are rejected. |
| `customMode` | boolean | yes | `true` unlocks the detailed controls (`style`, `title`, lyrics); `false` is the simple mode driven by `prompt` alone. |
| `version` | string | yes | One of `V4`, `V4_5`, `V4_5PLUS`, `V4_5ALL`, `V5`, `V5_5`. |
| `instrumental` | boolean | no | Generate the mashup without lyrics. In custom mode it decides whether `prompt` is required; in non-custom mode it has no effect on required fields. |
| `prompt` | string | no | Conditionally required — see Notes. In custom mode it is used verbatim as the lyrics; in non-custom mode it is the core idea and lyrics are written from it. |
| `style` | string | conditional | Required in custom mode, ignored otherwise. Genre, mood or artistic direction. |
| `title` | string | conditional | Required in custom mode, ignored otherwise. Max 80 characters on every version. |
| `vocalGender` | string | no | `m` or `f`. Only takes effect when `customMode` is `true`. |
| `styleWeight` | number | no | Adherence to `style`. 0–1, up to 2 decimal places. Only takes effect when `customMode` is `true`. |
| `weirdnessConstraint` | number | no | Creative deviation. 0–1, up to 2 decimal places. Only takes effect when `customMode` is `true`. |
| `audioWeight` | number | no | Weight of the source audio elements. 0–1, up to 2 decimal places. Only takes effect when `customMode` is `true`. |
**Response** — `output.audio_urls[]` plus `output.tracks[]`, each track carrying `id`, `url`, `type`, `title`, `duration`, `tags` and `lyrics`.
**Notes** — `uploadUrlList` must contain exactly two URLs. With `customMode: true`, `style` and `title` become required, and `prompt` is required too unless `instrumental` is `true`; the caps then depend on `version` — `prompt` up to 3000 characters on `V4` and 5000 on every other version, `style` up to 200 on `V4` and 1000 elsewhere, `title` 80 throughout. With `customMode: false`, `prompt` is optional but capped at 500 characters and the other content fields are ignored. Pricing: [reapi.ai/models/suno](https://reapi.ai/models/suno).
```bash
curl -X POST https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-mashup",
"uploadUrlList": [
"https://example.com/audio1.mp3",
"https://example.com/audio2.mp3"
],
"customMode": true,
"instrumental": false,
"version": "V4_5PLUS",
"prompt": "A calm and relaxing piano track with soft melodies",
"style": "Jazz",
"title": "Relaxing Piano"
}'
```
## Section editing
### Replace a section
`POST /api/v1/audio/generations` · `model: "suno-replace-section"`
Regenerates one time window inside an existing song from a new prompt and style, blending the replacement into the material before and after it, and returns the re-rendered track(s).
**Request**
| Field | Type | Required | Notes |
| -------------- | ------ | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-replace-section`. |
| `taskId` | string | conditional | Variant A. The reAPI task id (`task_...`) of the song you are editing. Required together with `audioId`; see Notes. |
| `audioId` | string | conditional | Variant A. Identifies which track of that task to edit — take it from the parent task's `output.tracks[].id`. Required together with `taskId`. |
| `uploadUrl` | string | conditional | Variant B. Public `http(s)` URL of your own audio file. Required together with `version`. Base64 / `data:` URIs are rejected. |
| `version` | enum | conditional | Variant B. Model version used to render the replacement: `V4`, `V4_5`, `V4_5PLUS`, `V4_5ALL`, `V5`, `V5_5`. Required together with `uploadUrl`. |
| `prompt` | string | yes | Lyrics for the replaced segment. Min 1 char. |
| `tags` | string | yes | Style tags for the replaced segment, e.g. `Jazz`. Min 1 char. |
| `title` | string | yes | Title of the resulting song. Min 1 char. |
| `negativeTags` | string | no | Styles to keep out of the replaced segment, e.g. `Rock`. Min 1 char. |
| `infillStartS` | number | yes | Start of the replacement window, in seconds from the beginning of the track. `>= 0`. Must be less than `infillEndS`. Values are interpreted to 2-decimal precision (e.g. `10.50`). |
| `infillEndS` | number | yes | End of the replacement window, in seconds. `>= 0`. Must be greater than `infillStartS`, and `infillEndS - infillStartS` must be between 6 and 60 seconds. |
| `fullLyrics` | string | yes | The COMPLETE lyrics of the song after the edit — untouched sections plus the rewritten one — not just the replaced part. Min 1 char. |
**Response** — on completion, `output.audio_urls[]` holds the URL of each rendered track. `output.tracks[]` carries the same audio index-aligned with per-track metadata: `type` (`"audio"`), `url`, `id` (pass this back as `audioId` in later track-referencing operations), `title`, `duration` (seconds), `tags`, and `lyrics`.
**Notes** — the source is specified by exactly one of two mutually exclusive variants: **(A)** `taskId` + `audioId` to edit a track generated on reAPI, or **(B)** `uploadUrl` + `version` to edit audio you supply. Both halves of a variant must be present together, and mixing fields from the two variants in one request is rejected with a validation error — so is sending neither. `version` belongs to variant B only — sending it alongside `taskId` / `audioId` is what makes a request "mixed", so variant A must omit it and the replacement is rendered with the source track's own model. Two further limits apply to the window: it must span at least 6 and at most 60 seconds, and upstream additionally refuses a replacement longer than 50% of the original track's total duration. `fullLyrics` is what the whole song is re-rendered against, so passing only the new lines will drop the rest of the lyrics.
## Track assets
These operations act on a track you already generated: they take the reapi `taskId` of a completed Suno task plus (usually) the `audioId` of one track inside it, and return a derived asset. Per-operation pricing: [https://reapi.ai/models/suno](https://reapi.ai/models/suno).
### Separate vocals and instrument stems
`POST /api/v1/audio/generations` · `model: "suno-vocal-separation"`
Splits one generated track into vocal/accompaniment or per-instrument stems and returns one audio URL per stem.
**Request**
| Field | Type | Required | Notes |
| ---------- | ------ | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-vocal-separation`. |
| `taskId` | string | yes | reapi task id (`task_...`) of a completed Suno task that produced the track. Min length 1. |
| `audioId` | string | yes | The track inside that task to process. Copy it from the parent task's `output.tracks[].id`. Min length 1. |
| `type` | enum | no | Separation mode. `separate_vocal` (default) splits into 2 stems — vocals + instrumental. `split_stem` splits into up to 12 stems (vocals, backing vocals, drums, bass, guitar, keyboard, strings, brass, woodwinds, percussion, synth, FX/other). `split_stem_advanced` does multi-stem separation and can target one specific instrument via `stemName`. |
| `stemName` | enum | conditional | The single instrument track to extract. Only meaningful with `type: "split_stem_advanced"`, where it is required. 98 accepted values covering vocals, drums, guitars, keys, strings, brass, woodwinds, percussion and synths — e.g. `Lead Vocal`, `Backing Vocals`, `Drum Kit`, `Kick`, `Bass`, `Piano`, `Electric Guitar`, `Synth Pad`, `Brass Section`, `Violin`, `Flute`, `808`. |
**Response**
`output.audio_urls` holds one URL per separated stem, index-aligned with `output.tracks[]`. Each entry in `output.tracks[]` carries `type: "audio"`, `url`, and `label` naming the stem. When upstream returns per-stem records, `label` is the upstream stem group name and the track also carries `id` and `duration`; otherwise `label` comes from the fixed set `origin`, `instrumental`, `vocals`, `backing_vocals`, `drums`, `bass`, `guitar`, `piano`, `keyboard`, `percussion`, `strings`, `synth`, `fx`, `brass`, `woodwinds`. Stems that upstream did not produce are simply absent — the array length varies with the mode and the source material.
**Notes** — `stemName` is rejected as missing only when `type` is `split_stem_advanced`; the other two modes ignore it. Separation quality depends on the mix: cleanly separated AI mixes yield the best stems. The per-stem `id` values returned here are what you pass as `audioId` to `suno-midi`.
### Convert a track to WAV
`POST /api/v1/audio/generations` · `model: "suno-wav"`
Re-renders one generated track as an uncompressed WAV file for professional editing.
**Request**
| Field | Type | Required | Notes |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-wav`. |
| `taskId` | string | yes | reapi task id (`task_...`) of the completed Suno task that produced the track. Min length 1. |
| `audioId` | string | yes | Which track in that task to convert. Copy it from the parent task's `output.tracks[].id`. Min length 1. |
**Response**
`output.audio_urls` holds a single URL pointing at the WAV file. No `output.tracks[]` is emitted for this operation.
**Notes** — WAV files are typically 5–10× larger than the MP3 equivalent, and processing time scales with the length of the source track.
### Generate MIDI from separated stems
`POST /api/v1/audio/generations` · `model: "suno-midi"`
Transcribes separated audio into structured MIDI note data (pitch, timing, velocity) per detected instrument.
**Request**
| Field | Type | Required | Notes |
| --------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-midi`. |
| `taskId` | string | yes | reapi task id (`task_...`) of a completed **vocal-separation** task — not a music-generation task. Min length 1. |
| `audioId` | string | no | Restricts transcription to one separated stem; take the value from that separation task's `output.tracks[].id`. Omit to transcribe all separated stems. Min length 1. |
**Response**
A structured text result, not audio: there is no `audio_urls` or `tracks`. On completion `output.result.midi` holds a `state` string and an `instruments[]` array. Each instrument has a `name` and a `notes[]` array whose entries carry `pitch` (MIDI note number, 0–127), `start` and `end` (seconds), and `velocity` (0–1).
**Notes** — Separation must run first; passing the task id of a plain music-generation task is not valid input for this operation. Not every instrument is detected — the result reflects what is actually present in the source stems, and upstream notes that separations run with `type: "split_stem"` can yield no note data at all. If upstream completes with no note data, the task is reported as failed rather than completing with an empty result.
### Create a music video
`POST /api/v1/audio/generations` · `model: "suno-music-video"`
Renders one generated track into an MP4 with visualizations plus optional artist and brand attribution.
**Request**
| Field | Type | Required | Notes |
| ------------ | ------ | -------- | --------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-music-video`. |
| `taskId` | string | yes | reapi task id (`task_...`) of the completed Suno task that produced the track. Min length 1. |
| `audioId` | string | yes | Which track in that task to visualize. Copy it from the parent task's `output.tracks[].id`. Min length 1. |
| `author` | string | no | Artist or creator name shown as a signature on the video cover. 1–50 characters. |
| `domainName` | string | no | Website or brand shown as a watermark at the bottom of the video. 1–50 characters. |
**Response**
`output.video_urls` holds a single URL pointing at the rendered MP4.
**Notes** — Render time varies with the length of the source track.
### Generate cover art
`POST /api/v1/audio/generations` · `model: "suno-cover-image"`
Generates cover images for a completed music task; usually returns two stylistic variants to choose from.
**Request**
| Field | Type | Required | Notes |
| -------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-cover-image`. |
| `taskId` | string | yes | reapi task id (`task_...`) of the completed music-generation task to illustrate. Min length 1. Note this operation targets the task as a whole — there is no `audioId`. |
**Response**
`output.image_urls` holds the generated cover images, typically two.
**Notes** — A cover can be generated only once per source task; a second request for the same `taskId` is rejected upstream rather than producing new images. Submit it after the music task has completed.
## Voice cloning
Cloning a singing voice is a three-step chain, and each step is its own billable task (see [pricing](https://reapi.ai/models/suno)):
1. **`suno-voice-validate`** — submit the source recording and the vocal segment to analyse. The completed task returns a *validation phrase*.
2. **`suno-voice-generate`** — have the person record themselves reading (ideally singing) that phrase, host the recording at a public URL, and submit it as `verifyUrl` together with the reapi `taskId` of the step-1 task. The completed task returns a `voiceId`.
3. **`suno-voice-regenerate`** — only needed when a validation phrase failed or expired.
The `voiceId` from step 2 is then used in the generation operations that accept a persona: pass it as `personaId` with `personaModel: "voice_persona"`.
### Step 1 — Generate a validation phrase
`POST /api/v1/audio/generations` · `model: "suno-voice-validate"`
Analyses the vocal segment of a source recording and returns the phrase the speaker must read aloud to verify the voice.
**Request**
| Field | Type | Required | Notes |
| ------------- | ------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-voice-validate`. |
| `voiceUrl` | string | yes | Public `http(s)` URL of the source recording. Base64 / `data:` payloads are rejected. |
| `vocalStartS` | integer | yes | Start time, in seconds, of the vocal segment to extract. `0` or greater. |
| `vocalEndS` | integer | yes | End time, in seconds, of the vocal segment. Must be greater than `vocalStartS`. |
| `language` | string | no | Language of the generated phrase. Upstream documents `en`, `zh`, `es`, `fr`, `pt`, `de`, `ja`, `ko`, `hi`, `ru`. |
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-voice-validate",
"voiceUrl": "https://example.com/audio/source_voice.mp3",
"vocalStartS": 0,
"vocalEndS": 10,
"language": "en"
}'
```
**Response** — a structured text result. When the task completes, `output.result.validateInfo` holds the phrase text to be read, and `output.result.status` holds the upstream state that made it terminal (`wait_validating` while the phrase awaits the reader, `success` once the whole flow has finished).
**Notes** — `vocalEndS` must be strictly greater than `vocalStartS`; equal or inverted values are rejected before the task is created. Keep the reapi task id (`task_...`) returned by this call: step 2 references it, and it is the only handle you have on the phrase — the upstream phrase id is never exposed.
### Step 2 — Create the custom voice
`POST /api/v1/audio/generations` · `model: "suno-voice-generate"`
Submits the reader's verification recording against a completed validation task and produces a reusable custom voice.
**Request**
| Field | Type | Required | Notes |
| ------------------ | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-voice-generate`. |
| `taskId` | string | yes | Your reapi task id (`task_...`) from step 1 (or from a step-3 regeneration). Must be your own task, in `completed` status, from the same surface, and from the voice flow. |
| `verifyUrl` | string | yes | Public `http(s)` URL of the recording of the person reading the `validateInfo` phrase. Singing the phrase rather than speaking it produces a more accurate voice profile. Base64 / `data:` payloads are rejected. |
| `voiceName` | string | no | Display name for the voice. |
| `description` | string | no | Free-text description of the voice. |
| `style` | string | no | Voice style, e.g. `Pop, Female Vocal`. |
| `singerSkillLevel` | enum | no | One of `beginner`, `intermediate`, `advanced`, `professional`. Upstream default is `beginner`. |
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-voice-generate",
"taskId": "task_abc123",
"verifyUrl": "https://example.com/audio/verify_read.mp3",
"voiceName": "My Voice",
"description": "created from uploaded voice",
"style": "Pop, Female Vocal",
"singerSkillLevel": "beginner"
}'
```
**Response** — a structured text result: `output.result.voiceId`, the identifier of the finished custom voice.
**Notes** — feed `voiceId` back into a generation call as `personaId`, together with `personaModel: "voice_persona"`. The persona fields only apply in custom mode (`customMode: true`), and `personaModel` is only available on `V5` and `V5_5`. The `taskId` reference is resolved and rejected before any charge, so a bad or unfinished parent task costs nothing. A clean a-cappella take of the verification recording gives the most accurate voice profile.
### Step 3 — Regenerate the validation phrase
`POST /api/v1/audio/generations` · `model: "suno-voice-regenerate"`
Asks upstream for a fresh validation phrase on an existing voice task, for when the previous phrase failed or expired.
**Request**
| Field | Type | Required | Notes |
| -------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Must be `suno-voice-regenerate`. |
| `taskId` | string | yes | Your reapi task id (`task_...`) of an earlier voice-flow task. Same ownership / `completed` / voice-family checks as step 2. |
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-voice-regenerate",
"taskId": "task_abc123"
}'
```
**Response** — a structured text result carrying the NEW phrase: the task completes with `output.result.validateInfo` (and its `status`), the same shape step 1 returns.
**Notes** — this reissues the validation phrase, it does not produce a voice. Record yourself reading the new phrase and send that recording to step 2 (`suno-voice-generate`) with this task's id as `taskId`. Use it when the earlier phrase expired or the reading was rejected.
## Synchronous helpers
These four operations do not run on `/api/v1/audio/generations` and have no task lifecycle — they answer inline on the same HTTP response, so there is nothing to poll. Each has its own route, accepts only the fields listed below (unknown fields are rejected), and returns `id` (the audit task id recording this call, `task_...`), `credits` (the credits charged), plus its result fields. A failed call is refunded in full. Pricing: [reapi.ai/models/suno](https://reapi.ai/models/suno).
```bash
curl -X POST https://reapi.ai/api/v1/audio/suno/boost-style \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content":"Pop, Mysterious"}'
```
### Boost music style
`POST /api/v1/audio/suno/boost-style`
Expands a short style description into a richer, generation-ready style prompt and returns the enhanced text inline.
**Request**
| Field | Type | Required | Notes |
| --------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `content` | string | yes | Style description to enhance, min 1 character. Describe the music style you expect in concise, clear language — e.g. `Pop, Mysterious`. |
**Response** — `result`: the enhanced style text. Feed it into the `style` field of a generation request.
### Timestamped lyrics
`POST /api/v1/audio/suno/timestamped-lyrics`
Returns word-level time-aligned lyrics for one track of a completed song task.
**Request**
| Field | Type | Required | Notes |
| --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------- |
| `taskId` | string | yes | Your reapi task id (`task_...`) of the completed song task the track belongs to, min 1 character. |
| `audioId` | string | yes | Identifier of the specific track, min 1 character. Take it from the parent task's `output.tracks[].id`. |
**Response**
* `alignedWords`: array of aligned words, each with `word` (the lyric token), `success` (boolean, whether that token aligned), `startS` / `endS` (word start and end time in seconds), and `palign` (alignment parameter).
* `waveformData`: array of numbers for audio visualisation, or `null` when upstream omits it.
* `hootCer`: lyrics alignment accuracy score, or `null`.
* `isStreamed`: whether the source is streaming audio, or `null`.
**Notes** — `taskId` must name a Suno-family task you own that has reached `completed`; a task that is still running, or one from outside the Suno family, is rejected before any charge. Which Suno operation produced it is not gated here — upstream expects a music-generating parent (song or extend) and answers for the rest. `waveformData` is delivered inline only — it is deliberately not kept in the audit row, so a later `GET /api/v1/tasks/{id}` on this call's `id` returns the other three fields plus `waveform_omitted: true`.
### Generate persona
`POST /api/v1/audio/suno/persona`
Analyses a segment of an existing track and creates a reusable persona, returning its `personaId`.
**Request**
| Field | Type | Required | Notes |
| ------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `taskId` | string | yes | Your reapi task id (`task_...`) of the completed song task to analyse, min 1 character. |
| `audioId` | string | yes | Identifier of the track to build the persona from, min 1 character. Take it from the parent task's `output.tracks[].id`. |
| `name` | string | yes | Name for the persona, min 1 character — something that captures the musical style or character, e.g. `Electronic Pop Singer`. |
| `description` | string | yes | Description of the persona's musical characteristics, style and personality, min 1 character. Be specific about genre, mood, instrumentation and vocal qualities. |
| `vocalStart` | number | no | Start of the analysis segment in seconds, `>= 0`. Defaults to `0`. |
| `vocalEnd` | number | no | End of the analysis segment in seconds, `>= 0`. Defaults to `30`. |
| `style` | string | no | Supplementary style tag for the persona, min 1 character — e.g. `Electronic Pop`, `Jazz Trio`. |
**Response** — `personaId`: the persona identifier, reusable in the `personaId` field of the song, extend, upload-cover and upload-extend operations. `name` and `description` echo the request (each `null` if upstream omits it).
**Notes** — the analysis window is validated before charging: `vocalEnd - vocalStart` must be between 10 and 30 seconds inclusive, using the defaults `0` and `30` for whichever bound you omit. Setting only `vocalStart: 25` therefore fails (window is 5s), while `vocalStart: 25, vocalEnd: 40` succeeds. `taskId` must name a completed Suno task you own.
### Check voice availability
`POST /api/v1/audio/suno/check-voice`
Reports whether a custom voice produced by a completed voice task is ready to use.
**Request**
| Field | Type | Required | Notes |
| --------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `task_id` | string | yes | Your reapi task id (`task_...`) of the completed voice task to check, min 1 character. This endpoint's field is snake\_case `task_id` — the rest of the surface is camelCase. |
**Response** — `isAvailable`: boolean, whether the generated voice is available.
**Notes** — `task_id` must resolve to a completed **voice-family** task you own — in practice the `suno-voice-generate` or `suno-voice-regenerate` task that produced the voice; a song task is rejected before any charge.
## Output schema
Asynchronous results land on `GET /api/v1/tasks/{id}`:
```json
{
"id": "task_...",
"status": "completed",
"output": {
"audio_urls": ["https://..."],
"tracks": [
{
"id": "track id — reuse as audioId",
"url": "https://...",
"title": "Midnight Drive",
"duration": 128.5,
"tags": "synthwave, dreamy",
"lyrics": "..."
}
]
},
"error": null
}
```
Music videos populate `output.video_urls`, cover art
`output.image_urls`; lyric / MIDI / voice tasks return a structured
text `output` (e.g. `{ "lyrics": [...] }`, `{ "midi": ... }`,
`{ "voiceId": "..." }`, `{ "validateInfo": "..." }`).
## Pricing dimensions
Every operation bills a flat rate per request — nothing scales with
duration or output length. Generation-class operations (music, extend,
covers, vocals, mashup) share one rate covering both returned tracks;
section replacement, stem separation (three tiers by `type`), music
video, sound effects, lyrics, WAV conversion, and the utility
operations each have their own flat rate. Bill formula:
`credits = price_usd × 1000` (1 credit = $0.001). Current rates:
[Suno model page](/models/suno).
## Errors
Failures use the standard envelope
`{ "error": { "code", "message", "request_id" } }` — see the
[error catalog](/docs/api/errors). Notable cases: content flagged by
moderation returns a policy error (80006); malformed parameter
combinations surface 80007 with the upstream reason; upstream
generation failures (80003) are refunded automatically.
## Tips
* Start in inspiration mode (`customMode: false`) — one prompt line is
enough. Switch to custom mode when you need exact lyrics.
* `style` reads best as comma-separated genre + mood + vocal tags
(`"synthwave, dreamy, female vocal"`). Use the boost-style helper to
expand a rough idea.
* Save `output.tracks[].id` — every downstream operation needs it.
* For extensions, pick `continueAt` a beat before the natural end of
the source so the transition lands cleanly.
* Instrumental-only? Set `instrumental: true` and skip lyrics entirely.
## Related
* [Mureka V9 Song](/docs/mureka-v9-song) — lyrics-first song generation
* [Music Video 1.0](/docs/music-video-1-0) — song + images → MV
* [Vocal Remover](/docs/vocal-remover) — standalone stem separation
---
# topaz-video-upscaler (https://reapi.ai/docs/topaz-video-upscaler)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Topaz Video Upscaler upscales and enhances a **source video** — restoring
> detail and pushing resolution toward 4K. One async endpoint, two parameters:
> a `video_url` and an `upscale_factor` (`"1"` / `"2"` / `"4"`). Billing is per
> second of the source clip's server-probed length × the upscale tier. See
> current pricing on the
> [model page](https://reapi.ai/models/topaz-video-upscaler).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "topaz-video-upscaler",
"video_url": "https://your-cdn.com/source-480p.mp4",
"upscale_factor": "2"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "topaz-video-upscaler",
"video_url": "https://your-cdn.com/source-480p.mp4",
"upscale_factor": "2",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "topaz-video-upscaler",
video_url: "https://your-cdn.com/source-480p.mp4",
upscale_factor: "2",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "topaz-video-upscaler",
"video_url": "https://your-cdn.com/source-480p.mp4",
"upscale_factor": "2",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "topaz-video-upscaler",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
upscaled MP4 URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Request body
### `model` — required
`string`. Must be `"topaz-video-upscaler"`.
### `video_url` — required
`string`. Public HTTP(S) URL of the **source video** to upscale — MP4, MOV, or
MKV, up to 50MB. The output preserves the source clip's duration and frame
timing. The **billable second count is the source video's server-probed
duration**, not a client estimate.
**No `data:` URIs.** reApi rejects base64 inputs platform-wide. Upload to public
storage (your own CDN, S3, R2…) and pass the URL.
### `upscale_factor` — string, default `"2"`
How much each frame is enlarged. The factor also selects the per-second pricing
tier.
| Value | Behavior | Tier |
| ----- | ----------------------------------------------- | ----------- |
| `"1"` | Clean and restore detail at the same resolution | `1×` / `2×` |
| `"2"` | Double each dimension (default) | `1×` / `2×` |
| `"4"` | Enlarge 4× toward 4K | `4×` |
`"1"` and `"2"` share one per-second rate; `"4"` is a higher tier.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "topaz-video-upscaler",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
***
## Pricing
Per-second × upscale tier. The billable second count is the source video's
**server-probed** length — reApi measures the uploaded clip server-side rather
than trusting a client value. The `1×` / `2×` factors share one per-second rate;
`4×` is a higher tier.
**Bill formula** (1 credit = $0.001):
```
billable_seconds = ceil(server_probed_source_seconds)
bill_usd = per_second_usd(tier) × billable_seconds // tier from upscale_factor
credits = ceil(bill_usd × 1000)
```
See current per-second rates for each tier on the
[model page](https://reapi.ai/models/topaz-video-upscaler). Failed jobs refund
automatically. A probe failure returns `400 PRICING_UNAVAILABLE` (code `30002`)
with no charge.
The playground's draft estimate may show a default before the upload finishes —
the authoritative number is computed at submit, after the server probes the
file.
***
## Validation errors
All cases below return HTTP 400. Pattern-match on `code`, not `message` —
message strings carry request-specific context and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| ------------------------------------------------ | ------- | --------------------------------------------------------------- |
| `video_url` missing | `20002` | `topaz-video-upscaler: video_url is required` |
| `video_url` carrying a `data:` URI / non-public | `20003` | `topaz-video-upscaler: video_url must be a public http(s) URL` |
| `upscale_factor` not one of `1` / `2` / `4` | `20003` | `topaz-video-upscaler: invalid upscale_factor (allowed: 1/2/4)` |
| Source video probe fails (network / format) | `30002` | `Could not determine source video duration for billing: …` |
| Source video too large / unsupported by upstream | `80007` | Provider rejected the request as invalid |
The full envelope is `{ "error": { "code", "message", "request_id" } }` — see
[Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Minimum request (2× default)
```json
{
"model": "topaz-video-upscaler",
"video_url": "https://your-cdn.com/source-480p.mp4"
}
```
### Same-size cleanup (1×)
```json
{
"model": "topaz-video-upscaler",
"video_url": "https://your-cdn.com/source-1080p-grainy.mp4",
"upscale_factor": "1"
}
```
### Push to 4K (4×)
```json
{
"model": "topaz-video-upscaler",
"video_url": "https://your-cdn.com/source-540p.mp4",
"upscale_factor": "4"
}
```
***
## Tips
* **Cost tracks the source clip.** A 6s source bills \~6 seconds; trim or
compress long footage before submitting to cap spend.
* **Pick the factor by need.** `1×` restores detail at the same size, `2×` is the
balanced default, `4×` targets 4K from low-resolution sources.
* **Mind the 50MB input cap.** Re-encode or split oversized clips; the upstream
rejects files over the limit (`80007`), refunded automatically.
* **Length is preserved.** Upscaling changes resolution and detail, not
duration — a 10s input returns a 10s output.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
---
# Veo 3.1 (https://reapi.ai/docs/veo3-1)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Google's VEO 3.1 video model on reAPI. **Five channels** share one
> endpoint and submit shape — pick the channel via `model`.
>
> | Channel | Bills | Notes |
> | ------------------------- | -------------- | ------------------------------------- |
> | `veo3.1-fast` | per generation | Image-to-video (≤ 3 refs), remix-able |
> | `veo3.1-quality` | per generation | Frame-mode I2V only, remix-able |
> | `veo3.1-lite` | per generation | Prompt-only — no images |
> | `veo3.1-fast-official` | per second | First/last-frame I2V, audio, 4K |
> | `veo3.1-quality-official` | per second | Premium per-second tier |
>
> 8-second outputs on alt channels (4 / 6 / 8 seconds on official) at
> 720p / 1080p / 4K. Use the [remix endpoint](#remix) to extend Fast
> or Quality clips to 15 seconds. See current pricing on the
> [model page](https://reapi.ai/models/veo3-1).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "veo3.1-fast",
"prompt": "A dolphin leaping through cobalt ocean waves at sunrise",
"aspect_ratio": "16:9",
"resolution": "720p"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "veo3.1-fast",
"prompt": "A dolphin leaping through cobalt ocean waves at sunrise",
"aspect_ratio": "16:9",
"resolution": "720p",
},
)
task_id = resp.json()["data"][0]["task_id"]
```
```js
const res = await fetch('https://reapi.ai/api/v1/videos/generations', {
method: 'POST',
headers: {
Authorization: 'Bearer rk_live_xxx',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'veo3.1-fast',
prompt: 'A dolphin leaping through cobalt ocean waves at sunrise',
aspect_ratio: '16:9',
resolution: '720p',
}),
});
const { data } = await res.json();
const taskId = data[0].task_id;
```
```go
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "veo3.1-fast",
"prompt": "A dolphin leaping through cobalt ocean waves at sunrise",
"aspect_ratio": "16:9",
"resolution": "720p",
})
req, _ := http.NewRequest("POST", "https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
_ = out
}
```
## Endpoint
`POST /api/v1/videos/generations`
Returns a `task_id` immediately. Poll `GET /api/v1/tasks/{task_id}` until
`status` is `completed` to retrieve the video URL.
## Request fields
### Common (every channel)
| Field | Type | Required | Notes |
| -------------- | ------ | -------- | ------------------------------------ |
| `model` | string | yes | One of the five channel ids above |
| `prompt` | string | yes | ≤ 4000 chars; English recommended |
| `aspect_ratio` | enum | no | `16:9` (default landscape) or `9:16` |
| `resolution` | enum | no | `720p` (default), `1080p`, or `4k` |
### Alt channels (`veo3.1-fast`, `veo3.1-quality`, `veo3.1-lite`)
| Field | Type | Notes |
| ----------------- | --------- | ------------------------------------------------------ |
| `duration` | int | Fixed at `8` upstream |
| `image_urls` | string\[] | Up to 3 public http(s) URLs (Fast / Quality only) |
| `generation_type` | enum | `frame` or `reference` (Quality channel: `frame` only) |
| `enable_gif` | bool | Output GIF instead of mp4. Mutex with `1080p` / `4k` |
`veo3.1-lite` rejects `image_urls` and `generation_type`. Send only
`prompt` + the common fields.
### Official channels (`veo3.1-fast-official`, `veo3.1-quality-official`)
| Field | Type | Notes |
| ------------------- | ------ | -------------------------------------------------- |
| `duration` | int | `4`, `6`, or `8` (default `8`) |
| `negative_prompt` | string | What to avoid |
| `first_frame_image` | string | Public http(s) URL — anchors opening frame |
| `last_frame_image` | string | Requires `first_frame_image`; interpolation target |
| `seed` | int | `0`–`4294967295` |
| `sample_count` | int | `1`–`4` (default `1`) |
| `generate_audio` | bool | Adds synthesized audio (audio pricing tier) |
| `person_generation` | enum | `allow_adult` (default) or `disallow` |
| `resize_mode` | enum | `pad` (default) or `crop` |
| `enhance_prompt` | bool | Must be `true` if sent — omit to disable |
**Image inputs are URLs only.** Every reference image / frame field
rejects base64 and `data:` URIs at the gateway. Upload the image to
any public HTTPS host first.
## Image-to-video — alt channel
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "veo3.1-fast",
"prompt": "The cat slowly walks forward and looks around",
"image_urls": ["https://example.com/cat.png"],
"generation_type": "frame",
"aspect_ratio": "16:9",
"resolution": "720p"
}'
```
## Image-to-video — official channel (first / last frame)
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "veo3.1-quality-official",
"prompt": "Smooth cinematic transition from the first frame to the last frame",
"first_frame_image": "https://example.com/start.png",
"last_frame_image": "https://example.com/end.png",
"aspect_ratio": "16:9",
"resolution": "1080p",
"duration": 8,
"generate_audio": true
}'
```
## Remix — extend an 8s clip to 15s
`POST /api/v1/videos/{task_id}/remix`
Available on `veo3.1-fast` and `veo3.1-quality` only. The path `task_id`
is the reApi task id returned by the original generation; the source
task must be in `completed` status. The `model` in the body must match
the source task's model.
```bash
curl https://reapi.ai/api/v1/videos/{TASK_ID}/remix \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "veo3.1-fast",
"prompt": "The dolphin dives back into the wave and surfaces a moment later",
"raw": false,
"aspect_ratio": "16:9",
"resolution": "720p"
}'
```
| Field | Type | Notes |
| -------------- | ------ | -------------------------------------------------------------------------------------- |
| `model` | enum | `veo3.1-fast` or `veo3.1-quality` — must equal source |
| `prompt` | string | Continuation prompt; ≤ 4000 chars |
| `raw` | bool | `true` returns only the extension segment; `false` (default) returns the combined clip |
| `aspect_ratio` | enum | Optional — same set as generations |
| `resolution` | enum | Optional — same set as generations |
The remix endpoint returns a fresh `task_id`. Poll
`GET /api/v1/tasks/{task_id}` exactly like a generation.
## Polling
```bash
curl https://reapi.ai/api/v1/tasks/{TASK_ID} \
-H "Authorization: Bearer rk_live_xxx"
```
Response while in flight:
```json
{ "code": 200, "data": { "id": "task_…", "status": "processing", "progress": 47 } }
```
Response on success:
```json
{
"code": 200,
"data": {
"id": "task_…",
"status": "completed",
"result": { "videos": [{ "url": ["https://cdn.reapi.ai/…/video.mp4"] }] }
}
}
```
Generated video URLs are rehosted to reApi's CDN — they don't expire
with the upstream signed-URL window.
---
# viduq3 (https://reapi.ai/docs/viduq3)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Vidu Q3 — async video generation. **Two variants** (`viduq3-pro`,
> `viduq3-turbo`) share one endpoint and one parameter shape. Mode is
> implicit: the number of `image_urls` you send picks **T2V**, **I2V**, or
> **first/last-frame** transition. 1–16 second outputs at 540p / 720p /
> 1080p, audio-on by default. See current pricing on the
> [model page](https://reapi.ai/models/viduq3).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "viduq3-pro",
"prompt": "A kitten playing piano, slow camera push-in",
"duration": 8,
"resolution": "1080p",
"aspect_ratio": "16:9"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
json={
"model": "viduq3-pro",
"prompt": "A kitten playing piano, slow camera push-in",
"duration": 8,
"resolution": "1080p",
"aspect_ratio": "16:9",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer rk_live_xxx",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "viduq3-pro",
prompt: "A kitten playing piano, slow camera push-in",
duration: 8,
resolution: "1080p",
aspect_ratio: "16:9",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "viduq3-pro",
"prompt": "A kitten playing piano, slow camera push-in",
"duration": 8,
"resolution": "1080p",
"aspect_ratio": "16:9",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer rk_live_xxx")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "viduq3-pro",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls`
holds the generated MP4 URL, valid for 7 days.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
Keys carry the active workspace's billing scope — there is no separate
project header.
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the
task endpoint returns the same envelope until completion. Polling does
not consume credits.
***
## Variants
Both variants share one parameter shape; pick via `model`:
| Variant | Speed | Resolutions | Notes |
| -------------- | -------- | ------------------- | --------------------- |
| `viduq3-pro` | standard | 540p / 720p / 1080p | Highest fidelity |
| `viduq3-turbo` | faster | 540p / 720p / 1080p | Lower per-second cost |
reAPI never silently substitutes one variant for another — what you send
in `model` is what gets billed and forwarded.
***
## Mode routing
`viduq3` picks its mode from the **count of `image_urls`** you send —
there is no `mode` parameter:
| `image_urls` count | Mode | What it does |
| ------------------ | -------------- | --------------------------------------------- |
| `0` (or omitted) | **T2V** | Generate from text. `aspect_ratio` allowed. |
| `1` | **I2V** | Animate from a single starting frame. |
| `2` | **First/Last** | Transition between two frames (first → last). |
**Mutex rules.**
* In T2V (no `image_urls`), `prompt` is **required**.
* When `image_urls` is set (1 or 2 entries), `aspect_ratio` is **rejected**
with `400` — the source frame's ratio decides the output ratio.
* More than 2 `image_urls` is rejected with `400 image_urls accepts at
most 2 entries`.
***
## Request body
### `model` — required
`string`. One of `"viduq3-pro"` or `"viduq3-turbo"`.
### `prompt` — string, conditional
Up to **2,000 characters**. Required in T2V (when `image_urls` is empty);
optional in I2V and First/Last when at least one image is provided.
Empty / whitespace-only prompts are treated as missing.
**Failure modes.**
* Empty / missing in T2V → `400 prompt is required when image_urls is empty (text-to-video)` (code `20002`).
* Longer than 2,000 chars → `400 prompt exceeds 2000 characters (got N)` (code `20007`).
### `duration` — integer, default `5`
Output length in seconds. Any integer in `[1, 16]`. Out-of-range →
`400`. Drives pricing linearly:
`ceil(per_second_usd × duration × 1000)` credits (1 credit = $0.001).
### `resolution` — string, default `"720p"`
`540p` / `720p` / `1080p`. Drives pricing. Lowercase is canonical;
uppercase forms (`"1080P"`) are accepted and normalized.
### `aspect_ratio` — string, T2V only
Output ratio in T2V mode. One of:
| Value | Shape |
| ------ | --------------------- |
| `16:9` | Landscape |
| `9:16` | Portrait |
| `4:3` | Traditional landscape |
| `3:4` | Traditional portrait |
| `1:1` | Square |
**Only valid when `image_urls` is empty.** In I2V and First/Last modes
the upstream derives the ratio from the source frame and rejects this
field with `400 aspect_ratio is only allowed in text-to-video mode`
(code `20003`).
### `image_urls` — string\[]
Array of public HTTP(S) URLs. **0 to 2 entries**:
* **0 entries** — pure text-to-video.
* **1 entry** — image-to-video; the image is treated as the first frame.
* **2 entries** — first/last-frame transition. The first URL is the
starting frame; the second URL is the ending frame.
**No `data:` URIs.** reAPI rejects base64 inputs platform-wide — every
URL field on this endpoint must be a public HTTP(S) URL. Upload to
your own object storage (S3, R2, OSS, …) and pass the URL.
### `audio` — boolean, default `true`
When `true`, the model synthesizes an audio track (dialogue, sound
effects) that plays alongside the generated video. Set to `false` for
silent output. Audio generation does not change the per-second rate.
### `seed` — integer
Reproducibility hint. Range `[-1, 4294967295]`. Same seed plus an
otherwise identical request returns a similar (not bit-for-bit
identical) result. Use `-1` (or omit) for full randomness.
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in
over time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "viduq3-pro",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
`output.video_urls` URLs are valid for **7 days**. Re-host to your own
storage if you need them longer.
***
## Validation errors
All cases below return HTTP 400 with code `20003` unless noted. Pattern-match
on `code`, not `message` — message strings carry request-specific context
(field names, observed values, etc.) and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| -------------------------------------------------- | ------- | --------------------------------------------------------------------------------------- |
| `prompt` missing in T2V | `20002` | `viduq3: prompt is required when image_urls is empty (text-to-video)` |
| `prompt` longer than 2,000 chars | `20007` | `viduq3: prompt exceeds 2000 characters (got N)` |
| `image_urls` length > 2 | `20003` | `viduq3: image_urls accepts at most 2 entries, got N` |
| `aspect_ratio` set with `image_urls` non-empty | `20003` | `viduq3: aspect_ratio is only allowed in text-to-video mode (image_urls must be empty)` |
| `duration` outside `[1, 16]` | `20003` | `viduq3: duration must be 1-16 seconds, got N` |
| Unknown `resolution` | `20003` | `viduq3: invalid resolution "X" (allowed: 540p / 720p / 1080p)` |
| Unknown `aspect_ratio` | `20003` | `viduq3: invalid aspect_ratio "X" (allowed: 16:9 / 9:16 / 4:3 / 3:4 / 1:1)` |
| `seed` outside `[-1, 4294967295]` | `20003` | `viduq3: seed must be in [-1, 4294967295], got N` |
| `image_urls` carrying a `data:` URI or non-http(s) | `20003` | `viduq3: image_urls entries must be public http(s) URLs` |
The full envelope is `{ "error": { "code", "message", "request_id" } }` —
see [Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### T2V — minimum request
```json
{
"model": "viduq3-pro",
"prompt": "A little girl walking down a sunset coastal road"
}
```
### T2V — full parameters
```json
{
"model": "viduq3-pro",
"prompt": "A kitten playing piano, slow camera push-in, cinematic warm tones",
"duration": 8,
"resolution": "1080p",
"aspect_ratio": "16:9",
"audio": true,
"seed": 42
}
```
### I2V — animate a single frame
```json
{
"model": "viduq3-pro",
"prompt": "Bring the scene to life with a gentle camera dolly forward",
"image_urls": ["https://your-cdn.com/first_frame.jpg"],
"duration": 5,
"resolution": "1080p"
}
```
### First/Last-frame transition
```json
{
"model": "viduq3-pro",
"prompt": "Smooth motion from standing to seated",
"image_urls": [
"https://your-cdn.com/standing.jpg",
"https://your-cdn.com/seated.jpg"
],
"duration": 8
}
```
### Silent video — disable audio
```json
{
"model": "viduq3-pro",
"prompt": "Sunset timelapse over the ocean",
"duration": 10,
"resolution": "1080p",
"audio": false
}
```
### Turbo — cost-conscious
```json
{
"model": "viduq3-turbo",
"prompt": "Waves crashing on a beach at sunset, wide shot",
"duration": 5,
"resolution": "720p"
}
```
***
## Choosing a mode
| Need | Send |
| ------------------------------------ | ---------------------------------------------------------------- |
| Generate from text | `prompt` only (T2V) — `aspect_ratio` allowed |
| Animate a still | `prompt + image_urls` (1 entry) (I2V) |
| Smooth transition between two frames | `image_urls` (2 entries) (First/Last) |
| Cut spend | Switch to `viduq3-turbo` or drop `resolution` to `720p` / `540p` |
***
## Polling pattern
The task endpoint behaves identically to other video tasks — the only
difference is the completed `output` shape (`video_urls` instead of
`image_urls`). A pragmatic schedule:
```
0–5 minutes: poll every 5s
5 min – 1 h: back off gradually toward 1 min
≥ 1 h: cap at 3 min between polls
```
A typical task completes in a few minutes. The worker's wall-clock cap
is **48 hours**, comfortably above any realistic queue.
***
## Pricing
Per-second × resolution. Mode does **not** change the rate. Turbo
undercuts Pro at every tier. See current rates on the
[Vidu Q3 model page](https://reapi.ai/models/viduq3).
**Bill formula** (1 credit = $0.001):
```
credits = ceil(per_second_usd × duration × 1000)
```
Failed jobs refund automatically.
***
## Tips
* **Prompt motion, not just scene.** "Slow push-in, warm tones, shallow
depth of field" outperforms a pure noun-list of what's on screen.
* **Sweet-spot duration: 5–10 seconds.** Below 5s motion looks choppy;
above 10s the upstream wall-time grows fast.
* **First-frame quality matters.** Subject centered, clear composition,
no heavy filters — I2V output quality tracks input quality directly.
* **Two frames need to make sense as endpoints.** First/Last works best
when the two frames share enough subject and composition that the
model can interpolate motion between them — wildly different shots
produce abrupt transitions.
* **Pick Turbo first if you're iterating.** It's roughly half the cost
per second; lock the prompt on Turbo, then re-render the keeper on
Pro at the resolution you'll ship.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# Vocal Remover API (https://reapi.ai/docs/vocal-remover)
# Vocal Remover API
Use `audio-stem-separator` with `stem: "vocals"` to isolate vocals and receive
downloadable audio tracks through the universal task flow.
## Endpoint
```http
POST /api/v1/audio/generations
```
Poll the returned task:
```http
GET /api/v1/tasks/{id}
```
## Example
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer $REAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "audio-stem-separator",
"audio_url": "https://cdn.example.com/song.wav",
"stem": "vocals",
"encoder_format": "mp3"
}'
```
## Parameters
| Field | Type | Required | Notes |
| ------------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Use `audio-stem-separator`. |
| `audio_url` | URL | yes | Public HTTP(S) source audio URL. |
| `stem` | enum | yes | Use `vocals` for this API page. |
| `splitter` | enum | no | `auto`, `andromeda`, `perseus`, `orion`, `phoenix`, `lyra`, or `lynx`. `auto` is omitted upstream so the default engine is used. |
| `extraction_level` | enum | no | `deep_extraction` or `clear_cut`. Defaults to `deep_extraction`. |
| `multivocal` | string | no | `lead_back`. Only valid with `stem: "vocals"`. |
| `dereverb_enabled` | boolean | no | Enable dereverb cleanup. |
| `encoder_format` | enum | no | `mp3`, `wav`, `flac`, `aac`, or `ogg`. |
## Output
Completed tasks return `output.audio_urls`. When labels are available,
`output.tracks` includes `type`, `label`, and `url` for each track.
Audio jobs are billed per rounded-up source minute. See current rate on
the [Vocal Remover model page](https://reapi.ai/models/vocal-remover).
---
# Voice Changer API (https://reapi.ai/docs/voice-changer)
# Voice Changer API
Use `audio-voice-change` to convert vocals with a configured voice pack.
## Endpoint
```http
POST /api/v1/audio/generations
```
Poll completion with:
```http
GET /api/v1/tasks/{id}
```
## Example
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer $REAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "audio-voice-change",
"audio_url": "https://cdn.example.com/source-vocal.wav",
"voice_pack_id": "voice_pack_id_here",
"accent": 1,
"encoder_format": "mp3"
}'
```
## Parameters
| Field | Type | Required | Notes |
| -------------------- | ------- | -------- | --------------------------------------------------------- |
| `model` | string | yes | Use `audio-voice-change`. |
| `audio_url` | URL | yes | Public HTTP(S) source vocal URL. |
| `voice_pack_id` | string | yes | Target voice pack id. |
| `accent` | number | no | 0 to 1. Defaults to `1`. |
| `tonality_reference` | enum | no | `source_file` or `voice_pack`. Defaults to `source_file`. |
| `dereverb_enabled` | boolean | no | Enable dereverb cleanup. |
| `encoder_format` | enum | no | `mp3`, `wav`, `flac`, `aac`, or `ogg`. |
## Output
Completed tasks return `output.audio_urls` and, when available, labeled
`output.tracks`.
Audio jobs are billed per rounded-up source minute. See current rate on
the [Voice Changer model page](https://reapi.ai/models/voice-changer).
---
# Voice Cleaner API (https://reapi.ai/docs/voice-cleaner)
# Voice Cleaner API
Use `audio-voice-clean` for speech cleanup before publishing, transcription,
voiceover preparation, or downstream editing.
## Endpoint
```http
POST /api/v1/audio/generations
```
Poll completion with:
```http
GET /api/v1/tasks/{id}
```
## Example
```bash
curl https://reapi.ai/api/v1/audio/generations \
-H "Authorization: Bearer $REAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "audio-voice-clean",
"audio_url": "https://cdn.example.com/podcast.wav",
"noise_cancelling_level": 1,
"encoder_format": "mp3"
}'
```
## Parameters
| Field | Type | Required | Notes |
| ------------------------ | ------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `model` | string | yes | Use `audio-voice-clean`. |
| `audio_url` | URL | yes | Public HTTP(S) source audio URL. |
| `noise_cancelling_level` | integer | no | `0`, `1`, or `2`. Defaults to `0`. |
| `splitter` | enum | no | `auto`, `andromeda`, `perseus`, `orion`, `phoenix`, `lyra`, or `lynx`. `auto` is omitted upstream so the default engine is used. |
| `dereverb_enabled` | boolean | no | Enable dereverb cleanup. |
| `encoder_format` | enum | no | `mp3`, `wav`, `flac`, `aac`, or `ogg`. |
## Output
Completed tasks return `output.audio_urls` and, when available, labeled
`output.tracks`.
Audio jobs are billed per rounded-up source minute. See current rate on
the [Voice Cleaner model page](https://reapi.ai/models/voice-cleaner).
---
# wan-2-7-image (https://reapi.ai/docs/wan-2-7-image)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Alibaba's Wan image model on reAPI — one async endpoint for **generation and
> editing**. Text-to-image, **image editing**, **multi-image reference fusion**,
> **interactive region edits** (`bbox_list`), and **group series**. Two tiers:
> `wan2.7-image` (1K / 2K) and `wan2.7-image-pro` (text-to-image up to **4K**).
> Submit returns a `task_id`; poll until ready. See current pricing on the
> [model page](https://reapi.ai/models/wan-2-7-image).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "wan2.7-image-pro",
"prompt": "a flower shop with delicate windows and a beautiful wooden door, blossoms out front",
"size": "16:9",
"resolution": "2K",
"n": 1
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "wan2.7-image-pro",
"prompt": "a flower shop with delicate windows and a beautiful wooden door, blossoms out front",
"size": "16:9",
"resolution": "2K",
"n": 1,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "wan2.7-image-pro",
prompt: "a flower shop with delicate windows and a beautiful wooden door, blossoms out front",
size: "16:9",
resolution: "2K",
n: 1,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "wan2.7-image-pro",
"prompt": "a flower shop with delicate windows and a beautiful wooden door, blossoms out front",
"size": "16:9",
"resolution": "2K",
"n": 1,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "wan2.7-image-pro",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls` holds the
generated image URLs.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Tiers
Two model ids share one parameter shape:
| Model id | Tier | Resolution |
| ------------------ | -------- | -------------------------------------------------------- |
| `wan2.7-image` | Standard | 1K / 2K (all modes) |
| `wan2.7-image-pro` | Pro | text-to-image up to 4K; editing and group modes up to 2K |
Pick Pro for hero / print-grade detail and 4K; Standard for fast everyday work.
## Modes
There is no `mode` field — the mode is implicit in the request shape:
* **Text-to-image** — no `image_urls`. `prompt` is required.
* **Editing / multi-image reference** — pass `image_urls`. `prompt` is optional
(recommended). The output aspect ratio follows the **last** input image.
* **Interactive region editing** — add `bbox_list` to target exact regions.
* **Group series** — set `enable_sequential: true` for a consistent connected
set; raises the `n` cap to 12.
***
## Request body
### `model` — string, required
One of `wan2.7-image` or `wan2.7-image-pro`.
### `prompt` — string
Up to **5,000 characters**. Required for text-to-image (no `image_urls`);
optional but recommended in editing mode.
### `image_urls` — array, optional
Reference images for editing / multi-image fusion. **Public HTTPS URLs only** —
base64 / `data:` URIs are rejected at the gateway. Up to **9** images. Passing
any image switches the request to editing mode.
### `n` — integer, default `1`
Images per call. Range `[1, 4]` normally; up to `[1, 12]` when
`enable_sequential` is `true`. Billed per generated image.
### `size` — string
Output sizing. Polymorphic per the model:
* a gear keyword — `1K` / `2K` / `4K`
* an aspect ratio — `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`
* a pixel value — `1024x1024`
Pair an aspect ratio here with the quality gear in `resolution`.
### `resolution` — string
Quality gear: `1K` / `2K` / `4K` (case-insensitive).
**4K is `wan2.7-image-pro` text-to-image only.** In editing and group modes,
and on the `wan2.7-image` (standard) tier, the maximum is 2K.
### `negative_prompt` — string, optional
What to keep out of the image, e.g. `"blurry, distorted, low quality"`.
### `watermark` — boolean, default `false`
Set `true` to add an AI-generated watermark to the bottom-right corner.
### `seed` — integer, optional
Range `[0, 2147483647]`. The same seed with the same parameters yields a
similar, stable result.
### `thinking_mode` — boolean, default `true`
Enables extra reasoning for higher image quality. Only effective for
text-to-image with no reference image and outside group mode; ignored otherwise.
### `enable_sequential` — boolean, default `false`
Turns on group mode — a consistent, connected series in one call. Raises the
`n` cap to 12. In group mode `thinking_mode` and `color_palette` do not apply.
### `bbox_list` — array, optional
Interactive-edit regions. A list parallel to `image_urls` — one entry per input
image — where each entry is a list of up to two boxes, and each box is
`[x1, y1, x2, y2]` in absolute pixels with origin at the top-left. Use `[]` for
an image you are not boxing.
```json
{
"image_urls": [
"https://example.com/clock.webp",
"https://example.com/desk.webp"
],
"bbox_list": [
[],
[[989, 515, 1138, 681]]
]
}
```
### `color_palette` — array, optional
Steer the image toward a custom palette. 3-10 items of `{ hex, ratio }` where
`ratio` is a percentage string and all ratios sum to `100.00%`. Non-group mode
only.
```json
{
"color_palette": [
{ "hex": "#C2D1E6", "ratio": "23.51%" },
{ "hex": "#636574", "ratio": "76.49%" }
]
}
```
***
## Pricing
Wan 2.7 Image bills a flat rate **per successfully generated image**,
independent of resolution and aspect ratio. Total charge is the per-image rate
times `n`:
```
credits = ceil(per_image_usd × n × 1000)
```
where `1 credit = $0.001 USD`. The Pro tier carries a higher per-image rate than
Standard. Failed and rejected requests are not charged.
The exact per-image credit cost for each tier surfaces on the
[model page](https://reapi.ai/models/wan-2-7-image) and through the playground
estimator before submit.
***
## Response
The poll envelope returns image URLs in `output.image_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "wan2.7-image-pro",
"status": "completed",
"output": {
"image_urls": [
"https://cdn.reapi.ai/...png"
]
}
}
```
For a group-mode request (`enable_sequential: true`, `n > 1`), `image_urls`
holds the full series in order. Mirror the URLs to your own storage if you need
long-term retention.
***
## Errors
Failures return the standard reAPI envelope `{ error: { code, message,
request_id } }`. Common cases:
* Invalid input (out-of-range `n`, 4K requested in edit/group mode, a `bbox_list`
whose length does not match `image_urls`, a non-HTTPS media URL) → `400`.
* Insufficient credits → `402`.
* Rate limited → `429`.
See the full catalog at [/docs/api/errors](/docs/api/errors).
***
## Tips
* The mode is decided by your inputs, not a flag — omit `image_urls` for pure
generation, include it to edit.
* For precise edits, prefer `bbox_list` over describing the location in the
prompt — boxed regions composite with matched lighting and perspective.
* For storyboards and comic panels, use group mode (`enable_sequential: true`)
so the series stays on-model in one call instead of stitching separate
requests.
* Reach for `wan2.7-image-pro` at `resolution: "4K"` (text-to-image) when the
asset goes to print or a retina display; use `wan2.7-image` for fast drafts.
***
## Related
* [Image generation models](/docs/gpt-image-2)
* [Seedream 5.0 Lite](/docs/seedream-5-0-lite)
* [Tasks API](/docs/api/tasks)
* [Error codes](/docs/api/errors)
---
# wan-2-7-video (https://reapi.ai/docs/wan-2-7-video)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Wan 2.7 Video is a single umbrella model (`wan2.7-video`) that auto-routes by
> request shape into **text-to-video**, **image-to-video**,
> **reference-to-video**, and **video editing**. One async endpoint, 720P or
> 1080P output, billed per second. See current pricing on the
> [model page](https://reapi.ai/models/wan-2-7-video).
## Quick example
```bash
curl https://reapi.ai/api/v1/videos/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "wan2.7-video",
"prompt": "a paper boat drifting down a rain-soaked street at dusk, neon reflections, slow dolly-in, cinematic",
"resolution": "1080P",
"duration": 5
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/videos/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "wan2.7-video",
"prompt": "a paper boat drifting down a rain-soaked street at dusk",
"resolution": "1080P",
"duration": 5,
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/videos/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "wan2.7-video",
prompt: "a paper boat drifting down a rain-soaked street at dusk",
resolution: "1080P",
duration: 5,
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "wan2.7-video",
"prompt": "a paper boat drifting down a rain-soaked street at dusk",
"resolution": "1080P",
"duration": 5,
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/videos/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "wan2.7-video",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.video_urls` holds the
generated MP4 URLs.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/videos/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
***
## Modes
There is no `mode` field. The umbrella model `wan2.7-video` infers the mode from
**which inputs you send**:
| Mode | Trigger | Notes |
| ------------------ | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- |
| Text-to-video | `prompt` only | A prompt alone renders a clip. |
| Image-to-video | `image_urls` / `image_with_roles` / `video_urls` (and `audio_url`) | Animate a still, set first/last frame, continue a clip, or drive motion with audio. |
| Reference-to-video | `reference_image_urls` and/or `reference_video_urls` | Keeps your subjects while following a referenced performance; `reference_voice_urls` add per-subject audio. |
| Video editing | `video_url` (the source clip) | Re-renders the source against the prompt; `audio_setting` controls the soundtrack. |
The fields valid in each mode are mutually exclusive — sending, e.g., a
reference field together with a `video_url` edit source is rejected as invalid.
***
## Parameters
| Field | Type | Required | Default | Notes |
| ---------------------- | --------- | -------- | ------- | ------------------------------------------------------------------------------- |
| `model` | string | yes | — | Must be `"wan2.7-video"`. |
| `prompt` | string | cond. | — | Up to 5000 chars. Required for text-to-video and reference-to-video. |
| `negative_prompt` | string | no | — | Up to 500 chars. What to avoid. |
| `image_urls` | string\[] | no | — | Public image URLs. 1 animates a still; 2 set first/last frame. Max 4. |
| `image_with_roles` | object\[] | no | — | `[{ url, role }]` where role is `first_frame` or `last_frame`. Max 2. |
| `video_urls` | string\[] | no | — | Continuation source. Public video URL. Max 1. |
| `audio_url` | string | no | — | Public audio URL. Drives motion with an image input. |
| `reference_image_urls` | string\[] | no | — | Reference subjects. Public image URLs. Up to 5. |
| `reference_video_urls` | string\[] | no | — | Reference motion/action. Public video URLs. Up to 5. |
| `reference_voice_urls` | string\[] | no | — | Per-subject voices. Public audio URLs. Count must equal `reference_image_urls`. |
| `video_url` | string | no | — | Edit source. Public video URL. Routes to video editing. |
| `audio_setting` | enum | no | — | Video editing only. `auto` or `origin`. |
| `size` | enum | no | `16:9` | Aspect ratio: `16:9`, `9:16`, `1:1`, `4:3`, `3:4`. |
| `resolution` | enum | no | `1080P` | `720P` or `1080P`. Scales the per-second rate. |
| `duration` | integer | no | `5` | Seconds. Base modes 2–15; reference/edit modes cap lower (see below). |
| `prompt_extend` | boolean | no | `true` | Expand a short prompt into a richer description before rendering. |
| `watermark` | boolean | no | `false` | Add a watermark to the output. |
| `seed` | integer | no | — | `>= 0`. Fixes the random seed for reproducible output. |
**No `data:` URIs.** reApi rejects base64 inputs platform-wide. Upload media to
public storage (your own CDN, S3, R2…) and pass the URL.
### Duration limits by mode
| Mode | Allowed `duration` |
| -------------------------------- | -------------------------------- |
| Text-to-video / image-to-video | 2 – 15 seconds |
| Reference-to-video (images only) | 2 – 15 seconds |
| Reference-to-video (with videos) | 2 – 10 seconds |
| Video editing | 0 (full source length) or 2 – 10 |
***
## Response envelope
Submit and poll share the same shape — only `status` and `output` fill in over
time.
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "wan2.7-video",
"status": "completed",
"created_at": 1735000000,
"output": {
"video_urls": ["https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.mp4"]
},
"error": null
}
```
| Field | Type | Notes |
| ------------ | -------------- | ------------------------------------------------------- |
| `id` | string | Task identifier — keep it for polling and audit |
| `model` | string | Echo of the submitted `model` |
| `status` | string | `processing` / `completed` / `failed` |
| `created_at` | integer | Submission unix timestamp |
| `output` | object \| null | `null` until completion. `output.video_urls` holds MP4s |
| `error` | object \| null | Populated on `failed` — `{ code, message }` |
***
## Pricing
Per-second × `resolution`. The mode does **not** change the rate — only the
resolution does. `1080P` costs more per second than `720P`.
For the input-billed modes (reference-to-video and video editing) reApi probes
the uploaded clip **server-side** and folds input + output into the billable
second count, rather than trusting a client value. Base text/image modes bill
the requested output `duration`.
**Bill formula** (1 credit = $0.001):
```
billable_seconds = ceil(server_probed_or_output_seconds)
bill_usd = per_second_usd(resolution) × billable_seconds
credits = ceil(per_second_usd × billable_seconds × 1000)
```
See current per-second rates for `720P` and `1080P` on the
[model page](https://reapi.ai/models/wan-2-7-video). Failed jobs refund
automatically. A probe failure on an input-billed mode returns
`400 PRICING_UNAVAILABLE` (code `30002`) with no charge.
The playground's draft estimate may show a default before an uploaded clip is
probed — the authoritative number is computed at submit, after the server
measures the file.
***
## Validation errors
All cases below return HTTP 400. Pattern-match on `code`, not `message` —
message strings carry request-specific context and are not a stable contract.
| Trigger | Code | Message (illustrative) |
| ------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------- |
| `prompt` missing for text-to-video / reference-to-video | `20002` | `wan-2-7-video: prompt is required` |
| Field invalid for the inferred mode | `20003` | `wan-2-7-video: video_urls is not valid in video editing` |
| `duration` outside the mode's allowed range | `20003` | `wan-2-7-video: duration must be 2-15 seconds` |
| `reference_voice_urls` count != `reference_image_urls` | `20003` | `wan-2-7-video: reference_voice_urls length must equal reference_image_urls length` |
| A media field carrying a `data:` URI | `20003` | `wan-2-7-video: must be a public http(s) URL` |
| Source clip probe fails (network / format) | `30002` | `Could not determine source video duration for billing: …` |
| Provider rejected the request as invalid | `80007` | Provider rejected the request as invalid |
The full envelope is `{ "error": { "code", "message", "request_id" } }` — see
[Errors catalog](/docs/api/errors) for the wire format and request\_id
correlation tips.
***
## Recipes
### Text-to-video
```json
{
"model": "wan2.7-video",
"prompt": "a paper boat drifting down a rain-soaked street at dusk",
"resolution": "1080P",
"duration": 5
}
```
### Image-to-video (animate a still)
```json
{
"model": "wan2.7-video",
"prompt": "the subject turns to camera and smiles softly",
"image_urls": ["https://your-cdn.com/portrait.png"],
"resolution": "1080P",
"duration": 5
}
```
### Reference-to-video
```json
{
"model": "wan2.7-video",
"prompt": "apply the reference choreography to the new character",
"reference_image_urls": ["https://your-cdn.com/subject.png"],
"reference_video_urls": ["https://your-cdn.com/dance-6s.mp4"],
"resolution": "1080P",
"duration": 6
}
```
### Video editing
```json
{
"model": "wan2.7-video",
"prompt": "restyle the clip with warm cinematic color grading",
"video_url": "https://your-cdn.com/source-8s.mp4",
"audio_setting": "origin",
"duration": 0
}
```
***
## Tips
* **Let the inputs pick the mode.** There is no `mode` flag — send a prompt for
text-to-video, add `image_urls` for image-to-video, `reference_*` for
reference-to-video, or `video_url` to edit. Don't mix fields across modes.
* **Match `duration` to the mode.** Base modes allow 2–15s; reference-with-video
and editing cap at 10s, and editing accepts `0` for the full source length.
* **Resolution drives cost.** `720P` is the cheaper run; switch to `1080P` for
the final take. The mode does not change the per-second rate.
* **Keep `reference_voice_urls` aligned.** Their count must equal the number of
`reference_image_urls`, one voice per subject.
* **Prompt the motion, not just the scene.** Describing the action ("turns and
waves, smooth and continuous") sharpens text-to-video and image-to-video
results.
***
## Related
* [Errors catalog](/docs/api/errors)
* [Authentication](/docs/api/authentication)
* [Quickstart](/docs/api/quickstart)
---
# z-image (https://reapi.ai/docs/z-image)
import { Callout } from 'fumadocs-ui/components/callout';
import { Tab, Tabs } from 'fumadocs-ui/components/tabs';
> Alibaba's Tongyi Z-Image-Turbo on reAPI — fast, low-cost **text-to-image**
> with accurate **bilingual (English + Chinese) text rendering** and
> photorealistic output. Submit returns a `task_id`; poll until ready. See
> current pricing on the [model page](https://reapi.ai/models/z-image).
## Quick example
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"prompt": "a poster that reads OPENING SOON in bold type, warm tones",
"aspect_ratio": "3:4"
}'
```
```python
import requests
resp = requests.post(
"https://reapi.ai/api/v1/images/generations",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json={
"model": "z-image",
"prompt": "a poster that reads OPENING SOON in bold type, warm tones",
"aspect_ratio": "3:4",
},
timeout=30,
)
print(resp.json())
```
```js
const r = await fetch("https://reapi.ai/api/v1/images/generations", {
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "z-image",
prompt: "a poster that reads OPENING SOON in bold type, warm tones",
aspect_ratio: "3:4",
}),
});
console.log(await r.json());
```
```go
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)
func main() {
body, _ := json.Marshal(map[string]any{
"model": "z-image",
"prompt": "a poster that reads OPENING SOON in bold type, warm tones",
"aspect_ratio": "3:4",
})
req, _ := http.NewRequest("POST",
"https://reapi.ai/api/v1/images/generations", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
out, _ := io.ReadAll(resp.Body)
fmt.Println(string(out))
}
```
### Submit response
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "z-image",
"status": "processing",
"created_at": 1735000000
}
```
Poll `GET /api/v1/tasks/{id}` (see the [Tasks](/docs/api/tasks) reference) until
`status === "completed"`. The completed payload's `output.image_urls` holds the
generated image URL.
***
## Authentication
Every call needs a Bearer token. Generate keys at
[reapi.ai/settings/apikeys](https://reapi.ai/settings/apikeys).
```http
Authorization: Bearer YOUR_API_KEY
```
***
## Endpoint
```http
POST /api/v1/images/generations
GET /api/v1/tasks/{id}
```
Submission is async. The POST returns immediately with a `task_id`; the task
endpoint returns the same envelope until completion. Polling does not consume
credits.
Z-Image is **text-to-image only** — there is no image input. Each request
produces **exactly one image** (no `n` parameter).
***
## Request body
### `model` — string, required
Must be `z-image`.
### `prompt` — string, required
Up to **1,000 characters**. Put any text you want rendered in the image inside
quotes for best results.
### `aspect_ratio` — string, default `1:1`
One of `1:1`, `4:3`, `3:4`, `16:9`, `9:16`. There is no width/height or
resolution control.
### `nsfw_checker` — boolean, optional, default `false`
Enables upstream content moderation. Off by default.
***
## Pricing
Z-Image bills a flat rate **per generated image**, independent of aspect ratio.
Each request produces one image:
```
credits = ceil(per_image_usd × 1000)
```
where `1 credit = $0.001 USD`. It is one of the lowest per-image rates on the
platform. Failed and rejected requests are not charged.
The exact per-image credit cost surfaces on the
[model page](https://reapi.ai/models/z-image) and through the playground
estimator before submit.
***
## Response
The poll envelope returns the image URL in `output.image_urls`:
```json
{
"id": "task_019dfd44b7fd74168541552a3260a623",
"model": "z-image",
"status": "completed",
"output": {
"image_urls": [
"https://cdn.reapi.ai/...jpg"
]
}
}
```
Generated URLs expire — mirror them to your own storage if you need long-term
retention.
***
## Errors
Failures return the standard reAPI envelope `{ error: { code, message,
request_id } }`. Common cases:
* Invalid input (prompt over 1000 characters, an unsupported `aspect_ratio`) →
`400`.
* Insufficient credits → `402`.
* Rate limited → `429`.
See the full catalog at [/docs/api/errors](/docs/api/errors).
***
## Tips
* Put the literal text you want rendered **in the prompt, in quotes** — Z-Image
is tuned for legible English and Chinese typography.
* It is text-to-image only — to edit an existing image, use a model that accepts
an `image_url`.
* For volume work (social graphics, e-commerce), Z-Image's flat low price makes
it well-suited to batching many single-image requests.
***
## Related
* [Image generation models](/docs/gpt-image-2)
* [Qwen Image 2](/docs/qwen-image-2)
* [Tasks API](/docs/api/tasks)
* [Error codes](/docs/api/errors)
---
# Authentication (https://reapi.ai/docs/api/authentication)
## Bearer token
Send your API key as a Bearer token on every request:
```http
Authorization: Bearer rk_live_xxxxxxxxxxxxxxxx
```
This matches the OpenAI scheme — most OpenAI clients work by changing
`base_url` and `api_key`.
## Create a key
1. Sign in at [reapi.ai](https://reapi.ai).
2. Go to **Dashboard → API Keys**.
3. Click **Create new key**, give it a name (e.g. `production-server`).
4. Copy the key. **It is shown only once** — store it in a secret manager.
You can create as many keys as you like (e.g. one per environment, one per
service).
## Revoke a key
Revoke compromised keys immediately on the dashboard. Pending tasks created
with the key continue to completion (and bill credits), but no new requests
can be made.
## Key prefixes
| Prefix | Environment |
| ------------- | -------------------------------------------------------- |
| `rk_live_...` | Production |
| `rk_test_...` | Test (no credit deduction, mock responses) — coming soon |
## Storing keys
**Don't** commit keys to git or paste them in chat. Use a secret manager:
* **Local dev**: `.env.local` (in `.gitignore`)
* **Vercel**: Environment Variables
* **AWS / GCP**: Secrets Manager / Secret Manager
* **Docker**: Docker secrets or `--env-file`
## Errors
| HTTP | `code` | Cause | Fix |
| ---- | ------- | ------------------------------------------- | ---------------------------------------------------------------------------------------- |
| 401 | `10001` | No `Authorization` header | Add the header |
| 401 | `10002` | Header isn't `Bearer ` | Use `Bearer ` |
| 401 | `10003` | Key invalid / typo | Check the dashboard |
| 401 | `10004` | Key has been revoked | Mint a new key |
| 401 | `10005` | Sign-in required (session-cookie path only) | Use API-key auth, or sign in |
| 403 | `10006` | Origin not in allow-list (browser-only) | Only relevant for session-cookie auth from the browser; API-key requests aren't affected |
| 402 | `30001` | Balance below request cost | Buy credits or wait for monthly refresh |
Full list: [Errors](/docs/api/errors).
---
# Balance (https://reapi.ai/docs/api/balance)
`GET /api/v1/balance` returns the current credit balance for the credentials
making the request. Use it to show remaining credits in your own dashboard, or
to check a key has funds before submitting work.
Reading this endpoint **does not consume credits**.
## GET /api/v1/balance
### Request
```http
GET /api/v1/balance
Authorization: Bearer rk_live_xxx
```
No body and no query parameters.
### Response
`200 OK`:
```json
{
"balance": 12500
}
```
### Response fields
| Field | Type | Description |
| --------- | ------- | ------------------------------------------------------------- |
| `balance` | integer | Current credit balance. Integer credits, `1 credit = $0.001`. |
### Errors
| HTTP | `code` | When |
| ---- | ----------------- | ---------------------------------------------------------------- |
| 401 | `10001` – `10005` | Auth missing / malformed / invalid / revoked |
| 429 | `50001` | Per-user rate limit exceeded — retry after `Retry-After` seconds |
| 500 | `60099` | Unexpected server error |
Full list: [Errors](/docs/api/errors).
---
# Errors (https://reapi.ai/docs/api/errors)
## Error format
Every error response carries a JSON body in this shape:
```json
{
"error": {
"code": 20002,
"message": "Missing required parameter",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
| Field | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------- |
| `code` | 5-digit numeric code. The leading digit is the category (1xxxx auth, 2xxxx validation, …). |
| `message` | Human-readable explanation. May embed request-specific context (parameter names, balance, etc.). |
| `request_id` | Unique per request. **Optional** — present whenever the gateway has assigned one; include in support tickets. |
The HTTP status code reflects the *category* (401 / 400 / 402 / 429 / 500 / 502 / 503 / 504); the `code` field gives you the precise reason.
For `failed` tasks, the same envelope is returned inside the polling response under `error` — see the model docs' Response section.
## Code catalog
### 1xxxx — Authentication (HTTP 401 / 403)
| `code` | HTTP | Cause |
| ------- | ---- | -------------------------------------------- |
| `10001` | 401 | Missing `Authorization` header |
| `10002` | 401 | `Authorization` header is not `Bearer ` |
| `10003` | 401 | API key invalid |
| `10004` | 401 | API key has been revoked |
| `10005` | 401 | Sign-in required (session-auth surfaces) |
| `10006` | 403 | Request origin not allowed |
### 2xxxx — Validation (HTTP 400)
| `code` | Cause |
| ------- | ------------------------------------------------- |
| `20001` | Request body is not a JSON object |
| `20002` | Required parameter missing — `message` says which |
| `20003` | Parameter value invalid (type / range / enum) |
| `20004` | `model` not supported on this endpoint |
| `20005` | `size` not supported by this model |
| `20006` | `quality` not supported by this model |
| `20007` | `prompt` exceeds maximum length |
### 3xxxx — Billing (HTTP 402 / 400)
| `code` | HTTP | Cause |
| ------- | ---- | --------------------------------------- |
| `30001` | 402 | Insufficient credits |
| `30002` | 400 | Cannot determine pricing for this model |
### 4xxxx — Resource (HTTP 404)
| `code` | Cause |
| ------- | ------------------------------------------ |
| `40001` | Task not found, or belongs to another user |
### 5xxxx — Rate limit (HTTP 429)
| `code` | Cause |
| ------- | --------------------------------------- |
| `50001` | Rate limit exceeded — see `Retry-After` |
### 6xxxx — Server internal (HTTP 500)
| `code` | Cause |
| ------- | ------------------------------------------- |
| `60001` | Failed to persist task — credits refunded |
| `60002` | Failed to start workflow — credits refunded |
| `60099` | Generic internal error |
### 7xxxx — Capacity (HTTP 503)
| `code` | Cause |
| ------- | ------------------------------------ |
| `70001` | Workflow service unavailable — retry |
| `70002` | Database unavailable |
### 8xxxx — Workflow execution (returned inside the polling response body when `status="failed"`)
> **HTTP status for `GET /api/v1/tasks/{id}` is always `200` when the task is
> found.** The codes below appear only inside `error.code` of the response
> body — they are *never* the HTTP response status.
| `code` | Cause |
| ------- | ---------------------------------------------------- |
| `80001` | Upstream submission failed (5xx / network on submit) |
| `80002` | Polling timeout (wall-clock cap reached) |
| `80003` | Upstream returned a terminal failure |
| `80004` | Upstream completed but returned no URLs |
| `80005` | Failed to persist generated files |
| `80006` | Content policy violation |
| `80007` | Upstream rejected the input as invalid |
| `80008` | Task canceled |
The 8xxxx codes are written to the task by the worker; polling returns them under `error.code` once `status="failed"`.
## Recommended handling
```python
import requests, time
def safe_call(method, url, **kwargs):
for attempt in range(3):
r = requests.request(method, url, **kwargs)
body = r.json()
if r.status_code == 200:
return body
if r.status_code == 429:
time.sleep(int(r.headers.get('Retry-After', '5')))
continue
if r.status_code in (502, 503, 504):
time.sleep(2 ** attempt)
continue
raise RuntimeError(
f"{body['error']['code']}: {body['error']['message']}"
)
raise RuntimeError("max retries exceeded")
```
### What to retry vs not
| HTTP | Retry? |
| ----------------------- | ------------------------------ |
| 200 | n/a |
| 400 / 401 / 402 / 404 | ❌ Fix the request |
| 429 | ✅ With `Retry-After` |
| 500 — `60001` / `60002` | ⚠️ Credits refunded, re-submit |
| 500 — `60099` | ✅ Idempotent retry safe |
| 502 / 503 / 504 | ✅ Exponential backoff |
## Support
When reporting an issue, include:
* The `request_id` from the response
* The full request URL + method
* Approximate timestamp (UTC)
Email: [support@reapi.ai](mailto:support@reapi.ai)
---
# Overview (https://reapi.ai/docs/api)
## Base URL
```
https://reapi.ai/api/v1
```
All endpoints accept and return JSON. The API mirrors OpenAI's conventions
where it makes sense — many existing OpenAI clients work by changing only
`base_url`.
## Authentication
Every request must carry an API key as a Bearer token:
```http
Authorization: Bearer rk_live_xxxxxxxxxxxx
```
Create keys in your [dashboard](https://reapi.ai/dashboard/api-keys). See
[Authentication](/docs/api/authentication) for full details.
## Asynchronous tasks (image, video)
Generation calls are **asynchronous**. The pattern is:
1. **POST** the model endpoint (e.g. `/api/v1/images/generations`)
→ returns a `task_id` immediately.
2. **GET** `/api/v1/tasks/{task_id}` periodically until `status` is
`completed` or `failed`.
```
┌──────────┐ ┌──────────┐ ┌──────────┐
│ POST │ → │processing│ → │ completed│
│ /images/ │ └──────────┘ │ failed │
│generations│ └──────────┘
└──────────┘ │
▼
output / error
```
Recommended polling cadence: **1 request every 1–2 seconds**.
For details see [Tasks](/docs/api/tasks). Per-model request schemas are
on each model's page in the **Models** sidebar.
## Credits
Each call deducts credits from your balance. Failed tasks are
**automatically refunded** by the worker the moment the workflow ends in
failure — atomically, before any poll observes the `failed` status. Refund
is one-shot; re-polling never refunds twice.
Pricing per model is configured in your account dashboard.
## Idempotency
reAPI does **not** deduplicate by `Idempotency-Key`. Every successful HTTP
POST to a generation endpoint creates a new task and charges credits — by
design, so no upstream provider call is ever silently skipped.
If you need retry safety, generate the same payload only once on your side
or rely on the natural recovery path: when a task fails, credits are
refunded automatically and you can resubmit.
The `Idempotency-Key` header is currently accepted and recorded for
telemetry but does **not** alter request behavior. Don't rely on it to
prevent duplicate charges.
## Rate limits
Requests are limited **per user**, in 1-second windows. The default is
**10 requests / second / user**. When exceeded, you get HTTP `429` with code
`50001` and a `Retry-After` header.
Polling `GET /api/v1/tasks/{id}` shares the same limit, so don't poll faster
than once every 1–2 seconds per task.
## Response format
Successful responses are JSON of the resource type. Errors follow a
standard shape:
```json
{
"error": {
"code": 20002,
"message": "Both `model` and `prompt` are required",
"request_id": "req_8a4f0d2e-1c8b-4f1a-9e2d-3b7c5a6f0a1b"
}
}
```
`code` is a 5-digit numeric identifier (see [Errors](/docs/api/errors)
for the full catalog). `request_id` is unique per call — include it in
support requests.
---
# Quickstart (https://reapi.ai/docs/api/quickstart)
## 1. Get an API key
Sign up at [reapi.ai](https://reapi.ai), then go to
**Dashboard → API Keys → Create new key**. Copy it now — you won't see it
again.
```
rk_live_xxxxxxxxxxxxxxxxxxxxxxx
```
## 2. Submit a generation task
```bash
curl https://reapi.ai/api/v1/images/generations \
-H "Authorization: Bearer rk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "a cute red panda eating bamboo, photorealistic",
"size": "1:1"
}'
```
Response (immediately):
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2",
"status": "processing",
"created_at": 1735000000
}
```
## 3. Poll until done
```bash
curl https://reapi.ai/api/v1/tasks/task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e \
-H "Authorization: Bearer rk_live_xxx"
```
Repeat every 1–2 seconds until `status` is `completed`:
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2",
"status": "completed",
"created_at": 1735000000,
"output": {
"image_urls": [
"https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png"
]
},
"error": null
}
```
That's it. Your image is at `output.image_urls[0]`.
## Full example (Node.js)
```js
const KEY = process.env.REAPI_KEY;
async function generate(prompt) {
const submit = await fetch('https://reapi.ai/api/v1/images/generations', {
method: 'POST',
headers: {
'Authorization': `Bearer ${KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-image-2',
prompt,
}),
}).then(r => r.json());
const taskId = submit.id;
while (true) {
await new Promise(r => setTimeout(r, 1500));
const task = await fetch(`https://reapi.ai/api/v1/tasks/${taskId}`, {
headers: { 'Authorization': `Bearer ${KEY}` },
}).then(r => r.json());
if (task.status === 'completed') return task.output.image_urls;
if (task.status === 'failed') throw new Error(task.error?.message ?? 'failed');
}
}
console.log(await generate('a cute red panda'));
```
## Python
```python
import os, time, requests
KEY = os.environ['REAPI_KEY']
HEAD = {'Authorization': f'Bearer {KEY}'}
submit = requests.post(
'https://reapi.ai/api/v1/images/generations',
headers=HEAD,
json={
'model': 'gpt-image-2',
'prompt': 'a cute red panda',
},
).json()
task_id = submit['id']
while True:
time.sleep(1.5)
task = requests.get(f'https://reapi.ai/api/v1/tasks/{task_id}', headers=HEAD).json()
if task['status'] == 'completed':
print(task['output']['image_urls'])
break
if task['status'] == 'failed':
raise RuntimeError(task['error'])
```
## Next steps
* [API overview](/docs/api) — full conventions
* [Tasks](/docs/api/tasks) — `GET /api/v1/tasks/{id}` polling reference
* [gpt-image-2 model card](/docs/gpt-image-2) — capabilities + pricing
* [Errors](/docs/api/errors) — error code reference
---
# Tasks (https://reapi.ai/docs/api/tasks)
Generation on reAPI is **asynchronous**. Every submission endpoint —
`/api/v1/images/generations`, `/api/v1/videos/generations`, and any future
`/api/v1/audio/generations` — returns a task id, and you poll
`GET /api/v1/tasks/{id}` until the task reaches a terminal state.
This page is the canonical reference for the polling endpoint. The
request shape, status values, error codes, polling cadence, and refund
semantics are the same regardless of which submission endpoint produced
the task; only the contents of `output` differ by media kind.
## GET /api/v1/tasks/\{id}
Retrieve the current state of a task.
### Request
```http
GET /api/v1/tasks/{id}
Authorization: Bearer rk_live_xxx
```
`{id}` is the `id` returned by the submitting POST. Polling **does not
consume credits**.
### Response
`200 OK` — image task example:
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"model": "gpt-image-2",
"status": "completed",
"created_at": 1735000000,
"output": {
"image_urls": [
"https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/0.png",
"https://cdn.reapi.ai/media/tasks/018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e/1.png"
]
},
"error": null
}
```
`200 OK` — video task example:
```json
{
"id": "task_01k9s419324drezfbwnsvxyr6h",
"model": "doubao-seedance-2.0",
"status": "completed",
"created_at": 1762853430,
"output": {
"video_urls": [
"https://cdn.reapi.ai/media/tasks/01k9s419324drezfbwnsvxyr6h/0.mp4"
]
},
"error": null
}
```
### Status values
| Status | Meaning |
| ------------ | ---------------------------------------------------------------- |
| `processing` | Submitted, provider is generating |
| `completed` | Done — `output` populated |
| `failed` | Provider returned an error — `error` populated, credits refunded |
### Output shape
The shape of `output` depends on the media kind of the task:
| Media kind | Field | Type |
| ---------- | ------------ | --------- |
| Image | `image_urls` | string\[] |
| Video | `video_urls` | string\[] |
| Audio | `audio_urls` | string\[] |
Some video models add an extra `last_frame_url` (string) when the request
opted into a return-last-frame capability.
URLs returned in `output` are rehosted to reAPI's CDN. If you need
long-term archival, copy them to your own storage as soon as the task
completes — bucket lifecycle policies are configured per-deployment and
not guaranteed by this contract.
### Errors
| HTTP | `code` | When |
| ---- | ----------------- | ------------------------------------------------------------------------ |
| 401 | `10001` – `10005` | Auth missing / invalid / revoked |
| 404 | `40001` | Task doesn't exist or belongs to another user |
| 429 | `50001` | Per-user request rate limit exceeded — retry after `Retry-After` seconds |
## Polling pattern
Recommended:
```
attempt 1: wait 2s
attempt 2: wait 2s
...
the worker keeps a task alive up to a wall-clock cap before it self-reports
an 80002 timeout: 1 hour for image and audio, 48 hours for video. Most tasks
finish far sooner (image/audio usually under a minute, video in minutes), so
size your client-side give-up to your own tolerance, not the cap.
```
The polling endpoint is cached for **5 seconds** while a task is
in-flight, so polling faster than every 2–3 seconds returns the same
state and just consumes rate-limit budget. Video tasks routinely take
several minutes; pace your polling accordingly.
## Refund behavior
Failed tasks are **automatically refunded** by the worker the moment the
workflow ends in failure — the credit return happens atomically with the
status flip, before any poll observes it. Refund is one-shot: re-polling
a `failed` task never refunds twice.
A failed task surfaces the failure code under `error`:
```json
{
"id": "task_018f5a3a1b6e7d9f8c2b4d6e8f0a2c4e",
"status": "failed",
"output": null,
"error": {
"code": 80006,
"message": "Request violates content policy"
}
}
```
The `code` here is one of the 8xxxx workflow-execution codes — see
[Errors → 8xxxx](/docs/api/errors#8xxxx--workflow-execution-returned-in-polling-response-when-statusfailed).