Quick Start
Generate your first AI image in under a minute.
1. Get Your API Key
Get your API key from the API Keys page. You'll need it for all API requests.
2. Generate an Image
Copy this command, replace
YOUR_API_TOKEN, and run it:
curl -X POST "https://runapi.ai/api/v1/nano_banana/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model": "nano-banana", "prompt": "A cute cat wearing a space helmet"}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/nano_banana/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = { model: 'nano-banana', prompt: 'A cute cat wearing a space helmet' }.to_json
response = http.request(request)
puts response.body
import requests
response = requests.post(
'https://runapi.ai/api/v1/nano_banana/text_to_image',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={'model': 'nano-banana', 'prompt': 'A cute cat wearing a space helmet'}
)
print(response.json())
fetch('https://runapi.ai/api/v1/nano_banana/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'nano-banana',
prompt: 'A cute cat wearing a space helmet'
})
})
.then(res => res.json())
.then(data => console.log(data));
Response:
{
"id": "281e5b0f...f39b9"
}
This creates an image generation task and returns a task ID.
3. Get Your Image
Use the task ID to check the result:
curl "https://runapi.ai/api/v1/nano_banana/text_to_image/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = 'YOUR_TASK_ID'
uri = URI("https://runapi.ai/api/v1/nano_banana/text_to_image/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
task_id = 'YOUR_TASK_ID'
response = requests.get(
f'https://runapi.ai/api/v1/nano_banana/text_to_image/{task_id}',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
const taskId = 'YOUR_TASK_ID';
fetch(`https://runapi.ai/api/v1/nano_banana/text_to_image/${taskId}`, {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
})
.then(res => res.json())
.then(data => console.log(data));
Response when completed:
{
"id": "281e5b0f...f39b9",
"status": "completed",
"model": "nano-banana",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png"
}
]
}
Poll this endpoint every 5 seconds until status is completed (usually 10-30 seconds). The images array contains your generated image objects.
Tips
Use Callbacks for Production: Instead of polling, provide a callback_url to receive automatic notifications when your task completes. This is more efficient and reliable. See Callbacks below.
Common First-Time Errors:
| Error | Cause | Solution |
|---|---|---|
| 401 Unauthorized | Invalid or missing key | Check your API key is correct and included in the header |
| 403 Forbidden | Key limit reached or permission denied | Check the key has access and has not hit its spending cap |
| 422 Unprocessable | Invalid parameters | Check required fields: model and prompt
|
| 402 Payment Required | Insufficient credits | Purchase credits or check your balance |
Overview
Authentication
runapi.ai uses API keys to authenticate requests. Include your key in the Authorization header:
Authorization: Bearer YOUR_API_TOKEN
Get your API key from the API Keys page.
Base URL
Most API requests (image, video, music generation) should be made to:
https://runapi.ai/api/v1
LLM endpoints use SDK-compatible paths for seamless client integration:
| SDK | Base URL | Endpoints |
|---|---|---|
| Anthropic SDK | https://runapi.ai |
/v1/messages, /v1/models
|
| OpenAI SDK | https://runapi.ai/v1 |
/chat/completions, /responses, /models
|
| Gemini (via OpenAI SDK) | https://runapi.ai/v1beta/openai |
/chat/completions, /models
|
Each SDK constructs the full URL differently — the Anthropic SDK appends /v1/messages to the base, while the OpenAI SDK appends /models directly. The base URLs above ensure all requests land on the correct endpoint without any code changes beyond the URL.
DeepSeek models (deepseek-v4-flash, deepseek-v4-pro) are available through both the Anthropic SDK (/v1/messages) and the OpenAI SDK (/chat/completions), and appear in each SDK's models.list(). Call GET /v1/models to see every model available to your key.
Pricing
Plan pricing and credit options are available at runapi.ai/pricing. For per-model credit usage, see the Credits section in the API area.
Callbacks
Callbacks let us notify your system when a task finishes. Provide a callback_url in your request, and we'll send a POST request to that URL with the final result.
1) Create a callback secret
In the account dashboard, open API Keys and generate a Callback Secret. Save the Base64 value; you'll use it to verify callback signatures.
2) Configure callback_url
Example: include
callback_urlwhen creating a task.
{
"model": "nano-banana",
"prompt": "A serene mountain landscape",
"callback_url": "https://your-app.com/callbacks/nano-banana"
}
3) Verify callback signatures
Each callback request includes these headers:
-
X-Callback-Id: Unique identifier for the callback message -
X-Callback-Timestamp: Unix timestamp (seconds) when the message was sent -
X-Callback-Signature: Base64-encoded signature
Signature algorithm:
- Signed content:
callback_id.callback_timestamp.raw_body - HMAC-SHA-256 with the Base64-decoded secret
- Base64-encode the resulting HMAC
Example: callback signature verification using the raw body.
const crypto = require('crypto');
function verifyCallback(headers, rawBody, secret) {
const callbackId = headers['x-callback-id'];
const callbackTimestamp = headers['x-callback-timestamp'];
const signatureHeader = headers['x-callback-signature'] || '';
const signedContent = `${callbackId}.${callbackTimestamp}.${rawBody}`;
const secretBytes = Buffer.from(secret, 'base64');
const computedSigBase64 = crypto
.createHmac('sha256', secretBytes)
.update(signedContent)
.digest('base64');
const signatures = signatureHeader
.split(',')
.map(s => s.trim())
.filter(Boolean);
return signatures.some(expectedSig => {
if (expectedSig.length !== computedSigBase64.length) {
return false;
}
const expectedBuffer = Buffer.from(expectedSig, 'base64');
const computedBuffer = Buffer.from(computedSigBase64, 'base64');
return crypto.timingSafeEqual(expectedBuffer, computedBuffer);
});
}
import base64
import hashlib
import hmac
def verify_callback(headers, raw_body, secret):
callback_id = headers.get('X-Callback-Id')
callback_timestamp = headers.get('X-Callback-Timestamp')
signature_header = headers.get('X-Callback-Signature', '')
signed_content = f"{callback_id}.{callback_timestamp}.{raw_body}"
secret_bytes = base64.b64decode(secret)
computed_sig_base64 = base64.b64encode(
hmac.new(secret_bytes, signed_content.encode('utf-8'), hashlib.sha256).digest()
).decode('utf-8')
signatures = [s.strip() for s in signature_header.split(',') if s.strip()]
return any(hmac.compare_digest(sig, computed_sig_base64) for sig in signatures)
require "base64"
require "openssl"
def verify_callback(headers, raw_body, secret)
callback_id = headers["x-callback-id"]
callback_timestamp = headers["x-callback-timestamp"]
signature_header = headers["x-callback-signature"].to_s
signed_content = "#{callback_id}.#{callback_timestamp}.#{raw_body}"
secret_bytes = Base64.decode64(secret)
computed_sig = Base64.strict_encode64(
OpenSSL::HMAC.digest("sha256", secret_bytes, signed_content)
)
signatures = signature_header.split(",").map(&:strip).reject(&:empty?)
signatures.any? { |sig| secure_compare(sig, computed_sig) }
end
def secure_compare(a, b)
return false unless a.bytesize == b.bytesize
l = a.unpack("C*")
res = 0
b.each_byte { |byte| res |= byte ^ l.shift }
res.zero?
end
Callback payload
The callback body matches the task status response for the same endpoint. Example:
Example: callback payload for a completed task.
{
"id": "281e5b0*********************f39b9",
"status": "completed",
"model": "nano-banana",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png"
}
]
}
Retry policy
If your endpoint returns a non-2xx response or times out, we'll retry up to 10 times with increasing delays. Respond with a 2xx status quickly and process the payload asynchronously.
Local webhook testing
Test callbacks on your local machine without a public URL. The runapi listen command opens a connection to RunAPI and forwards callback events to a local endpoint — similar to stripe listen.
Install the CLI
Install RunAPI CLI:
curl -fsSL https://runapi.ai/cli/install.sh | sh
runapi login
If you already have the CLI installed, make sure it's up to date.
Forward events to your local server
Start listening and forwarding:
runapi listen --forward-to http://localhost:3000/webhooks
Ready! Webhook signing secret: 4a8b2c...
Forwarding to http://localhost:3000/webhooks
[1] task=281e5b0...f39b9 status=completed → http://localhost:3000/webhooks 200
[2] task=93fa7e1...a82c4 status=completed → http://localhost:3000/webhooks 200
The CLI prints a webhook signing secret on startup. Use this secret in your local application to verify callback signatures. The verification code is identical to production — only the secret string differs.
Trigger a test event
Create a task without
callback_url:
curl -X POST https://runapi.ai/api/v1/nano_banana/text_to_image \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model": "nano-banana", "prompt": "A mountain landscape"}'
require "runapi"
client = RunApi::NanoBanana::Client.new(api_key: "YOUR_API_KEY")
client.text_to_images.create(
model: "nano-banana",
prompt: "A mountain landscape"
)
import requests
response = requests.post(
"https://runapi.ai/api/v1/nano_banana/text_to_image",
headers={"Authorization": "Bearer YOUR_API_KEY"},
json={"model": "nano-banana", "prompt": "A mountain landscape"},
)
print(response.json())
import RunApi from "@runapi.ai/sdk";
const client = new RunApi({ apiKey: "YOUR_API_KEY" });
await client.nanoBanana.textToImages.create({
model: "nano-banana",
prompt: "A mountain landscape",
});
Do not include a callback_url in the request. When the task completes, the event appears in your runapi listen terminal and is forwarded to your local server.
If your local endpoint returns a non-2xx response, the CLI leaves the event unacknowledged and retries it instead of marking the task delivered.
Verify signatures locally
The forwarded callback includes the same X-Callback-Id, X-Callback-Timestamp, and X-Callback-Signature headers as production callbacks. Use the listen signing secret (printed on CLI startup) instead of your production callback secret.
The signature algorithm is identical — see Verify callback signatures above.
Routing rules
| Scenario | Where callbacks go |
|---|---|
callback_url set |
Your public URL (production behavior, unchanged) |
No callback_url, runapi listen running |
Forwarded to your local server via CLI |
No callback_url, no listener running |
Callback is dropped (not queued for later) |
CLI reference
| Flag | Description |
|---|---|
--forward-to <url> |
Local URL to POST callbacks to. Without this flag, events are printed to the terminal but not forwarded. |
--api-key <key> |
API key. Overrides RUNAPI_API_KEY environment variable. |
--base-url <url> |
API base URL. Defaults to https://runapi.ai. |
SDKs, CLI, and Agent Skills
RunAPI publishes public SDKs, the runapi CLI, and portable agent skills under the runapi-ai GitHub organization.
Use these links when you want package-first integrations instead of raw HTTP requests.
SDK Index
Each model has one multi-language SDK repository with JavaScript, Ruby, and Go source. JavaScript packages are published under @runapi.ai/*; Go modules live under each <model>-sdk/go module; Ruby SDK source is public and RubyGems publishing is temporarily deferred.
ElevenLabs SDK
- API reference: ElevenLabs API
- SDK repository:
runapi-ai/elevenlabs-sdk - JavaScript:
npm install @runapi.ai/elevenlabs - Go:
go get github.com/runapi-ai/elevenlabs-sdk/goand importgithub.com/runapi-ai/elevenlabs-sdk/go/elevenlabs - Ruby:
runapi-elevenlabsgem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/elevenlabs
Flux Kontext SDK
- API reference: Flux Kontext API
- SDK repository:
runapi-ai/flux-kontext-sdk - JavaScript:
npm install @runapi.ai/flux-kontext - Go:
go get github.com/runapi-ai/flux-kontext-sdk/goand importgithub.com/runapi-ai/flux-kontext-sdk/go/fluxkontext - Ruby:
runapi-flux-kontextgem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/flux-kontext
Flux 2 SDK
- API reference: Flux 2 API
- SDK repository:
runapi-ai/flux2-sdk - JavaScript:
npm install @runapi.ai/flux2 - Go:
go get github.com/runapi-ai/flux2-sdk/goand importgithub.com/runapi-ai/flux2-sdk/go/flux2 - Ruby:
runapi-flux2gem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/flux2
GPT Image SDK
- API reference: GPT Image API
- SDK repository:
runapi-ai/gpt-image-sdk - JavaScript:
npm install @runapi.ai/gpt-image - Go:
go get github.com/runapi-ai/gpt-image-sdk/goand importgithub.com/runapi-ai/gpt-image-sdk/go/gptimage - Ruby:
runapi-gpt-imagegem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/gpt-image
GPT Image 2 SDK
- API reference: GPT Image 2 API
- SDK repository:
runapi-ai/gpt-image-2-sdk - JavaScript:
npm install @runapi.ai/gpt-image-2 - Go:
go get github.com/runapi-ai/gpt-image-2-sdk/goand importgithub.com/runapi-ai/gpt-image-2-sdk/go/gptimage2 - Ruby:
runapi-gpt-image-2gem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/gpt-image-2
GPT-4o Image SDK
- API reference: GPT-4o Image API
- SDK repository:
runapi-ai/gpt4o-image-sdk - JavaScript:
npm install @runapi.ai/gpt4o-image - Go:
go get github.com/runapi-ai/gpt4o-image-sdk/goand importgithub.com/runapi-ai/gpt4o-image-sdk/go/gpt4oimage - Ruby:
runapi-gpt4o-imagegem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/gpt4o-image
Grok Imagine SDK
- API reference: Grok Imagine API
- SDK repository:
runapi-ai/grok-imagine-sdk - JavaScript:
npm install @runapi.ai/grok-imagine - Go:
go get github.com/runapi-ai/grok-imagine-sdk/goand importgithub.com/runapi-ai/grok-imagine-sdk/go/grokimagine - Ruby:
runapi-grok-imaginegem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/grok-imagine
Hailuo SDK
- API reference: Hailuo API
- SDK repository:
runapi-ai/hailuo-sdk - JavaScript:
npm install @runapi.ai/hailuo - Go:
go get github.com/runapi-ai/hailuo-sdk/goand importgithub.com/runapi-ai/hailuo-sdk/go/hailuo - Ruby:
runapi-hailuogem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/hailuo
Ideogram V3 SDK
- API reference: Ideogram V3 API
- SDK repository:
runapi-ai/ideogram-v3-sdk - JavaScript:
npm install @runapi.ai/ideogram-v3 - Go:
go get github.com/runapi-ai/ideogram-v3-sdk/goand importgithub.com/runapi-ai/ideogram-v3-sdk/go/ideogramv3 - Ruby:
runapi-ideogram-v3gem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/ideogram-v3
Imagen 4 SDK
- API reference: Imagen 4 API
- SDK repository:
runapi-ai/imagen4-sdk - JavaScript:
npm install @runapi.ai/imagen4 - Go:
go get github.com/runapi-ai/imagen4-sdk/goand importgithub.com/runapi-ai/imagen4-sdk/go/imagen4 - Ruby:
runapi-imagen4gem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/imagen4
Infinitalk SDK
- API reference: Infinitalk API
- SDK repository:
runapi-ai/infinitalk-sdk - JavaScript:
npm install @runapi.ai/infinitalk - Go:
go get github.com/runapi-ai/infinitalk-sdk/goand importgithub.com/runapi-ai/infinitalk-sdk/go/infinitalk - Ruby:
runapi-infinitalkgem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/infinitalk
Kling SDK
- API reference: Kling API
- SDK repository:
runapi-ai/kling-sdk - JavaScript:
npm install @runapi.ai/kling - Go:
go get github.com/runapi-ai/kling-sdk/goand importgithub.com/runapi-ai/kling-sdk/go/kling - Ruby:
runapi-klinggem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/kling
Luma SDK
- API reference: Luma API
- SDK repository:
runapi-ai/luma-sdk - JavaScript:
npm install @runapi.ai/luma - Go:
go get github.com/runapi-ai/luma-sdk/goand importgithub.com/runapi-ai/luma-sdk/go/luma - Ruby:
runapi-lumagem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/luma
Nano Banana SDK
- API reference: Nano Banana API
- SDK repository:
runapi-ai/nano-banana-sdk - JavaScript:
npm install @runapi.ai/nano-banana - Go:
go get github.com/runapi-ai/nano-banana-sdk/goand importgithub.com/runapi-ai/nano-banana-sdk/go/nanobanana - Ruby:
runapi-nano-bananagem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/nano-banana
Qwen 2 SDK
- API reference: Qwen 2 API
- SDK repository:
runapi-ai/qwen2-sdk - JavaScript:
npm install @runapi.ai/qwen2 - Go:
go get github.com/runapi-ai/qwen2-sdk/goand importgithub.com/runapi-ai/qwen2-sdk/go/qwen2 - Ruby:
runapi-qwen2gem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/qwen2
Recraft SDK
- API reference: Recraft API
- SDK repository:
runapi-ai/recraft-sdk - JavaScript:
npm install @runapi.ai/recraft - Go:
go get github.com/runapi-ai/recraft-sdk/goand importgithub.com/runapi-ai/recraft-sdk/go/recraft - Ruby:
runapi-recraftgem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/recraft
Runway SDK
- API reference: Runway API
- SDK repository:
runapi-ai/runway-sdk - JavaScript:
npm install @runapi.ai/runway - Go:
go get github.com/runapi-ai/runway-sdk/goand importgithub.com/runapi-ai/runway-sdk/go/runway - Ruby:
runapi-runwaygem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/runway
Seedance SDK
- API reference: Seedance API
- SDK repository:
runapi-ai/seedance-sdk - JavaScript:
npm install @runapi.ai/seedance - Go:
go get github.com/runapi-ai/seedance-sdk/goand importgithub.com/runapi-ai/seedance-sdk/go/seedance - Ruby:
runapi-seedancegem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/seedance
Seedream SDK
- API reference: Seedream API
- SDK repository:
runapi-ai/seedream-sdk - JavaScript:
npm install @runapi.ai/seedream - Go:
go get github.com/runapi-ai/seedream-sdk/goand importgithub.com/runapi-ai/seedream-sdk/go/seedream - Ruby:
runapi-seedreamgem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/seedream
Suno SDK
- API reference: Suno API
- SDK repository:
runapi-ai/suno-sdk - JavaScript:
npm install @runapi.ai/suno - Go:
go get github.com/runapi-ai/suno-sdk/goand importgithub.com/runapi-ai/suno-sdk/go/suno - Ruby:
runapi-sunogem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/suno
Topaz SDK
- API reference: Topaz API
- SDK repository:
runapi-ai/topaz-sdk - JavaScript:
npm install @runapi.ai/topaz - Go:
go get github.com/runapi-ai/topaz-sdk/goand importgithub.com/runapi-ai/topaz-sdk/go/topaz - Ruby:
runapi-topazgem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/topaz
Veo 3 SDK
- API reference: Veo 3 API
- SDK repository:
runapi-ai/veo3-sdk - JavaScript:
npm install @runapi.ai/veo3 - Go:
go get github.com/runapi-ai/veo3-sdk/goand importgithub.com/runapi-ai/veo3-sdk/go/veo3 - Ruby:
runapi-veo3gem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/veo3
Wan SDK
- API reference: Wan API
- SDK repository:
runapi-ai/wan-sdk - JavaScript:
npm install @runapi.ai/wan - Go:
go get github.com/runapi-ai/wan-sdk/goand importgithub.com/runapi-ai/wan-sdk/go/wan - Ruby:
runapi-wangem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/wan
Z Image SDK
- API reference: Z Image API
- SDK repository:
runapi-ai/z-image-sdk - JavaScript:
npm install @runapi.ai/z-image - Go:
go get github.com/runapi-ai/z-image-sdk/goand importgithub.com/runapi-ai/z-image-sdk/go/zimage - Ruby:
runapi-z-imagegem source is in the repository; RubyGems publication is deferred until RubyGems onboarding reopens. - Agent skill:
runapi-ai/z-image
RunAPI CLI
runapi is the command-line client for RunAPI — one binary calls 100+ AI models with a JSON-first interface that maps cleanly to scripts, CI pipelines, and agent runtimes.
Install
Linux / macOS (servers, CI, Dockerfiles):
curl -fsSL https://runapi.ai/cli/install.sh | sh
Homebrew (macOS / Linux):
brew install runapi-ai/tap/runapi
Or paste this prompt to your AI agent (Claude Code, Codex, Gemini CLI, OpenClaw, Hermes):
Install the RunAPI CLI on this machine and wire it into this agent runtime:
1. Run `curl -fsSL https://runapi.ai/cli/install.sh | sh`.
2. Save my API key with `printf '%s' "$RUNAPI_API_KEY" | runapi auth import-token --token -`
(or run `runapi login` if I'm on a laptop with a browser).
3. Install the CLI skill into this runtime with `runapi agent install-skill --target <claude|codex|gemini|openclaw|hermes>`.
4. Confirm it works by running `runapi version` and `runapi auth status`.
The curl installer detects OS/arch (Linux/macOS, amd64/arm64), verifies a SHA-256 checksum from https://runapi.ai/cli/latest.json, and refuses to write the binary on musl libc distros (Alpine) rather than installing a binary that will crash at runtime.
Pin a release or use a mirror
# Pin to a specific tag
curl -fsSL https://runapi.ai/cli/install.sh | sh -s -- --version v0.1.0
# Install somewhere else
curl -fsSL https://runapi.ai/cli/install.sh | sh -s -- --dir ~/.local/bin
Environment variables also work: RUNAPI_VERSION, RUNAPI_INSTALL_DIR, RUNAPI_INSTALL_BASE, and RUNAPI_DOWNLOAD_BASE (set to a private mirror to fetch the release archive from your own CDN).
Release metadata is published as a stable JSON contract that build pipelines can verify against without depending on the install script:
-
https://runapi.ai/cli/latest.json— current stable release -
https://runapi.ai/cli/v<x.y.z>/manifest.json— pinned, immutable
Each manifest lists Linux and macOS amd64/arm64 archive URLs with their SHA-256 sums.
Headless authentication
The CLI reads credentials from --api-key, then RUNAPI_API_KEY, then ~/.config/runapi/config.json. Override the API endpoint with RUNAPI_BASE_URL.
# Recommended: pipe the key so it never appears in `ps` or shell history
printf '%s' "$RUNAPI_API_KEY" | runapi auth import-token --token -
runapi auth status
# Alternative: keep the key in the environment for the current shell only
export RUNAPI_API_KEY=<your-key>
runapi auth status
auth import-token verifies the key against /api/v1/me before writing; pass --skip-verify to bypass when bootstrapping an offline image. Avoid --token "$RUNAPI_API_KEY" directly on the command line — the value would be visible in ps -ef to other users on the host.
Agent runtime skill
Install the matching CLI skill into a supported agent runtime so commands like runapi suno generate are documented locally:
runapi agent install-skill --target claude
runapi agent install-skill --target codex
runapi agent install-skill --target gemini
runapi agent install-skill --target openclaw
runapi agent install-skill --target hermes
runapi agent list-targets
The skill content lives at runapi-ai/cli-skill and is tagged in lockstep with CLI releases. Use --target-dir <path> to install into a non-standard location.
CLI links
- Install script: https://runapi.ai/cli/install.sh
- Release manifest: https://runapi.ai/cli/latest.json
- CLI repository:
runapi-ai/cli - Homebrew tap:
runapi-ai/homebrew-tap - Release archives:
runapi-ai/clireleases
Agent Skills
RunAPI skills are portable instructions for Codex, Claude Code, and Gemini CLI. Model skill repositories use pure model names, while sdk-skill and cli-skill route SDK and CLI workflows.
- SDK routing skill:
runapi-ai/sdk-skill - CLI usage skill:
runapi-ai/cli-skill - Model skills: use the model repository links in the SDK index.
Claude
The Claude API provides access to Anthropic's Claude language models. The endpoint is fully compatible with the Anthropic Messages API, so you can use the official Anthropic SDK with RunAPI as the base URL.
Endpoint: POST https://runapi.ai/v1/messages
SDK Quickstart
Using the Anthropic Python SDK:
import anthropic
client = anthropic.Anthropic(
api_key="YOUR_API_TOKEN",
base_url="https://runapi.ai"
)
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{"role": "user", "content": "Hello, Claude!"}
]
)
print(message.content[0].text)
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({
apiKey: 'YOUR_API_TOKEN',
baseURL: 'https://runapi.ai',
});
const message = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [
{ role: 'user', content: 'Hello, Claude!' }
],
});
console.log(message.content[0].text);
require "anthropic"
client = Anthropic::Client.new(
api_key: "YOUR_API_TOKEN",
base_url: "https://runapi.ai"
)
message = client.messages.create(
model: "claude-sonnet-4-6",
max_tokens: 1024,
messages: [
{ role: "user", content: "Hello, Claude!" }
]
)
puts message.content.first.text
curl -X POST "https://runapi.ai/v1/messages" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "Hello, Claude!"}
]
}'
Set base_url (or baseURL) to https://runapi.ai and use your RunAPI API key. Everything else works exactly like the official Anthropic API.
Claude Code
Connect Claude Code to RunAPI:
ANTHROPIC_BASE_URL=https://runapi.ai ANTHROPIC_API_KEY=YOUR_API_TOKEN claude
Set the ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY environment variables, then launch Claude Code as usual.
Claude Authentication
Claude API supports two authentication methods:
| Method | Header | Example |
|---|---|---|
| Anthropic-style | x-api-key |
x-api-key: YOUR_API_TOKEN |
| Bearer token | Authorization |
Authorization: Bearer YOUR_API_TOKEN |
Both methods use the same RunAPI API key. The x-api-key header is recommended for compatibility with the Anthropic SDK.
Models
| Model ID | Description |
|---|---|
claude-opus-4-7 |
Strongest general model for complex tasks |
claude-opus-4-6 |
High-end reasoning model for complex tasks |
claude-sonnet-4-6 |
Best balance of speed and capability |
claude-opus-4-5-20251101 |
High-end reasoning model with tool and reasoning controls |
claude-sonnet-4-5-20250929 |
Balanced chat model with include_thoughts support |
claude-haiku-4-5-20251001 |
Fastest and most affordable |
Official aliases are accepted and canonicalized before storage: claude-opus-4-5 -> claude-opus-4-5-20251101, claude-sonnet-4-5 -> claude-sonnet-4-5-20250929, and claude-haiku-4-5 -> claude-haiku-4-5-20251001.
List Models
List Claude models in Anthropic-compatible format:
curl -X GET "https://runapi.ai/v1/models" \
-H "x-api-key: YOUR_API_TOKEN"
{
"data": [
{
"type": "model",
"id": "claude-opus-4-7",
"display_name": "Claude Opus 4.7",
"created_at": "2026-05-05T08:00:00Z"
}
],
"has_more": false,
"first_id": "claude-opus-4-7",
"last_id": "claude-haiku-4-5-20251001"
}
Count Tokens
Count input tokens before sending a message:
curl -X POST "https://runapi.ai/v1/messages/count_tokens" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"messages": [
{"role": "user", "content": "Count the tokens for this request."}
]
}'
{
"input_tokens": 12
}
POST /v1/messages/count_tokens
This endpoint accepts the same Claude Messages request shape and returns a local estimate of input tokens only. Image blocks use a fixed 512-token heuristic instead of model-specific vision counting. Use the final usage from POST /v1/messages for exact billed token counts.
Create a Message
Create a non-streaming message:
curl -X POST "https://runapi.ai/v1/messages" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"system": "You are a helpful assistant.",
"messages": [
{"role": "user", "content": "Explain quantum computing in simple terms."}
]
}'
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
system="You are a helpful assistant.",
messages=[
{"role": "user", "content": "Explain quantum computing in simple terms."}
]
)
const message = await client.messages.create({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
system: 'You are a helpful assistant.',
messages: [
{ role: 'user', content: 'Explain quantum computing in simple terms.' }
],
});
message = client.messages.create(
model: "claude-sonnet-4-6",
max_tokens: 1024,
system: "You are a helpful assistant.",
messages: [
{ role: "user", content: "Explain quantum computing in simple terms." }
]
)
Response:
{
"id": "msg_01XFDUDYJgAACzvnptvVoYEL",
"type": "message",
"role": "assistant",
"content": [
{
"type": "text",
"text": "Quantum computing uses quantum bits (qubits)..."
}
],
"model": "claude-sonnet-4-6",
"stop_reason": "end_turn",
"usage": {
"input_tokens": 25,
"output_tokens": 150
}
}
POST /v1/messages
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model ID (see Models above) |
messages |
array | Yes | Array of message objects with role and content
|
max_tokens |
integer | Yes | Maximum tokens to generate |
system |
string | No | System prompt |
stream |
boolean | No | Enable SSE streaming (default: false) |
temperature |
number | No | Sampling temperature (0-1) |
top_p |
number | No | Nucleus sampling threshold |
top_k |
integer | No | Top-k sampling |
tools |
array | No | Tool definitions; web access uses googleSearch when supported |
tool_choice |
object | No | Tool selection strategy |
stop_sequences |
array | No | Custom stop sequences |
thinking |
object | No | Extended thinking configuration |
reasoning_effort |
string | No |
"low" or "high" reasoning control |
include_thoughts |
boolean | No | Claude Sonnet only; includes internal reasoning output when supported |
metadata |
object | No | Request metadata |
Headers
| Header | Required | Description |
|---|---|---|
x-api-key |
Yes | Your RunAPI API key |
anthropic-version |
No | API version string |
anthropic-beta |
No | Beta feature flags (e.g., interleaved-thinking-2025-05-14) |
Content-Type |
Yes | Must be application/json
|
Multimodal Input
Send text plus an image in one Claude request:
{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": [
{ "type": "text", "text": "What is in this image?" },
{
"type": "image_url",
"image_url": {
"url": "https://cdn.runapi.ai/public/samples/image.jpg"
}
}
]
}
]
}
Claude accepts the standard Anthropic message format. For multimodal requests, keep media blocks inside messages[].content and use the image_url structure shown above.
Web Access and Reasoning
Enable web access plus reasoning controls:
{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"reasoning_effort": "high",
"include_thoughts": true,
"tools": [
{
"type": "function",
"function": {
"name": "googleSearch"
}
}
],
"messages": [
{
"role": "user",
"content": "Find the latest public information about Claude 4.6."
}
]
}
-
reasoning_effortis supported on the Claude models documented above -
include_thoughtsis only supported onclaude-sonnet-4-5-20250929andclaude-sonnet-4-6 - Web access uses
{"type":"function","function":{"name":"googleSearch"}}
Streaming
Stream a response with SSE:
curl -X POST "https://runapi.ai/v1/messages" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4-6",
"max_tokens": 1024,
"stream": true,
"messages": [
{"role": "user", "content": "Write a haiku about coding."}
]
}'
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[
{"role": "user", "content": "Write a haiku about coding."}
]
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
const stream = await client.messages.stream({
model: 'claude-sonnet-4-6',
max_tokens: 1024,
messages: [
{ role: 'user', content: 'Write a haiku about coding.' }
],
});
for await (const event of stream) {
if (event.type === 'content_block_delta') {
process.stdout.write(event.delta.text);
}
}
client.messages.create(
model: "claude-sonnet-4-6",
max_tokens: 1024,
stream: true,
messages: [
{ role: "user", content: "Write a haiku about coding." }
]
) do |event|
print event.content_block_delta.delta.text if event.type == "content_block_delta"
end
Set stream: true to receive Server-Sent Events (SSE). The response is streamed as a series of events:
SSE Event Types
| Event Type | Description |
|---|---|
message_start |
Session started, includes message metadata |
content_block_start |
New content block begins |
content_block_delta |
Incremental text content |
content_block_stop |
Content block completed |
message_delta |
Final metadata (stop_reason, usage) |
message_stop |
Stream completed |
Billing
Claude API uses token-based billing. Credits are calculated based on actual input and output token usage:
| Model | Input (per 1K tokens) | Output (per 1K tokens) |
|---|---|---|
claude-opus-4-7 |
15 credits | 75 credits |
claude-opus-4-6 |
15 credits | 75 credits |
claude-sonnet-4-6 |
3 credits | 15 credits |
claude-opus-4-5-20251101 |
15 credits | 75 credits |
claude-sonnet-4-5-20250929 |
3 credits | 15 credits |
claude-haiku-4-5-20251001 |
1 credit | 5 credits |
A provisional hold (based on credits_per_call) is placed when the request starts. After the response completes, credits are settled based on actual token usage from the usage field.
Veo 3.1
Veo 3.1 on RunAPI creates high-quality videos from text prompts and images. It supports text-to-video, image-to-video, and reference-based generation with two model variants: Quality and Fast.
Model Variants
| Model | Description | Pricing | Best For |
|---|---|---|---|
veo-3.1 |
Quality model with highest fidelity | Pricing | Professional content, highest quality |
veo-3.1-fast |
Fast variant with strong results | Pricing | Fast iterations and drafts |
Choose Your Generation Mode
| Scenario | Mode | Input Required |
|---|---|---|
| Generate video purely from text description | Text to Video | Text prompt only |
| Animate from a single starting image | Image to Video | 1 image + prompt |
| Create smooth transition between two images | Image to Video | 2 images + prompt |
| Generate video inspired by reference images | Reference Images | 1-3 reference images + prompt |
Generate Video (Text to Video)
Create a video from text prompt:
curl -X POST "https://runapi.ai/api/v1/veo_3_1/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A dog playing in a park on a sunny day, running through grass with a ball in its mouth",
"model": "veo-3.1-fast",
"input_mode": "text",
"aspect_ratio": "16:9",
"duration_seconds": 8,
"enable_translation": true,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/veo_3_1/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
prompt: 'A dog playing in a park on a sunny day, running through grass with a ball in its mouth',
model: 'veo-3.1-fast',
input_mode: 'text',
aspect_ratio: '16:9',
duration_seconds: 8,
enable_translation: true,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/veo_3_1/text_to_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'prompt': 'A dog playing in a park on a sunny day, running through grass with a ball in its mouth',
'model': 'veo-3.1-fast',
'input_mode': 'text',
'aspect_ratio': '16:9',
'duration_seconds': 8,
'enable_translation': True,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
prompt: 'A dog playing in a park on a sunny day, running through grass with a ball in its mouth',
model: 'veo-3.1-fast',
input_mode: 'text',
aspect_ratio: '16:9',
duration_seconds: 8,
enable_translation: true,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/veo_3_1/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Generate a video from a text description.
HTTP Request
POST https://runapi.ai/api/v1/veo_3_1/text_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Text description of the desired video content |
model |
string | Yes | Model to use: veo-3.1 (Quality) or veo-3.1-fast (Fast) |
input_mode |
string | No | Input mode (default: text) |
aspect_ratio |
string | No | Video aspect ratio: 16:9, 9:16, auto (default: 16:9) |
duration_seconds |
integer | No | Generated video duration: 4, 6, or 8 seconds (default: 8) |
seeds |
integer | No | Random seed for reproducibility (10000-99999) |
enable_translation |
boolean | No | Auto-translate prompt to English (default: true) |
watermark |
string | No | Optional watermark text to add to video |
callback_url |
string | No | URL for completion callback |
Aspect Ratio Options
- 16:9: Landscape format, supports 1080P HD video generation
- 9:16: Portrait format, suitable for mobile short videos
- Auto: Automatically crops based on content
Response
Returns a task ID for checking generation status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success Response:
- Includes videos array with generated video details (url, resolution, has_audio)
- For Image to Video: Contains sources array with input image URLs
Failed Response:
- Contains error message describing the failure reason
Success callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/videos/video1.mp4",
"resolution": "720p",
"has_audio": true
}
]
}
Failed callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Your prompt was flagged as violating content policies."
}
Generate Video (Image to Video)
Create a video from one or two images:
curl -X POST "https://runapi.ai/api/v1/veo_3_1/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "The dog starts running energetically across the field, jumping with joy",
"first_frame_image_url": "https://cdn.runapi.ai/public/samples/image-to-video.jpg",
"model": "veo-3.1-fast",
"input_mode": "first_and_last_frames",
"aspect_ratio": "16:9",
"duration_seconds": 8,
"enable_translation": true,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/veo_3_1/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
prompt: 'The dog starts running energetically across the field, jumping with joy',
first_frame_image_url: 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
model: 'veo-3.1-fast',
input_mode: 'first_and_last_frames',
aspect_ratio: '16:9',
duration_seconds: 8,
enable_translation: true,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/veo_3_1/text_to_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'prompt': 'The dog starts running energetically across the field, jumping with joy',
'first_frame_image_url': 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
'model': 'veo-3.1-fast',
'input_mode': 'first_and_last_frames',
'aspect_ratio': '16:9',
'duration_seconds': 8,
'enable_translation': True,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
prompt: 'The dog starts running energetically across the field, jumping with joy',
first_frame_image_url: 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
model: 'veo-3.1-fast',
input_mode: 'first_and_last_frames',
aspect_ratio: '16:9',
duration_seconds: 8,
enable_translation: true,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/veo_3_1/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Generate a video from one or two images.
HTTP Request
POST https://runapi.ai/api/v1/veo_3_1/text_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Description of how the video should animate |
first_frame_image_url |
string | Yes | First frame image URL |
last_frame_image_url |
string | No | Last frame image URL |
model |
string | Yes | Model to use: veo-3.1 or veo-3.1-fast
|
input_mode |
string | No | first_and_last_frames |
aspect_ratio |
string | No | Video aspect ratio: 16:9, 9:16, auto (default: 16:9) |
duration_seconds |
integer | No | Generated video duration: 4, 6, or 8 seconds (default: 8) |
seeds |
integer | No | Random seed for reproducibility (10000-99999) |
enable_translation |
boolean | No | Auto-translate prompt to English (default: true) |
watermark |
string | No | Optional watermark text |
callback_url |
string | No | URL for completion callback |
Frame Images
-
first_frame_image_url: Video unfolds from this starting frame -
last_frame_image_url: Optional ending frame for a smooth transition
Callback Format
Same as Text to Video callback format.
Example with 2 Images
{
"prompt": "Smooth transition from sunrise to sunset",
"first_frame_image_url": "https://cdn.runapi.ai/public/samples/first-frame.jpg",
"last_frame_image_url": "https://cdn.runapi.ai/public/samples/last-frame.jpg",
"model": "veo-3.1-fast",
"input_mode": "first_and_last_frames",
"aspect_ratio": "16:9"
}
Generate Video (Reference Images)
Create a video using 1-3 reference images:
curl -X POST "https://runapi.ai/api/v1/veo_3_1/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A person walking through a modern office space with natural lighting",
"reference_image_urls": [
"https://cdn.runapi.ai/public/samples/reference-1.jpg",
"https://cdn.runapi.ai/public/samples/reference-2.jpg",
"https://cdn.runapi.ai/public/samples/reference-3.jpg"
],
"model": "veo-3.1-fast",
"input_mode": "reference",
"aspect_ratio": "16:9",
"duration_seconds": 8,
"enable_translation": true,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/veo_3_1/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
prompt: 'A person walking through a modern office space with natural lighting',
reference_image_urls: [
'https://cdn.runapi.ai/public/samples/reference-1.jpg',
'https://cdn.runapi.ai/public/samples/reference-2.jpg',
'https://cdn.runapi.ai/public/samples/reference-3.jpg'
],
model: 'veo-3.1-fast',
input_mode: 'reference',
aspect_ratio: '16:9',
duration_seconds: 8,
enable_translation: true,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/veo_3_1/text_to_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'prompt': 'A person walking through a modern office space with natural lighting',
'reference_image_urls': [
'https://cdn.runapi.ai/public/samples/reference-1.jpg',
'https://cdn.runapi.ai/public/samples/reference-2.jpg',
'https://cdn.runapi.ai/public/samples/reference-3.jpg'
],
'model': 'veo-3.1-fast',
'input_mode': 'reference',
'aspect_ratio': '16:9',
'duration_seconds': 8,
'enable_translation': True,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
prompt: 'A person walking through a modern office space with natural lighting',
reference_image_urls: [
'https://cdn.runapi.ai/public/samples/reference-1.jpg',
'https://cdn.runapi.ai/public/samples/reference-2.jpg',
'https://cdn.runapi.ai/public/samples/reference-3.jpg'
],
model: 'veo-3.1-fast',
input_mode: 'reference',
aspect_ratio: '16:9',
duration_seconds: 8,
enable_translation: true,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/veo_3_1/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Generate a video based on 1-3 reference images.
HTTP Request
POST https://runapi.ai/api/v1/veo_3_1/text_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Description of the desired video content |
reference_image_urls |
array | Yes | Array of 1-3 reference image URLs |
model |
string | Yes | Must be veo-3.1-fast for reference |
input_mode |
string | No | reference |
aspect_ratio |
string | No | Must be 16:9 for reference |
duration_seconds |
integer | No | Generated video duration: 4, 6, or 8 seconds (default: 8) |
seeds |
integer | No | Random seed for reproducibility (10000-99999) |
enable_translation |
boolean | No | Auto-translate prompt to English (default: true) |
watermark |
string | No | Optional watermark text |
callback_url |
string | No | URL for completion callback |
Callback Format
Same as Text to Video callback format.
Get Text-to-Video Status
Poll for generation results:
curl "https://runapi.ai/api/v1/veo_3_1/text_to_video/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer YOUR_API_TOKEN"
require 'net/http'
require 'uri'
task_id = '550e8400-e29b-41d4-a716-446655440000'
uri = URI("https://runapi.ai/api/v1/veo_3_1/text_to_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
task_id = '550e8400-e29b-41d4-a716-446655440000'
url = f'https://runapi.ai/api/v1/veo_3_1/text_to_video/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = '550e8400-e29b-41d4-a716-446655440000';
fetch(`https://runapi.ai/api/v1/veo_3_1/text_to_video/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Processing:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Completed:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/videos/video1.mp4",
"resolution": "1080p",
"has_audio": true
}
]
}
Failed:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Video generation failed"
}
Retrieve the status and results of a video generation task.
HTTP Request
GET https://runapi.ai/api/v1/veo_3_1/text_to_video/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The ID of the task returned from the create request |
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
videos |
array | Array of generated video objects (only when completed) |
error |
string | Error message (only when failed) |
Video Object
| Field | Type | Description |
|---|---|---|
url |
string | Video download URL |
resolution |
string | Video resolution (e.g., "1080p", "720p") |
has_audio |
boolean | Whether the video has audio |
Extend Video
Extend an existing video with new content:
curl -X POST "https://runapi.ai/api/v1/veo_3_1/extend_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"prompt": "The dog continues running through the park, jumping over obstacles",
"watermark": "MyBrand",
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/veo_3_1/extend_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
source_task_id: '550e8400-e29b-41d4-a716-446655440000',
prompt: 'The dog continues running through the park, jumping over obstacles',
watermark: 'MyBrand',
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/veo_3_1/extend_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'source_task_id': '550e8400-e29b-41d4-a716-446655440000',
'prompt': 'The dog continues running through the park, jumping over obstacles',
'watermark': 'MyBrand',
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
source_task_id: '550e8400-e29b-41d4-a716-446655440000',
prompt: 'The dog continues running through the park, jumping over obstacles',
watermark: 'MyBrand',
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/veo_3_1/extend_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Extend an existing Veo 3.1 video by generating new content based on the original video and a text prompt.
HTTP Request
POST https://runapi.ai/api/v1/veo_3_1/extend_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
source_task_id |
string | Yes | Task ID of the original video generation |
prompt |
string | Yes | Text description of the extension content |
seeds |
integer | No | Random seed for reproducibility (10000-99999) |
watermark |
string | No | Optional watermark text to add to the extended video |
callback_url |
string | No | URL for completion callback |
Response
Returns a task ID for checking extension status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
Callback Format
Same as Text to Video callback format.
Get Video Extension Status
Poll for extension results:
curl "https://runapi.ai/api/v1/veo_3_1/extend_video/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = '550e8400-e29b-41d4-a716-446655440000'
uri = URI("https://runapi.ai/api/v1/veo_3_1/extend_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
task_id = '550e8400-e29b-41d4-a716-446655440000'
url = f'https://runapi.ai/api/v1/veo_3_1/extend_video/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = '550e8400-e29b-41d4-a716-446655440000';
fetch(`https://runapi.ai/api/v1/veo_3_1/extend_video/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Processing:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
Completed:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/videos/extended_video1.mp4",
"resolution": "720p",
"has_audio": true
}
],
"sources": [
{
"url": "https://file.runapi.ai/videos/original_video1.mp4"
}
]
}
Failed:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Video extension failed."
}
Retrieve the status and results of a video extension task.
HTTP Request
GET https://runapi.ai/api/v1/veo_3_1/extend_video/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The ID of the extension task returned from the create request |
Response Fields
Same as Get Text-to-Video Status response format.
Request 1080P Video
Request high-definition 1080P version:
curl -X POST "https://runapi.ai/api/v1/veo_3_1/upscale_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"output_resolution": "1080p",
"index": 0,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/veo_3_1/upscale_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
source_task_id: '550e8400-e29b-41d4-a716-446655440000',
output_resolution: '1080p',
index: 0,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/veo_3_1/upscale_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'source_task_id': '550e8400-e29b-41d4-a716-446655440000',
'output_resolution': '1080p',
'index': 0,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
source_task_id: '550e8400-e29b-41d4-a716-446655440000',
output_resolution: '1080p',
index: 0,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/veo_3_1/upscale_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Request a high-definition 1080P version of a completed video generation task.
HTTP Request
POST https://runapi.ai/api/v1/veo_3_1/upscale_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
source_task_id |
string | Yes | The task ID from a completed video generation |
output_resolution |
string | Yes | Output resolution: 1080p
|
index |
integer | No | Video index if multiple videos were generated (default: 0) |
callback_url |
string | No | URL for completion callback |
Response
Returns a task ID for checking 1080P conversion status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
Callback Format
When the 1080P video is ready, a POST request will be sent to your callback_url.
Success Response:
- Includes status: "completed"
- Contains videos array with URL and resolution
- References source_task_id
Success:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "completed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"videos": [
{
"url": "https://tempfile.runapi.ai/example.mp4",
"resolution": "1080p"
}
]
}
Failed Response:
- Includes status: "failed"
- Contains error message with failure reason
Failed:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "failed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"error": "Video generation failed."
}
Request 4K Video
Request ultra-high-definition 4K version:
curl -X POST "https://runapi.ai/api/v1/veo_3_1/upscale_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"output_resolution": "4k",
"index": 0,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/veo_3_1/upscale_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
source_task_id: '550e8400-e29b-41d4-a716-446655440000',
output_resolution: '4k',
index: 0,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/veo_3_1/upscale_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'source_task_id': '550e8400-e29b-41d4-a716-446655440000',
'output_resolution': '4k',
'index': 0,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
source_task_id: '550e8400-e29b-41d4-a716-446655440000',
output_resolution: '4k',
index: 0,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/veo_3_1/upscale_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Request an ultra-high-definition 4K version of a completed video generation task.
HTTP Request
POST https://runapi.ai/api/v1/veo_3_1/upscale_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
source_task_id |
string | Yes | The task ID from a completed video generation |
output_resolution |
string | Yes | Output resolution: 4k
|
index |
integer | No | Video index if multiple videos were generated (default: 0) |
callback_url |
string | No | URL for completion callback |
Response
Returns a task ID for checking 4K conversion status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
Callback Format
When the 4K video is ready, a POST request will be sent to your callback_url.
Success Response:
- Includes status: "completed"
- Contains videos array with URL and resolution
- References source_task_id
Success:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "completed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"videos": [
{
"url": "https://tempfile.runapi.ai/example_4k.mp4",
"resolution": "4k"
}
]
}
Failed Response:
- Includes status: "failed"
- Contains error message with failure reason
Failed:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "failed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"error": "The 4K version of this video is unavailable."
}
Get 4K Video Status
Poll for 4K conversion results:
curl "https://runapi.ai/api/v1/veo_3_1/upscale_video/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = '3fa85f64-5717-4562-b3fc-2c963f66afa6'
uri = URI("https://runapi.ai/api/v1/veo_3_1/upscale_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
task_id = '3fa85f64-5717-4562-b3fc-2c963f66afa6'
url = f'https://runapi.ai/api/v1/veo_3_1/upscale_video/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = '3fa85f64-5717-4562-b3fc-2c963f66afa6';
fetch(`https://runapi.ai/api/v1/veo_3_1/upscale_video/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Processing:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "processing",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000"
}
Completed:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "completed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"videos": [
{
"url": "https://tempfile.runapi.ai/example_4k.mp4",
"resolution": "4k"
}
]
}
Failed:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "failed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"error": "The 4K version of this video is unavailable."
}
Check the status of a 4K video conversion request.
HTTP Request
GET https://runapi.ai/api/v1/veo_3_1/upscale_video/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the create request |
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
source_task_id |
string | The source video generation task ID |
videos |
array | Array of video objects (only when completed) |
error |
string | Error message (only when failed) |
Video Object
| Field | Type | Description |
|---|---|---|
url |
string | 4K video download URL |
resolution |
string | Video resolution ("4k") |
Get 1080P Video Status
Poll for 1080P conversion results:
curl "https://runapi.ai/api/v1/veo_3_1/upscale_video/3fa85f64-5717-4562-b3fc-2c963f66afa6" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = '3fa85f64-5717-4562-b3fc-2c963f66afa6'
uri = URI("https://runapi.ai/api/v1/veo_3_1/upscale_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
task_id = '3fa85f64-5717-4562-b3fc-2c963f66afa6'
url = f'https://runapi.ai/api/v1/veo_3_1/upscale_video/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = '3fa85f64-5717-4562-b3fc-2c963f66afa6';
fetch(`https://runapi.ai/api/v1/veo_3_1/upscale_video/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Processing:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "processing",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000"
}
Completed:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "completed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"videos": [
{
"url": "https://tempfile.runapi.ai/example.mp4",
"resolution": "1080p"
}
]
}
Failed:
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"status": "failed",
"source_task_id": "550e8400-e29b-41d4-a716-446655440000",
"error": "Video generation failed."
}
Check the status of a 1080P video conversion request.
HTTP Request
GET https://runapi.ai/api/v1/veo_3_1/upscale_video/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the create request |
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
source_task_id |
string | The source video generation task ID |
videos |
array | Array of video objects (only when completed) |
error |
string | Error message (only when failed) |
Video Object
| Field | Type | Description |
|---|---|---|
url |
string | 1080P video download URL |
resolution |
string | Video resolution ("1080p") |
Seedance
Seedance on RunAPI provides video creation models. It spans the stable seedance-1.5-pro, the newer seedance-2.0 / seedance-2.0-fast with frame-based and multimodal reference inputs, and the lightweight seedance-v1-* family for quick 5s / 10s clips.
Model Variants
| Model | Description | Pricing | Best For |
|---|---|---|---|
seedance-1.5-pro |
Mature model for text-to-video and image-to-video with camera lock support | Pricing | Stable production usage |
seedance-2.0 |
Newer model with first/last frame and multimodal reference inputs | Pricing | Richer guided generation |
seedance-2.0-fast |
Faster and cheaper 2.x variant | Pricing | Rapid iteration |
seedance-v1-lite |
Lightweight text-to-video and image-to-video (with optional end frame) | Pricing | Quick clips with fine-grained control |
seedance-v1-pro |
Higher-quality v1 text-to-video and image-to-video | Pricing | Premium v1 generation |
seedance-v1-pro-fast |
Image-to-video only, fastest v1 option | Pricing | Rapid image animation |
Seedance Endpoints
| Task | Endpoint | Models |
|---|---|---|
| Create video task | Create Seedance Text-to-Video |
seedance-1.5-pro, seedance-2.0, seedance-2.0-fast, seedance-v1-lite, seedance-v1-pro, seedance-v1-pro-fast
|
| Check video status | Get Seedance Text-to-Video Status | - |
Input Modes
seedance-1.5-pro
- Text-to-video: send
prompt+aspect_ratio+duration_seconds - Image-to-video: send
prompt+aspect_ratio+duration_seconds+source_image_urls - Supports
lock_camera -
duration_secondsis required and must be4,8, or12
seedance-2.0 / seedance-2.0-fast
- Text-to-video: send
prompt - Frame mode: send
first_frame_image_urland optionallylast_frame_image_url - Reference mode: send any of
reference_image_urls,reference_video_urls,reference_audio_urls
seedance-v1-lite / seedance-v1-pro
- Text-to-video: send
prompt+aspect_ratio+duration_seconds - Image-to-video: also send
first_frame_image_url.aspect_ratiois derived from the image in this mode — omit it. -
seedance-v1-liteimage-to-video additionally supportslast_frame_image_urlfor an end-frame target. -
duration_secondsis required and must be5or10.
seedance-v1-pro-fast
- Image-to-video only:
first_frame_image_urlis required. - Accepts a narrower parameter set:
prompt,first_frame_image_url,output_resolution(720por1080p),duration_seconds,enable_safety_checker,callback_url.
Create Seedance Text-to-Video
Create a Seedance video task:
curl -X POST "https://runapi.ai/api/v1/seedance/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-1.5-pro",
"prompt": "A cinematic drone shot of a mountain lake at sunrise with mist rising from the water",
"aspect_ratio": "16:9",
"output_resolution": "720p",
"duration_seconds": 8,
"generate_audio": false,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/seedance/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'seedance-1.5-pro',
prompt: 'A cinematic drone shot of a mountain lake at sunrise with mist rising from the water',
aspect_ratio: '16:9',
output_resolution: '720p',
duration_seconds: 8,
generate_audio: false,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/seedance/text_to_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'seedance-1.5-pro',
'prompt': 'A cinematic drone shot of a mountain lake at sunrise with mist rising from the water',
'aspect_ratio': '16:9',
'output_resolution': '720p',
'duration_seconds': 8,
'generate_audio': False,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'seedance-1.5-pro',
prompt: 'A cinematic drone shot of a mountain lake at sunrise with mist rising from the water',
aspect_ratio: '16:9',
output_resolution: '720p',
duration_seconds: 8,
generate_audio: false,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/seedance/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/seedance/text_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
seedance-1.5-pro, seedance-2.0, seedance-2.0-fast, seedance-v1-lite, seedance-v1-pro, or seedance-v1-pro-fast
|
prompt |
string | Yes | Text prompt. seedance-1.5-pro: 3–2500; seedance-2.0 / seedance-2.0-fast: 3–20000; seedance-v1-*: 3–10000 characters |
callback_url |
string | No | HTTPS callback URL for completion events |
aspect_ratio |
string | Conditional | Required for seedance-1.5-pro and for seedance-v1-lite / seedance-v1-pro text-to-video. 2.x supports 1:1, 4:3, 3:4, 16:9, 9:16, 21:9, auto. seedance-v1-lite: 1:1, 4:3, 3:4, 16:9, 9:16, 9:21. seedance-v1-pro: 1:1, 4:3, 3:4, 16:9, 9:16, 21:9. Rejected in v1 image-to-video mode (derived from image). |
output_resolution |
string | No |
seedance-1.5-pro, seedance-2.0, seedance-v1-lite, seedance-v1-pro: 480p, 720p, 1080p. seedance-2.0-fast: 480p, 720p. seedance-v1-pro-fast: 720p, 1080p
|
duration_seconds |
integer | Conditional |
seedance-1.5-pro required: 4, 8, or 12. 2.x: integer 4–15. seedance-v1-*: required; must be 5 or 10
|
generate_audio |
boolean | No | 1.5-pro / 2.x only. Whether to generate audio |
enable_safety_checker |
boolean | No | Content safety check toggle |
source_image_urls |
array | No |
seedance-1.5-pro only. Up to 2 source images for image-to-video |
lock_camera |
boolean | No | Lock camera movement. Supported by seedance-1.5-pro, seedance-v1-lite, seedance-v1-pro
|
seed |
integer | No |
seedance-v1-lite / seedance-v1-pro only. Random seed in [-1, 2147483647]; -1 = random |
first_frame_image_url |
string | Conditional | 2.x frame mode first frame image URL. Also triggers v1 image-to-video mode and is required for seedance-v1-pro-fast
|
last_frame_image_url |
string | No | 2.x frame mode, or seedance-v1-lite image-to-video end frame image URL |
reference_image_urls |
array | No | 2.x only. Reference image URLs (reference mode) |
reference_video_urls |
array | No | 2.x only. Max 3 reference videos, total ≤ 15s (reference mode) |
reference_audio_urls |
array | No | 2.x only. Reference audio URLs; requires at least one image or video (reference mode) |
web_search |
boolean | No | 2.x only. Optional web search toggle |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial async status, typically processing
|
Callback Format
If callback_url is provided, a POST request will be sent when the task completes.
Success callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/generated-video.mp4"
}
]
}
Failed callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Task failed"
}
Get Seedance Text-to-Video Status
Check the status of a Seedance task:
curl "https://runapi.ai/api/v1/seedance/text_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = 'YOUR_TASK_ID'
uri = URI("https://runapi.ai/api/v1/seedance/text_to_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
task_id = 'YOUR_TASK_ID'
response = requests.get(
f'https://runapi.ai/api/v1/seedance/text_to_video/{task_id}',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
const taskId = 'YOUR_TASK_ID';
fetch(`https://runapi.ai/api/v1/seedance/text_to_video/${taskId}`, {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
})
.then(res => res.json())
.then(data => console.log(data));
Completed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/generated-video.mp4"
}
],
"last_frame_image_url": "https://tempfile.runapi.ai/generated-last-frame.png"
}
HTTP Request
GET https://runapi.ai/api/v1/seedance/text_to_video/:id
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Task identifier |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video URLs when completed |
last_frame_image_url |
string | Present when the request included a last frame image |
error |
string | Error message when the task fails |
Seedream
Seedream on RunAPI supports text-to-image and image editing workflows.
Models
| Model | Type | Pricing |
|---|---|---|
seedream-4.5-text-to-image |
Text-to-image | Pricing |
seedream-4.5-edit |
Image edit | Pricing |
seedream-5-lite-text-to-image |
Text-to-image | Pricing |
seedream-5-lite-edit |
Image edit | Pricing |
seedream-v4-text-to-image |
Text-to-image | Pricing |
seedream-v4-edit |
Image edit | Pricing |
Seedream Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create image | Create Seedream Text-to-Image | POST |
| Edit image | Create Seedream Edit Image | POST |
| Check text-to-image status | Get Seedream Text-to-Image Status | GET |
| Check edit status | Get Seedream Edit Image Status | GET |
Create Seedream Text-to-Image
curl -X POST "https://runapi.ai/api/v1/seedream/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-4.5-text-to-image",
"prompt": "A cinematic portrait of a traveler crossing a rainy neon street",
"aspect_ratio": "16:9",
"output_quality": "basic"
}'
POST https://runapi.ai/api/v1/seedream/text_to_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
seedream-4.5-text-to-image, seedream-5-lite-text-to-image, or seedream-v4-text-to-image
|
prompt |
string | Yes | 4.5 models accept 1-3000 chars; 5-Lite models accept 3-3000 chars; v4 models accept 1-5000 chars |
aspect_ratio |
string | Conditional | Required for 4.5 and 5-Lite text models; 1:1, 4:3, 3:4, 16:9, 9:16, 2:3, 3:2, 21:9
|
output_quality |
string | Conditional | Required for 4.5 and 5-Lite text models; basic or high
|
callback_url |
string | No | Webhook URL for completion notification |
output_resolution |
string | No | v4 only; 1k, 2k, or 4k; default 1k
|
output_count |
integer | No | v4 only; 1-6 images, default 1 |
seed |
integer | No | v4 only; random seed for reproducible output |
enable_safety_checker |
boolean | No | Optional content safety check toggle; default false
|
Seedream v4 text-to-image request
curl -X POST "https://runapi.ai/api/v1/seedream/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-v4-text-to-image",
"prompt": "A precise product render of a glass teapot on white marble",
"aspect_ratio": "16:9",
"output_resolution": "2k",
"output_count": 3,
"seed": 12345,
"enable_safety_checker": true
}'
Create Seedream Edit Image
curl -X POST "https://runapi.ai/api/v1/seedream/edit_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-v4-edit",
"prompt": "Place the logo on a blue outdoor cap",
"source_image_urls": ["https://cdn.runapi.ai/public/samples/image.jpg"],
"aspect_ratio": "1:1",
"output_resolution": "4k",
"output_count": 1,
"enable_safety_checker": false
}'
POST https://runapi.ai/api/v1/seedream/edit_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
seedream-4.5-edit, seedream-5-lite-edit, or seedream-v4-edit
|
prompt |
string | Yes | 4.5 models accept 1-3000 chars; 5-Lite models accept 3-3000 chars; v4 models accept 1-5000 chars |
source_image_urls |
array | Yes | Source image URLs; v4 edit accepts up to 10 image URLs |
aspect_ratio |
string | Conditional | Required for 4.5 and 5-Lite edit models; optional for v4; 1:1, 4:3, 3:4, 16:9, 9:16, 2:3, 3:2, 21:9
|
output_quality |
string | Conditional | Required for 4.5 and 5-Lite edit models; basic or high
|
callback_url |
string | No | Webhook URL for completion notification |
output_resolution |
string | No | v4 only; 1k, 2k, or 4k; default 1k
|
output_count |
integer | No | v4 only; 1-6 images, default 1 |
seed |
integer | No | v4 only; random seed for reproducible output |
enable_safety_checker |
boolean | No | Optional content safety check toggle; default false
|
Response
{
"id": "281e5b0f39b9",
"status": "processing"
}
Get Seedream Text-to-Image Status
curl "https://runapi.ai/api/v1/seedream/text_to_image/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
GET https://runapi.ai/api/v1/seedream/text_to_image/:id
Get Seedream Edit Image Status
curl "https://runapi.ai/api/v1/seedream/edit_image/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
GET https://runapi.ai/api/v1/seedream/edit_image/:id
Completed response
{
"id": "281e5b0f39b9",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/seedream/result.png"
}
]
}
Failed response
{
"id": "281e5b0f39b9",
"status": "failed",
"error": "Prompt rejected"
}
Runway
Runway provides text_to_video and extend_video endpoints.
Create Text To Video
curl -X POST "https://runapi.ai/api/v1/runway/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A camera glide through ocean mist at sunrise",
"duration_seconds": 5,
"output_resolution": "720p",
"aspect_ratio": "16:9"
}'
Response
{
"id": "281e5b0f...f39b9",
"status": "processing"
}
POST https://runapi.ai/api/v1/runway/text_to_video
Pricing: Runway model page.
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Video prompt. |
duration_seconds |
integer | Yes |
5 or 10. |
output_resolution |
string | Yes |
720p or 1080p. |
first_frame_image_url |
string | No | First frame image for image-to-video. |
aspect_ratio |
string | No | Required for text-to-video. |
watermark |
string | No | Watermark text. |
callback_url |
string | No | Completion webhook URL. |
Get Text To Video
GET https://runapi.ai/api/v1/runway/text_to_video/{id}
Completed response
{
"id": "281e5b0f...f39b9",
"status": "completed",
"videos": [
{ "id": "video-1", "url": "https://file.runapi.ai/video.mp4" }
],
"images": [
{ "url": "https://file.runapi.ai/cover.png" }
]
}
Create Extend Video
POST https://runapi.ai/api/v1/runway/extend_video
Pricing: Runway model page.
| Parameter | Type | Required | Description |
|---|---|---|---|
source_task_id |
string | Yes | Source task ID. |
prompt |
string | Yes | How to continue the video. |
output_resolution |
string | Yes |
720p or 1080p. |
watermark |
string | No | Watermark text. |
callback_url |
string | No | Completion webhook URL. |
Get Extend Video
GET https://runapi.ai/api/v1/runway/extend_video/{id}
Runway Aleph
Runway Aleph provides the edit_video endpoint.
Create Edit Video
POST https://runapi.ai/api/v1/runway_aleph/edit_video
curl -X POST "https://runapi.ai/api/v1/runway_aleph/edit_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Transform the scene into moody dusk lighting",
"source_video_url": "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4",
"reference_image_url": "https://raw.githubusercontent.com/github/explore/main/topics/python/python.png"
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Transformation prompt. |
source_video_url |
string | Yes | Source video URL. |
reference_image_url |
string | No | Style reference image URL. |
aspect_ratio |
string | No |
16:9, 9:16, 4:3, 3:4, 1:1, 21:9. |
seed |
integer | No | Random seed. |
callback_url |
string | No | Completion webhook URL. |
Get Edit Video
GET https://runapi.ai/api/v1/runway_aleph/edit_video/{id}
Completed response
{
"id": "281e5b0f...f39b9",
"status": "completed",
"videos": [
{ "url": "https://file.runapi.ai/runway-aleph.mp4" }
],
"images": [
{ "url": "https://file.runapi.ai/runway-aleph.png" }
]
}
Kling
Kling is a video generation model on RunAPI. The kling-3.0 model supports text-to-video and image-to-video with both single-shot and multi-shot modes, plus element references for reusable subjects and styles.
Model Variants
| Model | Description | Pricing | Best For |
|---|---|---|---|
kling-3.0 |
Kling 3.0, supports single-shot and multi-shot generation with element references | Pricing | Cinematic video generation with scene control |
kling-ai-avatar-pro |
Kling AI Avatar Pro, high-quality avatar video driven by audio | Pricing | Professional-grade talking avatar videos |
kling-ai-avatar-standard |
Kling AI Avatar Standard, efficient avatar video driven by audio | Pricing | Quick talking avatar videos |
kling-ai-avatar-v1-pro |
Kling AI Avatar v1 Pro, high-quality avatar video driven by audio | Pricing | V1 Pro talking avatar videos |
kling-v1-avatar-standard |
Kling Avatar v1 Standard, efficient avatar video driven by audio | Pricing | V1 Standard talking avatar videos |
kling-v2.1-pro |
Kling V2.1 Pro image-to-video with optional final-frame control | Pricing | Image-anchored clips with start/end framing |
kling-v2.1-standard |
Kling V2.1 Standard image-to-video, efficient image animation | Pricing | Lower-cost image-to-video drafts |
kling-v2.1-master-text-to-video |
Kling V2.1 Master text-to-video with higher-quality rendering | Pricing | Premium text-to-video clips |
kling-v2.1-master-image-to-video |
Kling V2.1 Master image-to-video with higher-quality rendering | Pricing | Premium image-anchored clips |
kling-v2.5-turbo-text-to-video-pro |
Kling V2.5 Turbo text-to-video, fast high-quality generation | Pricing | Fast text-to-video with turbo speed |
kling-v2.5-turbo-image-to-video-pro |
Kling V2.5 Turbo image-to-video, fast high-quality generation | Pricing | Fast image-to-video with turbo speed |
Kling Endpoints
| Task | Endpoint | Models |
|---|---|---|
| Create text-to-video task | Create Kling Text-to-Video | kling-3.0 |
| Check text-to-video status | Get Kling Text-to-Video Status | - |
| Create AI avatar task | Create AI Avatar |
kling-ai-avatar-pro, kling-ai-avatar-standard, kling-ai-avatar-v1-pro, kling-v1-avatar-standard
|
| Check AI avatar status | Get AI Avatar Status | - |
| Create V2.1 / V2.5 video task | Create V2.1 and V2.5 Video |
kling-v2.1-pro, kling-v2.1-standard, kling-v2.1-master-text-to-video, kling-v2.1-master-image-to-video, kling-v2.5-turbo-text-to-video-pro, kling-v2.5-turbo-image-to-video-pro
|
| Check V2.1 / V2.5 video status | Get V2.1 and V2.5 Video Status | - |
| Create motion control task | Create Motion Control | kling-3.0 |
| Check motion control status | Get Motion Control Status | - |
Single-shot vs Multi-shot
Kling 3.0 supports two generation modes controlled by the multi_shots flag.
Single-shot mode (default)
- Omit
multi_shotsor set it tofalse - Send a single
promptdescribing the whole video - Optional
first_frame_image_urlandlast_frame_image_urlcan provide first and last frame references -
enable_soundis optional
Multi-shot mode
- Set
multi_shots: true -
enable_soundmust betrue - Send
multi_prompt, an ordered array of{prompt, duration_seconds}items, one per shot - Each shot's
promptis capped at 500 characters andduration_secondsmust be an integer from 1 to 12 seconds -
last_frame_image_urlis not supported in multi-shot mode
Element References
Kling 3.0 supports reusable "elements" — named subjects or styles you can reference inside any prompt using the @element_name syntax. Define each element under kling_elements, then mention it by name in your prompt or multi_prompt[i].prompt.
-
name: element identifier used in@namereferences inside prompts -
description: human-readable description of the element -
element_input_urls: 2 to 4 JPG or PNG image URLs, at least 300×300 pixels each, max 10 MB per image -
element_input_video_urls: 1 MP4 or MOV video URL, max 50 MB
Reference the element inside a prompt with @element_name, for example: "@alice walks through the neon-lit alley at night".
Example request with element references:
{
"model": "kling-3.0",
"prompt": "@alice and @bob stand back to back in the rain",
"kling_elements": [
{
"name": "alice",
"description": "A young woman in a red jacket with short black hair",
"element_input_urls": [
"https://cdn.runapi.ai/public/samples/portrait.jpg",
"https://cdn.runapi.ai/public/samples/portrait.jpg"
]
},
{
"name": "bob",
"description": "A tall man in a leather coat",
"element_input_urls": [
"https://cdn.runapi.ai/public/samples/portrait-2.jpg",
"https://cdn.runapi.ai/public/samples/portrait-2.jpg"
]
}
]
}
Create Kling Text-to-Video
Create a single-shot Kling text-to-video task:
curl -X POST "https://runapi.ai/api/v1/kling/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3.0",
"prompt": "A cinematic drone shot of a mountain lake at sunrise with mist rising from the water",
"aspect_ratio": "16:9",
"duration_seconds": 8,
"output_resolution": "1080p",
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/kling/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'kling-3.0',
prompt: 'A cinematic drone shot of a mountain lake at sunrise with mist rising from the water',
aspect_ratio: '16:9',
duration_seconds: 8,
output_resolution: '1080p',
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/kling/text_to_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'kling-3.0',
'prompt': 'A cinematic drone shot of a mountain lake at sunrise with mist rising from the water',
'aspect_ratio': '16:9',
'duration_seconds': 8,
'output_resolution': '1080p',
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'kling-3.0',
prompt: 'A cinematic drone shot of a mountain lake at sunrise with mist rising from the water',
aspect_ratio: '16:9',
duration_seconds: 8,
output_resolution: '1080p',
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/kling/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
Create a multi-shot Kling text-to-video task:
curl -X POST "https://runapi.ai/api/v1/kling/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3.0",
"multi_shots": true,
"enable_sound": true,
"aspect_ratio": "16:9",
"output_resolution": "1080p",
"multi_prompt": [
{ "prompt": "A lone astronaut steps out of a lunar lander onto the dusty grey surface", "duration_seconds": 4 },
{ "prompt": "Wide shot of the astronaut planting a flag as Earth rises on the horizon", "duration_seconds": 5 },
{ "prompt": "Close-up on the visor reflecting the stars and the Earth", "duration_seconds": 3 }
]
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/kling/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'kling-3.0',
multi_shots: true,
enable_sound: true,
aspect_ratio: '16:9',
output_resolution: '1080p',
multi_prompt: [
{ prompt: 'A lone astronaut steps out of a lunar lander onto the dusty grey surface', duration_seconds: 4 },
{ prompt: 'Wide shot of the astronaut planting a flag as Earth rises on the horizon', duration_seconds: 5 },
{ prompt: 'Close-up on the visor reflecting the stars and the Earth', duration_seconds: 3 }
]
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/kling/text_to_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'kling-3.0',
'multi_shots': True,
'enable_sound': True,
'aspect_ratio': '16:9',
'output_resolution': '1080p',
'multi_prompt': [
{ 'prompt': 'A lone astronaut steps out of a lunar lander onto the dusty grey surface', 'duration_seconds': 4 },
{ 'prompt': 'Wide shot of the astronaut planting a flag as Earth rises on the horizon', 'duration_seconds': 5 },
{ 'prompt': 'Close-up on the visor reflecting the stars and the Earth', 'duration_seconds': 3 }
]
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'kling-3.0',
multi_shots: true,
enable_sound: true,
aspect_ratio: '16:9',
output_resolution: '1080p',
multi_prompt: [
{ prompt: 'A lone astronaut steps out of a lunar lander onto the dusty grey surface', duration_seconds: 4 },
{ prompt: 'Wide shot of the astronaut planting a flag as Earth rises on the horizon', duration_seconds: 5 },
{ prompt: 'Close-up on the visor reflecting the stars and the Earth', duration_seconds: 3 }
]
};
fetch('https://runapi.ai/api/v1/kling/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/kling/text_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be kling-3.0
|
prompt |
string | Conditional | Required in single-shot mode. Text description of the desired video |
enable_sound |
boolean | Conditional | Whether to generate sound. Must be true when multi_shots is true
|
duration_seconds |
number | No | Total video duration in seconds, 3 through 15
|
aspect_ratio |
string | Conditional |
16:9, 9:16, or 1:1. Optional when first_frame_image_url is provided (auto-adapts to the image) |
output_resolution |
string | No | Output resolution: 720p, 1080p, or 4k
|
multi_shots |
boolean | No | Set to true to enable multi-shot mode |
multi_prompt |
array | Conditional | Required when multi_shots is true. Ordered array of {prompt, duration_seconds} items, one per shot |
first_frame_image_url |
string | No | First-frame image URL |
last_frame_image_url |
string | No | Last-frame image URL for single-shot mode |
kling_elements |
array | No | Element references for reusable subjects or styles (see Element References) |
callback_url |
string | No | HTTPS callback URL for completion events |
multi_prompt item
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Text prompt for this shot, max 500 characters |
duration_seconds |
integer | Yes | Duration of this shot in seconds, integer from 1 to 12
|
kling_elements item
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Element name, referenced in prompts as @name
|
description |
string | Yes | Human-readable description of the element |
element_input_urls |
array | No | 2 to 4 JPG or PNG image URLs, at least 300×300 pixels each, max 10 MB per image |
element_input_video_urls |
array | No | 1 MP4 or MOV video URL, max 50 MB |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial async status, typically processing
|
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/generated-video.mp4"
}
]
}
Failed callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
Get Kling Text-to-Video Status
Check the status of a Kling task:
curl "https://runapi.ai/api/v1/kling/text_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = 'YOUR_TASK_ID'
uri = URI("https://runapi.ai/api/v1/kling/text_to_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
task_id = 'YOUR_TASK_ID'
response = requests.get(
f'https://runapi.ai/api/v1/kling/text_to_video/{task_id}',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
const taskId = 'YOUR_TASK_ID';
fetch(`https://runapi.ai/api/v1/kling/text_to_video/${taskId}`, {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
})
.then(res => res.json())
.then(data => console.log(data));
Completed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/generated-video.mp4"
}
]
}
Failed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
HTTP Request
GET https://runapi.ai/api/v1/kling/text_to_video/:id
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Task identifier |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video URLs when completed |
error |
string | Error message when generation fails |
Create AI Avatar
Generate a talking avatar video from a portrait image and an audio file. The AI Avatar feature animates a face to match spoken audio, producing natural lip-sync and head movements.
Create an AI Avatar task:
curl -X POST "https://runapi.ai/api/v1/kling/ai_avatar" \
-H "Authorization: Bearer {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-ai-avatar-pro",
"source_image_url": "https://cdn.runapi.ai/public/samples/portrait.jpg",
"source_audio_url": "https://cdn.runapi.ai/public/samples/voice.mp3",
"prompt": "A professional presenter speaking confidently",
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/kling/ai_avatar')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer {{API_KEY}}'
request['Content-Type'] = 'application/json'
request.body = {
model: 'kling-ai-avatar-pro',
source_image_url: 'https://cdn.runapi.ai/public/samples/portrait.jpg',
source_audio_url: 'https://cdn.runapi.ai/public/samples/voice.mp3',
prompt: 'A professional presenter speaking confidently',
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/kling/ai_avatar'
headers = {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
}
data = {
'model': 'kling-ai-avatar-pro',
'source_image_url': 'https://cdn.runapi.ai/public/samples/portrait.jpg',
'source_audio_url': 'https://cdn.runapi.ai/public/samples/voice.mp3',
'prompt': 'A professional presenter speaking confidently',
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'kling-ai-avatar-pro',
source_image_url: 'https://cdn.runapi.ai/public/samples/portrait.jpg',
source_audio_url: 'https://cdn.runapi.ai/public/samples/voice.mp3',
prompt: 'A professional presenter speaking confidently',
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/kling/ai_avatar', {
method: 'POST',
headers: {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/kling/ai_avatar
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
kling-ai-avatar-pro, kling-ai-avatar-standard, kling-ai-avatar-v1-pro, or kling-v1-avatar-standard
|
source_image_url |
string | Yes | Portrait image URL. Supported formats: JPEG or PNG. Max 10 MB |
source_audio_url |
string | Yes | Audio file URL. Supported formats: MP3, WAV, AAC, MP4, OGG. Max 10 MB |
prompt |
string | Yes | Text prompt to guide the avatar animation. Can be empty. Max 5000 characters |
callback_url |
string | No | HTTPS callback URL for completion events |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial async status, typically processing
|
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/avatar-video.mp4"
}
]
}
Failed callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
Get AI Avatar Status
Check the status of an AI Avatar task:
curl "https://runapi.ai/api/v1/kling/ai_avatar/YOUR_TASK_ID" \
-H "Authorization: Bearer {{API_KEY}}"
task_id = 'YOUR_TASK_ID'
uri = URI("https://runapi.ai/api/v1/kling/ai_avatar/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer {{API_KEY}}'
response = http.request(request)
puts response.body
task_id = 'YOUR_TASK_ID'
response = requests.get(
f'https://runapi.ai/api/v1/kling/ai_avatar/{task_id}',
headers={'Authorization': 'Bearer {{API_KEY}}'}
)
print(response.json())
const taskId = 'YOUR_TASK_ID';
fetch(`https://runapi.ai/api/v1/kling/ai_avatar/${taskId}`, {
headers: { 'Authorization': 'Bearer {{API_KEY}}' }
})
.then(res => res.json())
.then(data => console.log(data));
Completed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/avatar-video.mp4"
}
]
}
Failed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
HTTP Request
GET https://runapi.ai/api/v1/kling/ai_avatar/:id
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Task identifier |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video URLs when completed |
error |
string | Error message when generation fails |
Create V2.1 and V2.5 Video
Generate videos with Kling V2.1 and V2.5 models. V2.1 Master supports text-to-video and image-to-video, V2.1 Pro/Standard support image-to-video, and V2.5 Turbo provides fast text-to-video and image-to-video generation.
Create a V2.1 Master text-to-video task:
curl -X POST "https://runapi.ai/api/v1/kling/text_to_video" \
-H "Authorization: Bearer {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v2.1-master-text-to-video",
"prompt": "A cinematic paratrooper scene above a glowing city at sunset",
"duration_seconds": 5,
"aspect_ratio": "16:9",
"negative_prompt": "blur, distorted hands",
"cfg_scale": 0.5,
"callback_url": "https://your-domain.com/api/callback"
}'
Create a V2.1 Pro image-to-video task with final-frame control:
curl -X POST "https://runapi.ai/api/v1/kling/image_to_video" \
-H "Authorization: Bearer {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v2.1-pro",
"prompt": "The camera glides forward as the subject turns toward warm sunlight",
"first_frame_image_url": "https://cdn.runapi.ai/public/samples/first-frame.jpg",
"last_frame_image_url": "https://cdn.runapi.ai/public/samples/last-frame.jpg",
"duration_seconds": 5,
"cfg_scale": 0.5,
"callback_url": "https://your-domain.com/api/callback"
}'
Create a V2.1 Master image-to-video task:
curl -X POST "https://runapi.ai/api/v1/kling/image_to_video" \
-H "Authorization: Bearer {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v2.1-master-image-to-video",
"prompt": "A quiet landscape comes alive with flowing water and drifting clouds",
"first_frame_image_url": "https://cdn.runapi.ai/public/samples/image-to-video.jpg",
"duration_seconds": 5,
"cfg_scale": 0.5,
"callback_url": "https://your-domain.com/api/callback"
}'
Create a V2.5 Turbo text-to-video task:
curl -X POST "https://runapi.ai/api/v1/kling/text_to_video" \
-H "Authorization: Bearer {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v2.5-turbo-text-to-video-pro",
"prompt": "A golden retriever running through a field of wildflowers in slow motion",
"duration_seconds": 5,
"aspect_ratio": "16:9",
"cfg_scale": 0.5,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/kling/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer {{API_KEY}}'
request['Content-Type'] = 'application/json'
request.body = {
model: 'kling-v2.5-turbo-text-to-video-pro',
prompt: 'A golden retriever running through a field of wildflowers in slow motion',
duration_seconds: 5,
aspect_ratio: '16:9',
cfg_scale: 0.5,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/kling/text_to_video'
headers = {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
}
data = {
'model': 'kling-v2.5-turbo-text-to-video-pro',
'prompt': 'A golden retriever running through a field of wildflowers in slow motion',
'duration_seconds': 5,
'aspect_ratio': '16:9',
'cfg_scale': 0.5,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'kling-v2.5-turbo-text-to-video-pro',
prompt: 'A golden retriever running through a field of wildflowers in slow motion',
duration_seconds: 5,
aspect_ratio: '16:9',
cfg_scale: 0.5,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/kling/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
Create a V2.5 Turbo image-to-video task:
curl -X POST "https://runapi.ai/api/v1/kling/image_to_video" \
-H "Authorization: Bearer {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-v2.5-turbo-image-to-video-pro",
"prompt": "The scene comes to life with gentle wind and swaying trees",
"first_frame_image_url": "https://cdn.runapi.ai/public/samples/image-to-video.jpg",
"duration_seconds": 5,
"aspect_ratio": "16:9",
"cfg_scale": 0.5,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/kling/image_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer {{API_KEY}}'
request['Content-Type'] = 'application/json'
request.body = {
model: 'kling-v2.5-turbo-image-to-video-pro',
prompt: 'The scene comes to life with gentle wind and swaying trees',
first_frame_image_url: 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
duration_seconds: 5,
aspect_ratio: '16:9',
cfg_scale: 0.5,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/kling/image_to_video'
headers = {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
}
data = {
'model': 'kling-v2.5-turbo-image-to-video-pro',
'prompt': 'The scene comes to life with gentle wind and swaying trees',
'first_frame_image_url': 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
'duration_seconds': 5,
'aspect_ratio': '16:9',
'cfg_scale': 0.5,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'kling-v2.5-turbo-image-to-video-pro',
prompt: 'The scene comes to life with gentle wind and swaying trees',
first_frame_image_url: 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
duration_seconds: 5,
aspect_ratio: '16:9',
cfg_scale: 0.5,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/kling/image_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/kling/image_to_video
Request Parameters (Text-to-Video)
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
kling-v2.1-master-text-to-video or kling-v2.5-turbo-text-to-video-pro
|
prompt |
string | Yes | Text description of the desired video. Max 5000 characters for V2.1 Master and 2500 for V2.5 Turbo |
duration_seconds |
number | No | Video duration in seconds: 5 or 10. Default 5
|
aspect_ratio |
string | No |
16:9, 9:16, or 1:1
|
negative_prompt |
string | No | What to avoid in the generated video. Max 500 characters for V2.1 Master and 2500 for V2.5 Turbo |
cfg_scale |
number | No | Creativity vs prompt adherence, 0 to 1
|
callback_url |
string | No | HTTPS callback URL for completion events |
Request Parameters (Image-to-Video)
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
kling-v2.1-pro, kling-v2.1-standard, kling-v2.1-master-image-to-video, or kling-v2.5-turbo-image-to-video-pro
|
prompt |
string | Yes | Text description of the desired video. Max 5000 characters for V2.1 and 2500 for V2.5 Turbo |
first_frame_image_url |
string | Yes | Source image URL to animate |
last_frame_image_url |
string | No | Final-frame image URL supported by kling-v2.5-turbo-image-to-video-pro and kling-v2.1-pro
|
duration_seconds |
number | No | Video duration in seconds: 5 or 10. Default 5
|
aspect_ratio |
string | No |
16:9, 9:16, or 1:1
|
negative_prompt |
string | No | What to avoid in the generated video. Max 500 characters |
cfg_scale |
number | No | Creativity vs prompt adherence, 0 to 1
|
callback_url |
string | No | HTTPS callback URL for completion events |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial async status, typically processing
|
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/turbo-video.mp4"
}
]
}
Failed callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
Get V2.1 and V2.5 Video Status
Check the status of a V2.1 or V2.5 task:
curl "https://runapi.ai/api/v1/kling/image_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer {{API_KEY}}"
task_id = 'YOUR_TASK_ID'
uri = URI("https://runapi.ai/api/v1/kling/image_to_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer {{API_KEY}}'
response = http.request(request)
puts response.body
task_id = 'YOUR_TASK_ID'
response = requests.get(
f'https://runapi.ai/api/v1/kling/image_to_video/{task_id}',
headers={'Authorization': 'Bearer {{API_KEY}}'}
)
print(response.json())
const taskId = 'YOUR_TASK_ID';
fetch(`https://runapi.ai/api/v1/kling/image_to_video/${taskId}`, {
headers: { 'Authorization': 'Bearer {{API_KEY}}' }
})
.then(res => res.json())
.then(data => console.log(data));
Completed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/turbo-video.mp4"
}
]
}
Failed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
HTTP Request
GET https://runapi.ai/api/v1/kling/image_to_video/:id
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Task identifier |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video URLs when completed |
error |
string | Error message when generation fails |
Create Motion Control
Transfer motion from a reference video onto a subject image. The Motion Control feature extracts movement patterns from a video and applies them to a static image, producing a new video where the subject in the image performs the movements from the reference video.
Create a Motion Control task:
curl -X POST "https://runapi.ai/api/v1/kling/motion_control" \
-H "Authorization: Bearer {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"model": "kling-3.0",
"source_first_frame_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"reference_video_url": "https://cdn.runapi.ai/public/samples/video.mp4",
"prompt": "A person dancing gracefully in a studio",
"output_resolution": "1080p",
"character_orientation": "video",
"background_source": "video",
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/kling/motion_control')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer {{API_KEY}}'
request['Content-Type'] = 'application/json'
request.body = {
model: 'kling-3.0',
source_first_frame_image_url: 'https://cdn.runapi.ai/public/samples/image.jpg',
reference_video_url: 'https://cdn.runapi.ai/public/samples/video.mp4',
prompt: 'A person dancing gracefully in a studio',
output_resolution: '1080p',
character_orientation: 'video',
background_source: 'video',
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/kling/motion_control'
headers = {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
}
data = {
'model': 'kling-3.0',
'source_first_frame_image_url': 'https://cdn.runapi.ai/public/samples/image.jpg',
'reference_video_url': 'https://cdn.runapi.ai/public/samples/video.mp4',
'prompt': 'A person dancing gracefully in a studio',
'output_resolution': '1080p',
'character_orientation': 'video',
'background_source': 'video',
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'kling-3.0',
source_first_frame_image_url: 'https://cdn.runapi.ai/public/samples/image.jpg',
reference_video_url: 'https://cdn.runapi.ai/public/samples/video.mp4',
prompt: 'A person dancing gracefully in a studio',
output_resolution: '1080p',
character_orientation: 'video',
background_source: 'video',
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/kling/motion_control', {
method: 'POST',
headers: {
'Authorization': 'Bearer {{API_KEY}}',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/kling/motion_control
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be kling-3.0
|
source_first_frame_image_url |
string | Yes | Subject image URL |
reference_video_url |
string | Yes | Reference motion video URL |
prompt |
string | No | Text prompt to guide the generation. Max 2500 characters |
output_resolution |
string | No | Output resolution: 720p or 1080p
|
character_orientation |
string | No | Which source determines the character's orientation: video or image
|
background_source |
string | No | Which source provides the background: video or image
|
callback_url |
string | No | HTTPS callback URL for completion events |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial async status, typically processing
|
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/motion-control-video.mp4"
}
]
}
Failed callback example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
Get Motion Control Status
Check the status of a Motion Control task:
curl "https://runapi.ai/api/v1/kling/motion_control/YOUR_TASK_ID" \
-H "Authorization: Bearer {{API_KEY}}"
task_id = 'YOUR_TASK_ID'
uri = URI("https://runapi.ai/api/v1/kling/motion_control/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer {{API_KEY}}'
response = http.request(request)
puts response.body
task_id = 'YOUR_TASK_ID'
response = requests.get(
f'https://runapi.ai/api/v1/kling/motion_control/{task_id}',
headers={'Authorization': 'Bearer {{API_KEY}}'}
)
print(response.json())
const taskId = 'YOUR_TASK_ID';
fetch(`https://runapi.ai/api/v1/kling/motion_control/${taskId}`, {
headers: { 'Authorization': 'Bearer {{API_KEY}}' }
})
.then(res => res.json())
.then(data => console.log(data));
Completed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/motion-control-video.mp4"
}
]
}
Failed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
HTTP Request
GET https://runapi.ai/api/v1/kling/motion_control/:id
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Task identifier |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video URLs when completed |
error |
string | Error message when generation fails |
Suno
The Suno API provides comprehensive music generation and processing capabilities.
Base Endpoint: https://runapi.ai/api/v1/suno
Suno Endpoints
| Category | Endpoints | Use Case |
|---|---|---|
| Core | Generate Music, Generate Lyrics | Create new music or lyrics from scratch |
| Extend & Edit | Extend Music, Replace Section | Modify existing tracks |
| Audio Layers | Add Vocals, Add Instrumental | Layer vocals or instruments |
| Audio Separation | Vocal Separation, MIDI Generation | Split audio into components |
| Visual | Music Cover, Music Video | Generate cover art or videos |
| Audio Transformation | Upload and Covers, Upload and Extensions, Mashup | Process your own audio files |
| Sound Design | Generate Sound | Ambient, loops, sound effects |
| Voice | Voice Validation Phrase, Regenerate Validation Phrase, Generate Voice, Check Voice | Prepare verification phrases and reusable custom voices |
Model Versions
| Model | Description |
|---|---|
suno-v5.5 |
Custom models tailored to your taste |
suno-v5 |
Superior musical expression, faster generation, max 8 min |
suno-v4.5-plus |
Richer sound with new creative tools, max 8 min |
suno-v4.5-all |
Smarter prompts, faster generations, max 8 min |
suno-v4.5 |
Smarter prompts, faster generations, max 8 min |
suno-v4 |
Improved vocal quality, max 4 min |
Common Parameters
These parameters appear across most generation endpoints:
| Parameter | Type | Description |
|---|---|---|
model |
string | Model version (suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4) |
callback_url |
string | URL for completion notifications |
style_weight |
number | Style adherence (0-1, higher = stricter) |
weirdness_constraint |
number | Creativity level (0-1, higher = more experimental) |
audio_weight |
number | Audio consistency (0-1, higher = more consistent) |
vocal_gender |
string | Preferred voice: male or female. Increases probability only. |
Common Error Codes
| Status Code | Error Message | Description |
|---|---|---|
| 400 | SENSITIVE_WORD_ERROR | Prompt contains sensitive words. Please modify the prompt and try again. |
| 402 | INSUFFICIENT_QUOTA | Insufficient account balance. Please recharge. |
| 408 | TIMEOUT | Task timed out. Please try again later. |
| 429 | RATE_LIMIT_EXCEEDED | Rate limit exceeded. Please try again later. |
| 500 | SERVER_ERROR | Internal server error. Please try again later. |
| 531 | REFUNDED | Task failed and has been automatically refunded. Please try again. |
Generate Music
Create a music generation task:
curl -X POST "https://runapi.ai/api/v1/suno/text_to_music" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"vocal_mode": "auto_lyrics",
"prompt": "A chill lo-fi beat with soft vocals",
"model": "suno-v5",
"callback_url": "https://your-app.com/callbacks/suno"
}'
require 'net/http'
require 'json'
uri = URI('https://runapi.ai/api/v1/suno/text_to_music')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
vocal_mode: "auto_lyrics",
prompt: "A chill lo-fi beat with soft vocals",
model: "suno-v5",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
response = http.request(request)
import requests
url = "https://runapi.ai/api/v1/suno/text_to_music"
headers = {
"Authorization": "Bearer YOUR_API_TOKEN",
"Content-Type": "application/json"
}
data = {
"vocal_mode": "auto_lyrics",
"prompt": "A chill lo-fi beat with soft vocals",
"model": "suno-v5",
"callback_url": "https://your-app.com/callbacks/suno"
}
response = requests.post(url, headers=headers, json=data)
fetch('https://runapi.ai/api/v1/suno/text_to_music', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
vocal_mode: "auto_lyrics",
prompt: "A chill lo-fi beat with soft vocals",
model: "suno-v5",
callback_url: "https://your-app.com/callbacks/suno"
})
});
Generate original music from text descriptions.
HTTP Request
POST https://runapi.ai/api/v1/suno/text_to_music
Request Body Parameters
Request Shapes
Choose vocal_mode. Add model and optional callback_url to each request.
-
auto_lyrics: put the song brief inprompt. -
exact_lyrics: put the sung words inlyricsand providestyleandtitle. -
instrumental: providestyleandtitle.
| Parameter | Type | Required | Description |
|---|---|---|---|
vocal_mode |
string | Yes |
auto_lyrics, exact_lyrics, or instrumental. |
prompt |
string | No | Song brief for automatic lyrics. |
lyrics |
string | No | Exact sung lyrics. |
style |
string | No | Music style. |
title |
string | No | Music title. |
model |
string | Yes | Model version: suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4
|
callback_url |
string | No | URL to receive completion notifications |
persona_id |
string | No | Persona ID to apply. Create one via Generate Persona. |
persona_type |
string | No | Persona type, used with persona_id. Values: style for style characteristics, voice for voice characteristics (suno-v5 only). Ignored without persona_id. |
negative_tags |
string | No | Music styles to exclude |
vocal_gender |
string | No | Preferred vocal gender: male or female. Increases probability only. |
style_weight |
number | No | Style adherence weight (0-1, two decimals) |
weirdness_constraint |
number | No | Creativity/novelty constraint (0-1, two decimals) |
audio_weight |
number | No | Audio consistency weight (0-1, two decimals) |
Response
Returns the task ID for tracking generation status.
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
If callback_url is provided, a POST request will be sent at multiple stages during generation:
- text_generated: All text content ready (title, lyrics, tags, etc.)
- first_audio_ready: First audio track completed
- all_audios_ready: All audios completed
- failed: Generation failed
Success Response:
- Includes audios array with generated music details
- Contains track details like title, tags, duration
- Each track includes audio_url, stream_audio_url, and image_url
Failed Response:
- Contains error message describing the failure reason
Success callback example:
{
"id": "task-id",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "audio-id-1",
"audio_url": "https://file.runapi.ai/audio1.mp3",
"stream_audio_url": "https://file.runapi.ai/stream1",
"image_url": "https://file.runapi.ai/cover1.jpg",
"lyrics": "[Verse] Lyrics...",
"model_name": "chirp-v3-5",
"title": "Song Title",
"tags": ["genre1", "genre2"],
"duration": 198.44
}
]
}
Failed callback example:
{
"id": "task-id",
"status": "failed",
"generation_stage": "failed",
"error": "Error message"
}
Get Music Generation Details
Query music generation status:
curl "https://runapi.ai/api/v1/suno/text_to_music/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/text_to_music/TASK_ID')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
url = "https://runapi.ai/api/v1/suno/text_to_music/TASK_ID"
headers = {"Authorization": "Bearer YOUR_API_TOKEN"}
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/text_to_music/TASK_ID', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
});
Get detailed information about a music generation task.
HTTP Request
GET https://runapi.ai/api/v1/suno/text_to_music/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the generate endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing",
"generation_stage": "text_generated"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] 夜晚城市 灯火辉煌",
"model_name": "chirp-v3-5",
"title": "钢铁侠",
"tags": ["electrifying", "rock"],
"duration": 198.44
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Generation failed"
}
Extend Music
Extend an existing music track:
curl -X POST "https://runapi.ai/api/v1/suno/extend_music" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"parameter_mode": "custom",
"audio_id": "2902e564-9001-427c-a6fa-585b86a864fe",
"prompt": "Extend the music with more relaxing notes",
"style": "Classical",
"title": "Peaceful Piano Extended",
"continue_at": 60,
"model": "suno-v4",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
parameter_mode: "custom",
audio_id: "2902e564-9001-427c-a6fa-585b86a864fe",
prompt: "Extend the music with more relaxing notes",
style: "Classical",
title: "Peaceful Piano Extended",
continue_at: 60,
model: "suno-v4",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"parameter_mode": "custom",
"audio_id": "2902e564-9001-427c-a6fa-585b86a864fe",
"prompt": "Extend the music with more relaxing notes",
"style": "Classical",
"title": "Peaceful Piano Extended",
"continue_at": 60,
"model": "suno-v4",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
parameter_mode: "custom",
audio_id: "2902e564-9001-427c-a6fa-585b86a864fe",
prompt: "Extend the music with more relaxing notes",
style: "Classical",
title: "Peaceful Piano Extended",
continue_at: 60,
model: "suno-v4",
callback_url: "https://your-app.com/callbacks/suno"
})
Extend or modify existing music audios while maintaining the original style.
HTTP Request
POST https://runapi.ai/api/v1/suno/extend_music
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
parameter_mode |
string | Yes |
source to inherit source parameters, or custom to provide parameters in this request |
audio_id |
string | Yes | Audio ID of the track to extend |
callback_url |
string | No | URL to receive task completion notifications |
model |
string | Yes | Model version: suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4
|
prompt |
string | Conditional | Description of how to extend (required when parameter_mode is custom) |
style |
string | Conditional | Music style (required when parameter_mode is custom) |
title |
string | Conditional | Music title (required when parameter_mode is custom) |
continue_at |
number | Conditional | Start time in seconds (required when parameter_mode is custom) |
persona_id |
string | No | Persona ID to apply when parameter_mode is custom. Create one via Generate Persona. |
persona_type |
string | No | Persona type, used with persona_id. Values: style for style characteristics, voice for voice characteristics (suno-v5 only). Ignored without persona_id. |
negative_tags |
string | No | Music styles to exclude |
vocal_gender |
string | No | Preferred vocal gender: male or female. Only works with parameter_mode: "custom". Increases probability only. |
style_weight |
number | No | Style adherence weight (0-1) |
weirdness_constraint |
number | No | Creativity constraint (0-1) |
audio_weight |
number | No | Audio consistency weight (0-1) |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
If callback_url is provided, a POST request will be sent at multiple stages.
Success Response: - Includes extended track URLs - Contains metadata like title, tags, duration - Extended audios maintain style consistency with source
Success callback example:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Night city lights shining bright",
"model_name": "chirp-v3-5",
"title": "Iron Man Extended",
"tags": ["electrifying", "rock"],
"duration": 278.44
}
]
}
Failed callback example:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Failed to extend music, invalid audio source"
}
Get Music Extension Details
Query extension status:
curl "https://runapi.ai/api/v1/suno/extend_music/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/extend_music/TASK_ID')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
url = "https://runapi.ai/api/v1/suno/extend_music/TASK_ID"
headers = {"Authorization": "Bearer YOUR_API_TOKEN"}
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/extend_music/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a music extension task.
HTTP Request
GET https://runapi.ai/api/v1/suno/extend_music/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned when you submitted the extension request |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Night city lights shining bright",
"model_name": "chirp-v3-5",
"title": "Iron Man Extended",
"tags": ["electrifying", "rock"],
"duration": 278.44
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Extension generation failed."
}
Generate Lyrics
Create a lyrics generation task:
curl -X POST "https://runapi.ai/api/v1/suno/generate_lyrics" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A song about peaceful night in the city",
"callback_url": "https://your-app.com/callbacks/suno-lyrics"
}'
request.body = {
prompt: "A song about peaceful night in the city",
callback_url: "https://your-app.com/callbacks/suno-lyrics"
}.to_json
data = {
"prompt": "A song about peaceful night in the city",
"callback_url": "https://your-app.com/callbacks/suno-lyrics"
}
body: JSON.stringify({
prompt: "A song about peaceful night in the city",
callback_url: "https://your-app.com/callbacks/suno-lyrics"
})
Generate creative lyrics content based on a text prompt.
HTTP Request
POST https://runapi.ai/api/v1/suno/generate_lyrics
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Theme, style, or subject of the desired lyrics. Max 200 characters |
callback_url |
string | No | Publicly accessible URL to receive completion callback |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
When the lyrics generation task finishes, the system will POST to your callback_url.
Success Response:
- Contains lyrics array with generated content
- Includes title and structured text
- Returns one or more lyric entries with verse/chorus sections
Success callback example:
{
"id": "277a0b89-cd57-455e-ba43-7c6532ca99ed",
"status": "completed",
"lyrics": [
{
"title": "Starry Night Dreams",
"text": "[Verse]\nMoonlight spreads across the windowsill..."
}
]
}
Failed callback example:
{
"id": "277a0b89-cd57-455e-ba43-7c6532ca99ed",
"status": "failed",
"error": "Song Description flagged for moderation"
}
Get Lyrics Generation Details
Query lyrics generation status:
curl "https://runapi.ai/api/v1/suno/generate_lyrics/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/generate_lyrics/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/generate_lyrics/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/generate_lyrics/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Retrieve the status of a lyrics generation task.
HTTP Request
GET https://runapi.ai/api/v1/suno/generate_lyrics/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
Task ID returned from POST /lyrics |
Response
Processing:
{
"id": "277a0b89-cd57-455e-ba43-7c6532ca99ed",
"status": "processing",
"lyrics": []
}
Completed:
{
"id": "277a0b89-cd57-455e-ba43-7c6532ca99ed",
"status": "completed",
"lyrics": [
{
"title": "Starry Night Dreams",
"text": "[Verse]\nMoonlight spreads across the windowsill..."
}
]
}
Failed:
{
"id": "277a0b89-cd57-455e-ba43-7c6532ca99ed",
"status": "failed",
"error": "Lyrics generation failed.",
"lyrics": []
}
Add Vocals
Add vocals to an instrumental track:
curl -X POST "https://runapi.ai/api/v1/suno/add_vocals" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"lyrics": "[Verse]\nA calm and relaxing piano track with soothing vocals",
"title": "Relaxing Piano with Vocals",
"style": "Jazz",
"model": "suno-v4.5-plus",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
lyrics: "[Verse]\nA calm and relaxing piano track with soothing vocals",
title: "Relaxing Piano with Vocals",
style: "Jazz",
model: "suno-v4.5-plus",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"lyrics": "[Verse]\nA calm and relaxing piano track with soothing vocals",
"title": "Relaxing Piano with Vocals",
"style": "Jazz",
"model": "suno-v4.5-plus",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
lyrics: "[Verse]\nA calm and relaxing piano track with soothing vocals",
title: "Relaxing Piano with Vocals",
style: "Jazz",
model: "suno-v4.5-plus",
callback_url: "https://your-app.com/callbacks/suno"
})
Add vocals to an uploaded audio file.
HTTP Request
POST https://runapi.ai/api/v1/suno/add_vocals
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
upload_url |
string | Yes | URL of the uploaded music file. You can use the download_url returned by POST /files/from_url; that temporary link expires after 24 hours. |
lyrics |
string | Yes | Exact vocal lyrics |
title |
string | Yes | Title of the music track |
style |
string | Yes | Music style/genre |
model |
string | Yes | Model version: suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4
|
callback_url |
string | No | URL to receive completion notifications |
negative_tags |
string | No | Styles to exclude |
vocal_gender |
string | No | Preferred vocal gender: male or female. Increases probability only. |
style_weight |
number | No | Style adherence weight (0-1) |
weirdness_constraint |
number | No | Creativity constraint (0-1) |
audio_weight |
number | No | Audio consistency weight (0-1) |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
The system will POST to your callback_url at multiple stages.
Success callback example:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/audio.mp3",
"stream_audio_url": "https://file.runapi.ai/stream",
"image_url": "https://file.runapi.ai/cover.jpg",
"lyrics": "[Verse] Soothing melody with calm vocals",
"model_name": "chirp-v4-5",
"title": "Relaxing Piano with Vocals",
"tags": ["jazz", "calm", "soothing"],
"duration": 182.32
}
]
}
Failed callback example:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Vocal generation failed."
}
Get Add Vocals Details
Query vocals task status:
curl "https://runapi.ai/api/v1/suno/add_vocals/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/add_vocals/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/add_vocals/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/add_vocals/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Get detailed information about an add vocals task.
HTTP Request
GET https://runapi.ai/api/v1/suno/add_vocals/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the add vocals endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Soothing melody with calm vocals",
"model_name": "chirp-v4-5",
"title": "Relaxing Piano with Vocals",
"tags": ["jazz", "calm", "soothing"],
"duration": 182.32
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Vocal generation failed."
}
Add Instrumental
Add instrumental to a track:
curl -X POST "https://runapi.ai/api/v1/suno/add_instrumental" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"title": "Relaxing Piano",
"tags": "Relaxing Piano, Ambient, Peaceful",
"negative_tags": "Heavy Metal, Aggressive Drums",
"model": "suno-v4.5-plus",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
title: "Relaxing Piano",
tags: "Relaxing Piano, Ambient, Peaceful",
negative_tags: "Heavy Metal, Aggressive Drums",
model: "suno-v4.5-plus",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"title": "Relaxing Piano",
"tags": "Relaxing Piano, Ambient, Peaceful",
"negative_tags": "Heavy Metal, Aggressive Drums",
"model": "suno-v4.5-plus",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
title: "Relaxing Piano",
tags: "Relaxing Piano, Ambient, Peaceful",
negative_tags: "Heavy Metal, Aggressive Drums",
model: "suno-v4.5-plus",
callback_url: "https://your-app.com/callbacks/suno"
})
Generate musical accompaniment tailored to an uploaded audio file.
HTTP Request
POST https://runapi.ai/api/v1/suno/add_instrumental
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
upload_url |
string | Yes | URL of the uploaded music file. You can use the download_url returned by POST /files/from_url; that temporary link expires after 24 hours. |
title |
string | Yes | Title of the music track |
tags |
string | Yes | Music style and characteristics |
negative_tags |
string | Yes | Music styles to exclude |
model |
string | Yes | Model version: suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4
|
callback_url |
string | No | URL to receive completion notifications |
vocal_gender |
string | No | Preferred vocal gender: male or female. Increases probability only. |
style_weight |
number | No | Style adherence weight (0-1) |
weirdness_constraint |
number | No | Creativity constraint (0-1) |
audio_weight |
number | No | Audio consistency weight (0-1) |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback example:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Relaxing piano melody flows",
"model_name": "chirp-v4-5-plus",
"title": "Relaxing Piano",
"tags": ["Relaxing Piano", "Ambient", "Peaceful"],
"duration": 182.32
}
]
}
Failed callback example:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Instrumental generation failed."
}
Get Add Instrumental Details
Query instrumental task status:
curl "https://runapi.ai/api/v1/suno/add_instrumental/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/add_instrumental/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/add_instrumental/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/add_instrumental/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of an add instrumental task.
HTTP Request
GET https://runapi.ai/api/v1/suno/add_instrumental/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned when you submitted the request |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Relaxing piano melody flows",
"model_name": "chirp-v4-5-plus",
"title": "Relaxing Piano",
"tags": ["Relaxing Piano", "Ambient", "Peaceful"],
"duration": 182.32
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Instrumental generation failed."
}
Vocal & Instrument Separation
Separate vocals and instruments:
curl -X POST "https://runapi.ai/api/v1/suno/separate_audio_stems" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"type": "separate_vocal",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
type: "separate_vocal",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"type": "separate_vocal",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
type: "separate_vocal",
callback_url: "https://your-app.com/callbacks/suno"
})
Separate music into vocal, instrumental, and individual instrument tracks.
HTTP Request
POST https://runapi.ai/api/v1/suno/separate_audio_stems
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | The task ID of the music generation task |
audio_id |
string | Yes | The audio ID of the specific track to separate |
type |
string | No | Separation type: separate_vocal (default) or split_stem. See Suno pricing. |
callback_url |
string | No | URL to receive completion notification |
Separation Types:
- separate_vocal: Separates vocals and accompaniment (generates 2 tracks)
- split_stem: Separates individual instruments (generates 12+ tracks)
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
The system will POST to your callback_url when separation completes.
Success Response (separate_vocal):
- Contains instrumental_url and vocal_url
- Clean separation between vocals and backing music
Success Response (split_stem): - Contains individual instrument URLs - Includes backing_vocals, bass, brass, drums, fx, guitar, keyboard, percussion, strings, synth, vocal, woodwinds
Success callback (separate_vocal):
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"separated_audios": {
"instrumental_url": "https://file.runapi.ai/s/d92a****5528_Instrumental.mp3",
"vocal_url": "https://file.runapi.ai/s/3d70****b172_Vocals.mp3"
}
}
Success callback (split_stem):
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"separated_audios": {
"backing_vocals_url": "https://file.runapi.ai/s/aadc****673d_Backing_Vocals.mp3",
"bass_url": "https://file.runapi.ai/s/a3c2****26d33_Bass.mp3",
"brass_url": "https://file.runapi.ai/s/334b****fdd44_Brass.mp3",
"drums_url": "https://file.runapi.ai/s/ac75****78e44_Drums.mp3",
"fx_url": "https://file.runapi.ai/s/a882****007d_FX.mp3",
"guitar_url": "https://file.runapi.ai/s/064d****95b4_Guitar.mp3",
"keyboard_url": "https://file.runapi.ai/s/adc9****d74e0_Keyboard.mp3",
"piano_url": "https://file.runapi.ai/s/b1c2****3d4e_Piano.mp3",
"percussion_url": "https://file.runapi.ai/s/0f70****618b7dc6_Percussion.mp3",
"strings_url": "https://file.runapi.ai/s/4982****6b_Strings.mp3",
"synth_url": "https://file.runapi.ai/s/56b2****8348_Synth.mp3",
"vocal_url": "https://file.runapi.ai/s/0742****90ccb_Vocals.mp3",
"woodwinds_url": "https://file.runapi.ai/s/d815****abb02_Woodwinds.mp3"
}
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Vocal separation failed."
}
Get Vocal Separation Details
Query vocal separation status:
curl "https://runapi.ai/api/v1/suno/separate_audio_stems/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/separate_audio_stems/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/separate_audio_stems/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/separate_audio_stems/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a vocal separation task.
HTTP Request
GET https://runapi.ai/api/v1/suno/separate_audio_stems/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the vocal removal endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed (separate_vocal):
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"separated_audios": {
"instrumental_url": "https://file.runapi.ai/s/d92a****5528_Instrumental.mp3",
"vocal_url": "https://file.runapi.ai/s/3d70****b172_Vocals.mp3"
}
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Vocal separation failed."
}
Convert to WAV Format
Convert audio to WAV format:
curl -X POST "https://runapi.ai/api/v1/suno/convert_audio" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
callback_url: "https://your-app.com/callbacks/suno"
})
Convert existing music audios to high-quality WAV format.
HTTP Request
POST https://runapi.ai/api/v1/suno/convert_audio
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | The task ID of the music generation task |
audio_id |
string | Yes | The audio ID of the track to convert |
callback_url |
string | No | URL to receive completion notification |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"wav_url": "https://file.runapi.ai/s/04e6****e727.wav"
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "WAV conversion failed."
}
Get WAV Conversion Details
Query WAV conversion status:
curl "https://runapi.ai/api/v1/suno/convert_audio/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/convert_audio/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/convert_audio/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/convert_audio/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a WAV conversion task.
HTTP Request
GET https://runapi.ai/api/v1/suno/convert_audio/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the WAV generate endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"wav_url": "https://file.runapi.ai/s/04e6****e727.wav"
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "WAV conversion failed."
}
Generate MIDI from Audio
Generate MIDI from an audio file:
curl -X POST "https://runapi.ai/api/v1/suno/generate_midi" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
callback_url: "https://your-app.com/callbacks/suno"
})
Convert a separated audio track into MIDI format with detailed note information. Requires a task ID from a previous split_stem vocal separation task.
HTTP Request
POST https://runapi.ai/api/v1/suno/generate_midi
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | The task ID of a completed stem separation task |
callback_url |
string | No | URL to receive completion notification |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success Response:
- Contains instruments array with detected instruments
- Each instrument includes note sequence data
- Notes include pitch (MIDI 0-127), start/end times, velocity
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"instruments": [
{
"name": "Drums",
"notes": [
{
"pitch": 73,
"start_time": 0.036458333333333336,
"end_time": 0.18229166666666666,
"velocity": 1
}
]
},
{
"name": "Electric Bass (finger)",
"notes": [
{
"pitch": 44,
"start_time": 7.6875,
"end_time": 7.911458333333333,
"velocity": 1
}
]
}
]
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "MIDI generation failed."
}
Get MIDI Details
Query MIDI status:
curl "https://runapi.ai/api/v1/suno/generate_midi/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/generate_midi/TASK_ID')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
url = "https://runapi.ai/api/v1/suno/generate_midi/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/generate_midi/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a MIDI task.
HTTP Request
GET https://runapi.ai/api/v1/suno/generate_midi/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the MIDI endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing",
"instruments": []
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"instruments": [
{
"name": "Drums",
"notes": [
{
"pitch": 73,
"start_time": 0.036458333333333336,
"end_time": 0.18229166666666666,
"velocity": 1
}
]
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "MIDI generation failed.",
"instruments": []
}
Voice Validation Phrase
Generate a validation phrase from a source voice segment:
curl -X POST "https://runapi.ai/api/v1/suno/voice_to_validation_phrase" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"voice_url": "https://file.runapi.ai/source-vocal.mp3",
"vocal_start_seconds": 2,
"vocal_end_seconds": 12,
"language": "en",
"callback_url": "https://your-app.com/callbacks/suno-voice"
}'
Create a phrase the user can read for a later voice verification step. The vocal window must be an integer second range where vocal_end_seconds is greater than vocal_start_seconds.
HTTP Request
POST https://runapi.ai/api/v1/suno/voice_to_validation_phrase
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
voice_url |
string | Yes | Public URL of the source voice recording |
vocal_start_seconds |
integer | Yes | Start time of the vocal segment in seconds |
vocal_end_seconds |
integer | Yes | End time of the vocal segment in seconds; must be greater than vocal_start_seconds
|
language |
string | No | Phrase language: en, zh, es, fr, pt, de, ja, ko, hi, or ru
|
callback_url |
string | No | HTTPS callback URL |
Response
HTTP 202 - Task accepted:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Callback Format
Phrase ready:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"provider_status": "wait_validating",
"validation_phrase": "Harmonies fill the air with joyful melodies tonight"
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"provider_status": "processing_validate_fail",
"error": "Validation phrase generation failed."
}
Get Voice Validation Phrase Details
Query validation phrase status:
curl "https://runapi.ai/api/v1/suno/voice_to_validation_phrase/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Query the status and phrase returned by a validation phrase task.
HTTP Request
GET https://runapi.ai/api/v1/suno/voice_to_validation_phrase/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the voice validation phrase endpoint |
Response
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"provider_status": "wait_validating",
"validation_phrase": "Harmonies fill the air with joyful melodies tonight"
}
Regenerate Validation Phrase
Generate a new validation phrase for a previous validation phrase task:
curl -X POST "https://runapi.ai/api/v1/suno/regenerate_validation_phrase" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"callback_url": "https://your-app.com/callbacks/suno-voice"
}'
Use this endpoint when the previous phrase expired, was hard to read, or needs to be replaced before the user records verification audio. task_id must reference a prior Suno validation phrase task owned by the same account.
HTTP Request
POST https://runapi.ai/api/v1/suno/regenerate_validation_phrase
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | Task ID from Voice Validation Phrase or an earlier regeneration |
callback_url |
string | No | HTTPS callback URL |
Response
HTTP 202 - Task accepted:
{
"id": "04ab2c8d-15b4-4b77-833f-9a462ce02ef6",
"status": "processing"
}
Callback Format
Phrase ready:
{
"id": "04ab2c8d-15b4-4b77-833f-9a462ce02ef6",
"status": "completed",
"provider_status": "wait_validating",
"validation_phrase": "Silver echoes drift across the midnight ocean"
}
Get Regenerated Validation Phrase Details
Query regenerated validation phrase status:
curl "https://runapi.ai/api/v1/suno/regenerate_validation_phrase/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
HTTP Request
GET https://runapi.ai/api/v1/suno/regenerate_validation_phrase/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the regenerate validation phrase endpoint |
Response
Completed:
{
"id": "04ab2c8d-15b4-4b77-833f-9a462ce02ef6",
"status": "completed",
"provider_status": "wait_validating",
"validation_phrase": "Silver echoes drift across the midnight ocean"
}
Generate Voice
Create a reusable custom voice from a validation phrase task:
curl -X POST "https://runapi.ai/api/v1/suno/generate_voice" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"verify_url": "https://file.runapi.ai/verify-read.mp3",
"voice_name": "Warm Test Voice",
"description": "Warm vocal identity",
"style": "Pop, Female Vocal",
"singer_skill_level": "advanced",
"callback_url": "https://your-app.com/callbacks/suno-voice"
}'
Create a custom voice from a prior validation phrase task and a recording of the user reading that phrase. A completed response includes voice_id; use it as persona_id with persona_type: "voice" on supported Suno v5 music generation endpoints.
HTTP Request
POST https://runapi.ai/api/v1/suno/generate_voice
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | Task ID from Voice Validation Phrase or Regenerate Validation Phrase |
verify_url |
string | Yes | Public URL of the user's recording of the validation phrase |
voice_name |
string | No | Custom voice display name |
description |
string | No | Custom voice description |
style |
string | No | Voice style tags |
singer_skill_level |
string | No | Singer skill level: beginner, intermediate, advanced, or professional
|
callback_url |
string | No | HTTPS callback URL |
Response
HTTP 202 - Task accepted:
{
"id": "05bd3d12-92e6-4e8d-98f4-90ae86dfe6f1",
"status": "processing"
}
Callback Format
Voice ready:
{
"id": "05bd3d12-92e6-4e8d-98f4-90ae86dfe6f1",
"status": "completed",
"provider_status": "success",
"voice_id": "voice_custom_123"
}
Failed:
{
"id": "05bd3d12-92e6-4e8d-98f4-90ae86dfe6f1",
"status": "failed",
"provider_status": "fail",
"error": "Custom voice generation failed."
}
Get Generate Voice Details
Query custom voice generation status:
curl "https://runapi.ai/api/v1/suno/generate_voice/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Query the status and voice_id returned by a custom voice task.
HTTP Request
GET https://runapi.ai/api/v1/suno/generate_voice/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned by Generate Voice |
Response
Completed:
{
"id": "05bd3d12-92e6-4e8d-98f4-90ae86dfe6f1",
"status": "completed",
"provider_status": "success",
"voice_id": "voice_custom_123"
}
Check Voice
Confirm that a generated custom voice is available:
curl -X POST "https://runapi.ai/api/v1/suno/check_voice" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "05bd3d12-92e6-4e8d-98f4-90ae86dfe6f1"
}'
Check whether a completed Generate Voice task is available before using its voice_id as persona_id with persona_type: "voice".
HTTP Request
POST https://runapi.ai/api/v1/suno/check_voice
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | Task ID from Generate Voice |
Response
Available:
{
"is_available": true
}
Generate Music Cover
Create a music cover image:
curl -X POST "https://runapi.ai/api/v1/suno/generate_artwork" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
callback_url: "https://your-app.com/callbacks/suno"
})
Create personalized cover images for generated music.
HTTP Request
POST https://runapi.ai/api/v1/suno/generate_artwork
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | Original music task ID from music generation endpoint |
callback_url |
string | No | URL to receive completion notifications |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success Response:
- Contains covers array with generated cover images
- Usually generates 2 different style cover images
- Cover URLs are retained for 14 days
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"covers": [
{
"url": "https://tempfile.runapi.ai/s/1753958521_6c1b3015141849d1a9bf17b738ce9347.png"
},
{
"url": "https://tempfile.runapi.ai/s/1753958524_c153143acc6340908431cf0e90cbce9e.png"
}
]
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Cover generation failed.",
"covers": []
}
Get Music Cover
Query music cover status:
curl "https://runapi.ai/api/v1/suno/generate_artwork/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/generate_artwork/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/generate_artwork/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/generate_artwork/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and results of a music cover generation task.
HTTP Request
GET https://runapi.ai/api/v1/suno/generate_artwork/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
Task ID returned from the Generate Music Cover endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing",
"covers": []
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"covers": [
{
"url": "https://tempfile.runapi.ai/s/1753958521_6c1b3015141849d1a9bf17b738ce9347.png"
},
{
"url": "https://tempfile.runapi.ai/s/1753958524_c153143acc6340908431cf0e90cbce9e.png"
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Cover generation failed.",
"covers": []
}
Create Music Video
Create a music video:
curl -X POST "https://runapi.ai/api/v1/suno/visualize_music" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"callback_url": "https://your-app.com/callbacks/suno",
"author": "Suno Artist",
"domain_name": "music.example.com"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
callback_url: "https://your-app.com/callbacks/suno",
author: "Suno Artist",
domain_name: "music.example.com"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"callback_url": "https://your-app.com/callbacks/suno",
"author": "Suno Artist",
"domain_name": "music.example.com"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
callback_url: "https://your-app.com/callbacks/suno",
author: "Suno Artist",
domain_name: "music.example.com"
})
Generate an MP4 video with visualizations for a music track.
HTTP Request
POST https://runapi.ai/api/v1/suno/visualize_music
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | The task ID of the music generation task |
audio_id |
string | Yes | The ID of the specific track to convert to video |
callback_url |
string | No | URL to receive completion notification |
author |
string | No | Artist/creator name to display on video (max 50 chars) |
domain_name |
string | No | Website/brand to display as watermark (max 50 chars) |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"video_url": "https://file.runapi.ai/videos/video_847715e66259.mp4"
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Video generation failed."
}
Get Music Video Details
Query music video status:
curl "https://runapi.ai/api/v1/suno/visualize_music/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/visualize_music/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/visualize_music/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/visualize_music/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a music video generation task.
HTTP Request
GET https://runapi.ai/api/v1/suno/visualize_music/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the music video generation endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"video_url": "https://file.runapi.ai/videos/video_847715e66259.mp4"
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Video generation failed."
}
Boost Music Style
Enhance music style description:
curl -X POST "https://runapi.ai/api/v1/suno/boost_style" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"description": "Pop, Mysterious"
}'
request.body = {
description: "Pop, Mysterious"
}.to_json
data = {"description": "Pop, Mysterious"}
body: JSON.stringify({description: "Pop, Mysterious"})
Generate enhanced music style descriptions for more detailed prompts.
HTTP Request
POST https://runapi.ai/api/v1/suno/boost_style
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
description |
string | Yes | Concise style keywords or brief description |
Response
Returns the enhanced style description immediately (synchronous response).
{
"style": "Pop, Mysterious, Upbeat, Electronic, Synth-driven, Catchy hooks"
}
Get Timestamped Lyrics
Get timestamped lyrics:
curl -X POST "https://runapi.ai/api/v1/suno/get_timestamped_lyrics" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "b7e7c624-c244-4ba7-a9f7-7d85da73cf03",
"audio_id": "0aac6db8-cbe9-4af2-aea6-d7aa7d3a2355"
}'
request.body = {
task_id: "b7e7c624-c244-4ba7-a9f7-7d85da73cf03",
audio_id: "0aac6db8-cbe9-4af2-aea6-d7aa7d3a2355"
}.to_json
data = {
"task_id": "b7e7c624-c244-4ba7-a9f7-7d85da73cf03",
"audio_id": "0aac6db8-cbe9-4af2-aea6-d7aa7d3a2355"
}
body: JSON.stringify({
task_id: "b7e7c624-c244-4ba7-a9f7-7d85da73cf03",
audio_id: "0aac6db8-cbe9-4af2-aea6-d7aa7d3a2355"
})
Retrieve timestamped lyrics for synchronized display during audio playback.
HTTP Request
POST https://runapi.ai/api/v1/suno/get_timestamped_lyrics
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | The task ID of the music generation task |
audio_id |
string | Yes | The specific audio ID to retrieve lyrics for |
Response
Returns timestamped lyrics data immediately (synchronous response). This endpoint does not return a processing status.
{
"aligned_words": [
{
"word": "[Verse]\nWaggin'",
"success": true,
"start_time": 1.36,
"end_time": 1.79,
"palign": 0
},
{
"word": "my",
"success": true,
"start_time": 1.79,
"end_time": 1.95,
"palign": 0
}
],
"waveform_data": [0, 1, 0.5, 0.75],
"hoot_cer": 0.3803191489361702,
"is_streamed": false
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Timestamped lyrics generation failed."
}
Generate Persona
Create a music persona:
curl -X POST "https://runapi.ai/api/v1/suno/generate_persona" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"name": "Electronic Pop Singer",
"description": "A modern electronic music style pop singer, skilled in dynamic rhythms and synthesizer tones"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
name: "Electronic Pop Singer",
description: "A modern electronic music style pop singer"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"name": "Electronic Pop Singer",
"description": "A modern electronic music style pop singer"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
name: "Electronic Pop Singer",
description: "A modern electronic music style pop singer"
})
Create a personalized music Persona based on generated music.
HTTP Request
POST https://runapi.ai/api/v1/suno/generate_persona
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | Task ID from music generation endpoints |
audio_id |
string | Yes | Specific audio ID to create Persona for |
name |
string | Yes | Easily recognizable name for the Persona |
description |
string | Yes | Persona's musical characteristics and style |
Response
Returns the generated persona immediately (synchronous response).
{
"persona": {
"id": "abc123def456",
"name": "Electronic Pop Singer",
"description": "A modern electronic music style pop singer, skilled in dynamic rhythms and synthesizer tones"
}
}
Replace Music Section
Replace a music section:
curl -X POST "https://runapi.ai/api/v1/suno/replace_section" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"lyrics": "[Verse]\nA calm and relaxing piano track.",
"tags": "Jazz",
"title": "Relaxing Piano",
"infill_start_time": 10.5,
"infill_end_time": 20.75,
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
lyrics: "[Verse]\nA calm and relaxing piano track.",
tags: "Jazz",
title: "Relaxing Piano",
infill_start_time: 10.5,
infill_end_time: 20.75,
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"audio_id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"lyrics": "[Verse]\nA calm and relaxing piano track.",
"tags": "Jazz",
"title": "Relaxing Piano",
"infill_start_time": 10.5,
"infill_end_time": 20.75,
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
audio_id: "6949072e-5cdb-43a3-b093-56d16e582aeb",
lyrics: "[Verse]\nA calm and relaxing piano track.",
tags: "Jazz",
title: "Relaxing Piano",
infill_start_time: 10.5,
infill_end_time: 20.75,
callback_url: "https://your-app.com/callbacks/suno"
})
Replace a specific time segment within existing music.
HTTP Request
POST https://runapi.ai/api/v1/suno/replace_section
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | Original task ID that identifies the source music |
audio_id |
string | Yes | Unique identifier of the audio track to replace |
lyrics |
string | Yes | Exact lyrics for the replacement segment |
tags |
string | Yes | Music style tags |
title |
string | Yes | Music title |
infill_start_time |
number | Yes | Start time in seconds (2 decimals) |
infill_end_time |
number | Yes | End time in seconds (2 decimals) |
callback_url |
string | No | URL to receive completion notifications |
negative_tags |
string | No | Excluded music styles |
full_lyrics |
string | No | Complete lyrics after modification |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"track": {
"id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse]\nA calm and relaxing piano track.",
"model_name": "chirp-v3-5",
"title": "Relaxing Piano",
"tags": ["Jazz"],
"duration": 198.44
}
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Audio generation failed."
}
Get Replace Section Details
Query section replacement status:
curl "https://runapi.ai/api/v1/suno/replace_section/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/replace_section/TASK_ID')
request = Net::HTTP::Get.new(uri)
url = "https://runapi.ai/api/v1/suno/replace_section/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/replace_section/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and results of a replace section task.
HTTP Request
GET https://runapi.ai/api/v1/suno/replace_section/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
Task ID returned from the Replace Music Section endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"track": {
"id": "6949072e-5cdb-43a3-b093-56d16e582aeb",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse]\nA calm and relaxing piano track.",
"model_name": "chirp-v3-5",
"title": "Relaxing Piano",
"tags": ["Jazz"],
"duration": 198.44
}
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Audio generation failed."
}
Upload and Covers
Upload and cover an audio file:
curl -X POST "https://runapi.ai/api/v1/suno/cover_audio" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"vocal_mode": "instrumental",
"style": "Classical",
"title": "Peaceful Piano Meditation",
"model": "suno-v5",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
vocal_mode: "instrumental",
style: "Classical",
title: "Peaceful Piano Meditation",
model: "suno-v5",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"vocal_mode": "instrumental",
"style": "Classical",
"title": "Peaceful Piano Meditation",
"model": "suno-v5",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
vocal_mode: "instrumental",
style: "Classical",
title: "Peaceful Piano Meditation",
model: "suno-v5",
callback_url: "https://your-app.com/callbacks/suno"
})
Transform uploaded audio into a new style while retaining its core melody.
HTTP Request
POST https://runapi.ai/api/v1/suno/cover_audio
Request Body Parameters
Request Shapes
Choose vocal_mode. Add upload_url, model, and optional callback_url to each request.
-
auto_lyrics: put the cover brief inprompt. -
exact_lyrics: put the sung words inlyricsand providestyleandtitle. -
instrumental: providestyleandtitle.
| Parameter | Type | Required | Description |
|---|---|---|---|
upload_url |
string | Yes | URL of the audio file to process (max 8 minutes). You can use the download_url returned by POST /files/from_url; that temporary link expires after 24 hours. |
model |
string | Yes | Model version: suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4
|
callback_url |
string | No | URL to receive completion notifications |
vocal_mode |
string | Yes |
auto_lyrics, exact_lyrics, or instrumental. |
prompt |
string | No | Cover brief for automatic lyrics. |
lyrics |
string | No | Exact cover lyrics. |
style |
string | No | Music style. |
title |
string | No | Music title. |
persona_id |
string | No | Persona ID to apply. Create one via Generate Persona. |
persona_type |
string | No | Persona type, used with persona_id. Values: style for style characteristics, voice for voice characteristics (suno-v5 only). Ignored without persona_id. |
negative_tags |
string | No | Styles to exclude |
vocal_gender |
string | No | Preferred vocal gender: male or female. Increases probability only. |
style_weight |
number | No | Style adherence weight (0-1) |
weirdness_constraint |
number | No | Creativity constraint (0-1) |
audio_weight |
number | No | Audio consistency weight (0-1) |
Response
Returns the task ID for tracking generation status.
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Calm piano melodies flow gently",
"model_name": "chirp-v3-5",
"title": "Peaceful Piano Meditation (Cover)",
"tags": ["classical", "relaxing"],
"duration": 198.44
}
]
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Cover generation failed."
}
Get Upload and Covers Details
Query upload and covers status:
curl "https://runapi.ai/api/v1/suno/cover_audio/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/cover_audio/TASK_ID')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
url = "https://runapi.ai/api/v1/suno/cover_audio/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/cover_audio/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of an upload and covers task.
HTTP Request
GET https://runapi.ai/api/v1/suno/cover_audio/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned when you submitted the upload and covers request |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Calm piano melodies flow gently",
"model_name": "chirp-v3-5",
"title": "Peaceful Piano Meditation (Cover)",
"tags": ["classical", "relaxing"],
"duration": 198.44
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Cover generation failed."
}
Upload and Extensions
Extend an audio file:
curl -X POST "https://runapi.ai/api/v1/suno/extend_music" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"parameter_mode": "custom",
"prompt": "Extend the music with more relaxing piano notes",
"style": "Classical",
"title": "Peaceful Piano Extended",
"continue_at": 60,
"model": "suno-v5",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
parameter_mode: "custom",
prompt: "Extend the music with more relaxing piano notes",
style: "Classical",
title: "Peaceful Piano Extended",
continue_at: 60,
model: "suno-v5",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"upload_url": "https://cdn.runapi.ai/public/samples/music.mp3",
"parameter_mode": "custom",
"prompt": "Extend the music with more relaxing piano notes",
"style": "Classical",
"title": "Peaceful Piano Extended",
"continue_at": 60,
"model": "suno-v5",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
upload_url: "https://cdn.runapi.ai/public/samples/music.mp3",
parameter_mode: "custom",
prompt: "Extend the music with more relaxing piano notes",
style: "Classical",
title: "Peaceful Piano Extended",
continue_at: 60,
model: "suno-v5",
callback_url: "https://your-app.com/callbacks/suno"
})
Extend uploaded audios while preserving the original style.
HTTP Request
POST https://runapi.ai/api/v1/suno/extend_music
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
upload_url |
string | Yes | URL of the audio file to process (max 8 minutes). You can use the download_url returned by POST /files/from_url; that temporary link expires after 24 hours. |
parameter_mode |
string | Yes |
source to inherit source parameters, or custom to provide parameters in this request |
model |
string | Yes | Model version: suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4
|
callback_url |
string | No | URL to receive completion notifications |
continue_at |
number | Conditional | Start time in seconds (required when parameter_mode is custom) |
prompt |
string | Conditional | Extension description (required when parameter_mode is source or when instrumental is false) |
style |
string | Conditional | Music style (required when parameter_mode is custom) |
title |
string | Conditional | Music title (required when parameter_mode is custom) |
instrumental |
boolean | Conditional | Whether audio is instrumental (required when parameter_mode is custom) |
persona_id |
string | No | Persona ID to apply when parameter_mode is custom. Create one via Generate Persona. |
persona_type |
string | No | Persona type, used with persona_id. Values: style for style characteristics, voice for voice characteristics (suno-v5 only). Ignored without persona_id. |
negative_tags |
string | No | Styles to exclude |
vocal_gender |
string | No | Preferred vocal gender: male or female. Only works with parameter_mode: "custom". Increases probability only. |
style_weight |
number | No | Style adherence weight (0-1) |
weirdness_constraint |
number | No | Creativity constraint (0-1) |
audio_weight |
number | No | Audio consistency weight (0-1) |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Continue the beautiful melody",
"model_name": "chirp-v3-5",
"title": "Peaceful Piano Extended",
"tags": ["classical", "relaxing"],
"duration": 298.44
}
]
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Extension generation failed."
}
Get Upload and Extension Details
Query upload extension status:
curl "https://runapi.ai/api/v1/suno/extend_music/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/extend_music/TASK_ID')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
url = "https://runapi.ai/api/v1/suno/extend_music/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/extend_music/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of an upload extension task.
HTTP Request
GET https://runapi.ai/api/v1/suno/extend_music/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned when you submitted the upload extension request |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Continue the beautiful melody",
"model_name": "chirp-v3-5",
"title": "Peaceful Piano Extended",
"tags": ["classical", "relaxing"],
"duration": 298.44
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "Extension generation failed."
}
Mashup
Create a mashup from two audio files:
curl -X POST "https://runapi.ai/api/v1/suno/create_mashup" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"upload_url_list": ["https://cdn.runapi.ai/public/samples/music.mp3", "https://cdn.runapi.ai/public/samples/music.mp3"],
"vocal_mode": "exact_lyrics",
"lyrics": "[Verse]\nA calm and relaxing piano track with soft melodies",
"style": "Jazz",
"title": "Relaxing Piano",
"model": "suno-v4",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
upload_url_list: ["https://cdn.runapi.ai/public/samples/music.mp3", "https://cdn.runapi.ai/public/samples/music.mp3"],
vocal_mode: "exact_lyrics",
lyrics: "[Verse]\nA calm and relaxing piano track with soft melodies",
style: "Jazz",
title: "Relaxing Piano",
model: "suno-v4",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"upload_url_list": ["https://cdn.runapi.ai/public/samples/music.mp3", "https://cdn.runapi.ai/public/samples/music.mp3"],
"vocal_mode": "exact_lyrics",
"lyrics": "[Verse]\nA calm and relaxing piano track with soft melodies",
"style": "Jazz",
"title": "Relaxing Piano",
"model": "suno-v4",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
upload_url_list: ["https://cdn.runapi.ai/public/samples/music.mp3", "https://cdn.runapi.ai/public/samples/music.mp3"],
vocal_mode: "exact_lyrics",
lyrics: "[Verse]\nA calm and relaxing piano track with soft melodies",
style: "Jazz",
title: "Relaxing Piano",
model: "suno-v4",
callback_url: "https://your-app.com/callbacks/suno"
})
Combine two uploaded audio files into a cohesive new composition using AI mashup generation.
HTTP Request
POST https://runapi.ai/api/v1/suno/create_mashup
Request Body Parameters
Request Shapes
Choose vocal_mode. Add upload_url_list, model, and optional callback_url to each request.
-
auto_lyrics: put the mashup brief inprompt. -
exact_lyrics: put the sung words inlyricsand providestyleandtitle. -
instrumental: providestyleandtitle.
| Parameter | Type | Required | Description |
|---|---|---|---|
upload_url_list |
array | Yes | Two publicly accessible audio file URLs to mashup (exactly 2 required) |
model |
string | Yes | Model version: suno-v5, suno-v4.5-plus, suno-v4.5-all, suno-v4.5, suno-v4
|
callback_url |
string | No | URL to receive completion notifications |
vocal_mode |
string | Yes |
auto_lyrics, exact_lyrics, or instrumental. |
prompt |
string | No | Mashup brief for automatic lyrics. |
lyrics |
string | No | Exact mashup lyrics. |
style |
string | No | Music style. |
title |
string | No | Music title. |
persona_id |
string | No | Persona ID to apply. Create one via Generate Persona. |
persona_type |
string | No | Persona type, used with persona_id. Values: style for style characteristics, voice for voice characteristics (suno-v5 only). Ignored without persona_id. |
vocal_gender |
string | No | Preferred vocal gender: male or female. Increases probability only. |
style_weight |
number | No | Style adherence weight (0-1) |
weirdness_constraint |
number | No | Creativity constraint (0-1) |
audio_weight |
number | No | Audio consistency weight (0-1) |
Response
Returns the task ID for tracking generation status.
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Night city lights shining bright",
"model_name": "chirp-v4",
"title": "Relaxing Piano",
"tags": ["jazz", "piano"],
"duration": 198.44
}
]
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Mashup generation failed."
}
Get Mashup Details
Query mashup status:
curl "https://runapi.ai/api/v1/suno/create_mashup/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/create_mashup/TASK_ID')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
url = "https://runapi.ai/api/v1/suno/create_mashup/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/create_mashup/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a mashup task.
HTTP Request
GET https://runapi.ai/api/v1/suno/create_mashup/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned when you submitted the mashup request |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"lyrics": "[Verse] Night city lights shining bright",
"model_name": "chirp-v4",
"title": "Relaxing Piano",
"tags": ["jazz", "piano"],
"duration": 198.44
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Mashup generation failed."
}
Generate Sound
Create ambient or loopable sound effects from a prompt:
curl -X POST "https://runapi.ai/api/v1/suno/text_to_sound" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Rain on a tin roof, distant thunder, cozy atmosphere",
"model": "suno-v5",
"sound_loop": true,
"sound_tempo": 90,
"sound_key": "Cm",
"grab_lyrics": false,
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
prompt: "Rain on a tin roof, distant thunder, cozy atmosphere",
model: "suno-v5",
sound_loop: true,
sound_tempo: 90,
sound_key: "Cm",
grab_lyrics: false,
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"prompt": "Rain on a tin roof, distant thunder, cozy atmosphere",
"model": "suno-v5",
"sound_loop": True,
"sound_tempo": 90,
"sound_key": "Cm",
"grab_lyrics": False,
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
prompt: "Rain on a tin roof, distant thunder, cozy atmosphere",
model: "suno-v5",
sound_loop: true,
sound_tempo: 90,
sound_key: "Cm",
grab_lyrics: false,
callback_url: "https://your-app.com/callbacks/suno"
})
Create ambient audio, sound effects, or loopable tracks from a text prompt. Supports BPM and musical key control for precise stylistic direction.
HTTP Request
POST https://runapi.ai/api/v1/suno/text_to_sound
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Sound description, max 500 characters |
model |
string | Yes | Model version: suno-v5, suno-v5.5
|
sound_loop |
boolean | No | Generate looping audio. Default false
|
sound_tempo |
integer | No | Beats per minute, 1–300 |
sound_key |
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. Default Any
|
grab_lyrics |
boolean | No | Capture lyric subtitles for later retrieval. Default false
|
callback_url |
string | No | URL to receive completion notifications |
Response
Returns the task ID for tracking generation status.
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"prompt": "Rain on a tin roof, distant thunder",
"model_name": "chirp-v5",
"title": "Rainy Night",
"tags": ["ambient", "rain", "loopable"],
"duration": 32.5
}
]
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Generation failed, please retry."
}
Get Sound Details
Query sound task status:
curl "https://runapi.ai/api/v1/suno/text_to_sound/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/text_to_sound/TASK_ID')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
url = "https://runapi.ai/api/v1/suno/text_to_sound/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/text_to_sound/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a sound generation task.
HTTP Request
GET https://runapi.ai/api/v1/suno/text_to_sound/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned when you submitted the sound generation request |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing"
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"generation_stage": "all_audios_ready",
"audios": [
{
"id": "8551****662c",
"audio_url": "https://file.runapi.ai/****.mp3",
"stream_audio_url": "https://file.runapi.ai/****",
"image_url": "https://file.runapi.ai/****.jpeg",
"prompt": "Rain on a tin roof, distant thunder",
"model_name": "chirp-v5",
"title": "Rainy Night",
"tags": ["ambient", "rain", "loopable"],
"duration": 32.5
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"generation_stage": "failed",
"error": "Generation failed, please retry."
}
Generate MIDI from an audio file:
curl -X POST "https://runapi.ai/api/v1/suno/generate_midi" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"callback_url": "https://your-app.com/callbacks/suno"
}'
request.body = {
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
callback_url: "https://your-app.com/callbacks/suno"
}.to_json
data = {
"task_id": "03fe3e68-2dec-487c-a810-885667aed19c",
"callback_url": "https://your-app.com/callbacks/suno"
}
body: JSON.stringify({
task_id: "03fe3e68-2dec-487c-a810-885667aed19c",
callback_url: "https://your-app.com/callbacks/suno"
})
Convert a separated audio track into MIDI format with detailed note information. Requires a task ID from a previous split_stem vocal separation task.
HTTP Request
POST https://runapi.ai/api/v1/suno/generate_midi
Request Body Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
task_id |
string | Yes | The task ID of a completed stem separation task |
callback_url |
string | No | URL to receive completion notification |
Response
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Callback Format
Success Response:
- Contains instruments array with detected instruments
- Each instrument includes note sequence data
- Notes include pitch (MIDI 0-127), start/end times, velocity
Success callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"instruments": [
{
"name": "Drums",
"notes": [
{
"pitch": 73,
"start_time": 0.036458333333333336,
"end_time": 0.18229166666666666,
"velocity": 1
}
]
},
{
"name": "Electric Bass (finger)",
"notes": [
{
"pitch": 44,
"start_time": 7.6875,
"end_time": 7.911458333333333,
"velocity": 1
}
]
}
]
}
Failed callback:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "MIDI generation failed."
}
Get MIDI Details
Query MIDI status:
curl "https://runapi.ai/api/v1/suno/generate_midi/:id" \
-H "Authorization: Bearer YOUR_API_TOKEN"
uri = URI('https://runapi.ai/api/v1/suno/generate_midi/TASK_ID')
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
url = "https://runapi.ai/api/v1/suno/generate_midi/TASK_ID"
response = requests.get(url, headers=headers)
fetch('https://runapi.ai/api/v1/suno/generate_midi/TASK_ID', {
method: 'GET',
headers: {'Authorization': 'Bearer YOUR_API_TOKEN'}
});
Query the status and details of a MIDI task.
HTTP Request
GET https://runapi.ai/api/v1/suno/generate_midi/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The task ID returned from the MIDI endpoint |
Response
Processing:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "processing",
"instruments": []
}
Completed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "completed",
"instruments": [
{
"name": "Drums",
"notes": [
{
"pitch": 73,
"start_time": 0.036458333333333336,
"end_time": 0.18229166666666666,
"velocity": 1
}
]
}
]
}
Failed:
{
"id": "03fe3e68-2dec-487c-a810-885667aed19c",
"status": "failed",
"error": "MIDI generation failed.",
"instruments": []
}
Nano Banana
Nano Banana on RunAPI creates high-quality images from text prompts.
Version Comparison
| Feature | Base (nano-banana) |
Pro (nano-banana-pro) |
V2 (nano-banana-2) |
|---|---|---|---|
| Max Resolution | 1024px | 4K | 4K |
| Size Control |
aspect_ratio (11 presets) |
aspect_ratio + output_resolution
|
aspect_ratio (15 presets) + output_resolution
|
| Max Prompt | 5,000 chars | 5,000 chars | 20,000 chars |
| Max Images | 8 per request | 8 per request | 14 per request |
| Best For | Quick prototypes | Production quality | Complex multi-panel prompts |
Nano Banana Endpoints
| Task | Endpoint | Model |
|---|---|---|
| Create images (standard) | Create Text-to-Image (Base) | nano-banana |
| Create images (high quality) | Create Text-to-Image (Pro) | nano-banana-pro |
| Create images (V2) | Create Text-to-Image (V2) | nano-banana-2 |
| Edit/modify existing images | Edit Image | nano-banana-edit |
| Check text-to-image status | Get Text-to-Image Status | - |
| Check edit status | Get Edit Status | - |
Create Text-to-Image (Base)
Create an text-to-image task using the base model:
curl -X POST "https://runapi.ai/api/v1/nano_banana/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana",
"callback_url": "https://your-domain.com/api/callback",
"prompt": "A surreal painting of a giant banana floating in space, stars and galaxies in the background, vibrant colors, digital art",
"output_format": "png",
"aspect_ratio": "1:1",
"reference_image_urls": []
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/nano_banana/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'nano-banana',
callback_url: 'https://your-domain.com/api/callback',
prompt: 'A surreal painting of a giant banana floating in space, stars and galaxies in the background, vibrant colors, digital art',
output_format: 'png',
aspect_ratio: '1:1',
reference_image_urls: []
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/nano_banana/text_to_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'nano-banana',
'callback_url': 'https://your-domain.com/api/callback',
'prompt': 'A surreal painting of a giant banana floating in space, stars and galaxies in the background, vibrant colors, digital art',
'output_format': 'png',
'aspect_ratio': '1:1',
'reference_image_urls': []
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'nano-banana',
callback_url: 'https://your-domain.com/api/callback',
prompt: 'A surreal painting of a giant banana floating in space, stars and galaxies in the background, vibrant colors, digital art',
output_format: 'png',
aspect_ratio: '1:1',
reference_image_urls: []
};
fetch('https://runapi.ai/api/v1/nano_banana/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Create a new text-to-image task using the base Nano Banana model. The request is processed asynchronously.
HTTP Request
POST https://runapi.ai/api/v1/nano_banana/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model to use: nano-banana for base version |
prompt |
string | Yes | Text description of the desired image (max 5000 characters) |
callback_url |
string | No | URL for completion callback |
output_format |
string | No | Output format: png, jpg (default: png) |
aspect_ratio |
string | No | Output aspect ratio: 1:1, 9:16, 16:9, 3:4, 4:3, 3:2, 2:3, 5:4, 4:5, 21:9, auto (default: 1:1) |
reference_image_urls |
array | No | Array of reference image URLs (max 8 images, 30MB each) |
Response
Returns a task ID for checking text-to-image status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success Response:
- Includes status: "completed"
- Contains images array with generated image objects
Failed Response:
- Includes status: "failed"
- Contains error message describing the failure reason
Success callback example:
{
"id": "281e5b0*********************f39b9",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png"
}
]
}
Failed callback example:
{
"id": "281e5b0*********************f39b9",
"status": "failed",
"error": "Generation failed due to content policy violation"
}
Create Text-to-Image (Pro)
Create an text-to-image task using the Pro model:
curl -X POST "https://runapi.ai/api/v1/nano_banana/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana-pro",
"callback_url": "https://your-domain.com/api/callback",
"prompt": "A serene mountain landscape at sunset with vibrant orange and purple skies",
"aspect_ratio": "16:9",
"output_resolution": "4k",
"output_format": "png",
"reference_image_urls": []
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/nano_banana/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'nano-banana-pro',
callback_url: 'https://your-domain.com/api/callback',
prompt: 'A serene mountain landscape at sunset with vibrant orange and purple skies',
aspect_ratio: '16:9',
output_resolution: '4k',
output_format: 'png',
reference_image_urls: []
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/nano_banana/text_to_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'nano-banana-pro',
'callback_url': 'https://your-domain.com/api/callback',
'prompt': 'A serene mountain landscape at sunset with vibrant orange and purple skies',
'aspect_ratio': '16:9',
'output_resolution': '4k',
'output_format': 'png',
'reference_image_urls': []
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'nano-banana-pro',
callback_url: 'https://your-domain.com/api/callback',
prompt: 'A serene mountain landscape at sunset with vibrant orange and purple skies',
aspect_ratio: '16:9',
output_resolution: '4k',
output_format: 'png',
reference_image_urls: []
};
fetch('https://runapi.ai/api/v1/nano_banana/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Create a new text-to-image task using the Pro Nano Banana model with higher quality and more features.
HTTP Request
POST https://runapi.ai/api/v1/nano_banana/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model to use: nano-banana-pro for Pro version |
prompt |
string | Yes | Text description of the desired image (max 5000 characters) |
callback_url |
string | No | URL for completion callback |
aspect_ratio |
string | No | Image aspect ratio: 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9, auto (default: 1:1) |
output_resolution |
string | No | Image output resolution: 1k, 2k, 4k (default: 1k) |
output_format |
string | No | Output format: png, jpg (default: png) |
reference_image_urls |
array | No | Array of reference image URLs (max 8 images, 30MB each) |
Response
Returns a task ID for checking text-to-image status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success Response:
- Includes status: "completed"
- Contains images array with generated image objects
Failed Response:
- Includes status: "failed"
- Contains error message describing the failure reason
Success callback example:
{
"id": "281e5b0*********************f39b9",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png"
}
]
}
Failed callback example:
{
"id": "281e5b0*********************f39b9",
"status": "failed",
"error": "Generation failed due to content policy violation"
}
Create Text-to-Image (V2)
Create an text-to-image task using the V2 model:
curl -X POST "https://runapi.ai/api/v1/nano_banana/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "nano-banana-2",
"callback_url": "https://your-domain.com/api/callback",
"prompt": "Comic poster: cool banana hero in shades leaps from sci-fi pad. Six panels showcasing 4K landscapes, multilingual text, holograms, camera controls, frame ratios, and consistent banana poses.",
"aspect_ratio": "auto",
"output_resolution": "2k",
"output_format": "jpg",
"reference_image_urls": []
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/nano_banana/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'nano-banana-2',
callback_url: 'https://your-domain.com/api/callback',
prompt: 'Comic poster: cool banana hero in shades leaps from sci-fi pad.',
aspect_ratio: 'auto',
output_resolution: '2k',
output_format: 'jpg',
reference_image_urls: []
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/nano_banana/text_to_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'nano-banana-2',
'callback_url': 'https://your-domain.com/api/callback',
'prompt': 'Comic poster: cool banana hero in shades leaps from sci-fi pad.',
'aspect_ratio': 'auto',
'output_resolution': '2k',
'output_format': 'jpg',
'reference_image_urls': []
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'nano-banana-2',
callback_url: 'https://your-domain.com/api/callback',
prompt: 'Comic poster: cool banana hero in shades leaps from sci-fi pad.',
aspect_ratio: 'auto',
output_resolution: '2k',
output_format: 'jpg',
reference_image_urls: []
};
fetch('https://runapi.ai/api/v1/nano_banana/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Create a new text-to-image task using the Nano Banana V2 model. V2 accepts longer prompts, more reference images, and a wider range of aspect ratios than the Base and Pro variants.
HTTP Request
POST https://runapi.ai/api/v1/nano_banana/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model to use: nano-banana-2
|
prompt |
string | Yes | Text description of the desired image (max 20,000 characters) |
callback_url |
string | No | URL for completion callback |
aspect_ratio |
string | No | Aspect ratio: 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 (default: auto) |
output_resolution |
string | No | Image output resolution: 1k, 2k, 4k (default: 1k) |
output_format |
string | No | Output format: png, jpg (default: jpg) |
reference_image_urls |
array | No | Array of reference image URLs (max 14 images, jpeg/png/webp, 30MB each) |
Pricing
Imagen 4
Imagen 4 creates images from text prompts and supports pro remix image tasks.
| Model | Mode | Pricing |
|---|---|---|
imagen-4 |
Text-to-image | Pricing |
imagen-4-fast |
Text-to-image | Pricing |
imagen-4-ultra |
Text-to-image | Pricing |
imagen-4-pro-remix-image |
Pro remix image | Pricing |
Create Imagen 4 Text-to-Image
curl -X POST "https://runapi.ai/api/v1/imagen_4/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"imagen-4-fast","prompt":"A warm editorial photo of a glass studio","aspect_ratio":"16:9"}'
const response = await fetch('https://runapi.ai/api/v1/imagen_4/text_to_image', {
method: 'POST',
headers: { Authorization: 'Bearer YOUR_API_TOKEN', 'Content-Type': 'application/json' },
body: JSON.stringify({ model: 'imagen-4-fast', prompt: 'A warm editorial photo of a glass studio', aspect_ratio: '16:9' })
});
client = RunApi::Imagen4::Client.new(api_key: 'YOUR_API_TOKEN')
task = client.text_to_image.create(model: 'imagen-4-fast', prompt: 'A warm editorial photo of a glass studio', aspect_ratio: '16:9')
Response:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing"
}
Create a new Imagen 4 text-to-image task.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
imagen-4, imagen-4-fast, or imagen-4-ultra
|
prompt |
string | Yes | Text prompt; up to 5000 characters |
negative_prompt |
string | No | Content to discourage for Imagen 4 text-to-image models |
aspect_ratio |
string | No | Output aspect ratio; 1:1, 16:9, 9:16, 3:4, or 4:3
|
output_count |
number | No |
imagen-4-fast image count: 1, 2, 3, or 4
|
seed |
number | No | Reproducible image seed |
callback_url |
string | No | Webhook URL notified when the task finishes |
Create Pro Remix Image
curl -X POST "https://runapi.ai/api/v1/imagen_4/remix_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{"model":"imagen-4-pro-remix-image","prompt":"Restyle the reference image as a clean editorial poster","source_image_urls":["https://upload.wikimedia.org/wikipedia/commons/a/a9/Example.jpg"],"aspect_ratio":"auto","output_resolution":"2k","output_format":"png"}'
Create a prompt-guided remix image task.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | imagen-4-pro-remix-image |
prompt |
string | Yes | Text prompt; up to 10000 characters |
source_image_urls |
array | Yes | Public image URLs, up to 8 images |
aspect_ratio |
string | No | Output aspect ratio; 1:1, 2:3, 3:2, 3:4, 4:3, 4:5, 5:4, 9:16, 16:9, 21:9, or auto
|
output_resolution |
string | No | Output resolution: 1k, 2k, or 4k
|
output_format |
string | No | Output format: png or jpg
|
callback_url |
string | No | Webhook URL notified when the task finishes |
Get Imagen 4 Text-to-Image
curl "https://runapi.ai/api/v1/imagen_4/text_to_image/TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
Completed response:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image.png"
}
]
}
Flux Kontext
Flux Kontext is an advanced image generation and editing model from Black Forest Labs. It supports prompt-only generation and source-image editing through one text-to-image endpoint.
Models
| Model | Type | Pricing |
|---|---|---|
flux-kontext-pro |
Standard quality | Pricing |
flux-kontext-max |
Enhanced quality | Pricing |
Flux Kontext Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create text-to-image | Create Text-to-Image | POST |
| Check status | Get Text-to-Image Status | GET |
Create Text-to-Image
Text-to-image generation:
curl -X POST "https://runapi.ai/api/v1/flux_kontext/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-kontext-pro",
"prompt": "A futuristic cityscape at sunset with flying cars and neon lights",
"aspect_ratio": "16:9",
"output_format": "png"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/flux_kontext/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'flux-kontext-pro',
prompt: 'A futuristic cityscape at sunset with flying cars and neon lights',
aspect_ratio: '16:9',
output_format: 'png'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/flux_kontext/text_to_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'flux-kontext-pro',
'prompt': 'A futuristic cityscape at sunset with flying cars and neon lights',
'aspect_ratio': '16:9',
'output_format': 'png'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'flux-kontext-pro',
prompt: 'A futuristic cityscape at sunset with flying cars and neon lights',
aspect_ratio: '16:9',
output_format: 'png'
};
fetch('https://runapi.ai/api/v1/flux_kontext/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
Source-image editing:
curl -X POST "https://runapi.ai/api/v1/flux_kontext/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-kontext-pro",
"prompt": "Change the background to a tropical beach",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"safety_tolerance": 2
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/flux_kontext/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'flux-kontext-pro',
prompt: 'Change the background to a tropical beach',
source_image_url: 'https://cdn.runapi.ai/public/samples/image.jpg',
safety_tolerance: 2
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/flux_kontext/text_to_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'flux-kontext-pro',
'prompt': 'Change the background to a tropical beach',
'source_image_url': 'https://cdn.runapi.ai/public/samples/image.jpg',
'safety_tolerance': 2
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'flux-kontext-pro',
prompt: 'Change the background to a tropical beach',
source_image_url: 'https://cdn.runapi.ai/public/samples/image.jpg',
safety_tolerance: 2
};
fetch('https://runapi.ai/api/v1/flux_kontext/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Create a new image generation or editing task. When source_image_url is provided, the model edits that source image; otherwise it generates from text.
HTTP Request
POST https://runapi.ai/api/v1/flux_kontext/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model to use: flux-kontext-pro or flux-kontext-max
|
prompt |
string | Yes | Text description of the desired image or edit |
aspect_ratio |
string | No | Output aspect ratio: 21:9, 16:9, 4:3, 1:1, 3:4, 9:16 (default: 1:1) |
output_format |
string | No | Output format: jpeg, png (default: jpeg) |
source_image_url |
string | No | Source image URL for editing mode |
enable_translation |
boolean | No | Auto-translate non-English prompts |
enable_prompt_expansion |
boolean | No | Expand prompt with more detail |
safety_tolerance |
integer | No | Content safety level: 0-6 for generation, 0-2 for editing (lower is stricter) |
callback_url |
string | No | URL for completion callback |
watermark |
string | No | Watermark identifier for the generated image |
Response
Returns a task ID for checking text-to-image status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial status: processing
|
Callback Format
If callback_url is provided, a POST request will be sent when generation completes.
Success Response:
- Includes status: "completed"
- Contains images array with generated image URLs
Failed Response:
- Includes status: "failed"
- Contains error message describing the failure reason
Success callback example:
{
"id": "281e5b0*********************f39b9",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png",
"origin_url": "https://file.runapi.ai/origin/image1.png"
}
]
}
Failed callback example:
{
"id": "281e5b0*********************f39b9",
"status": "failed",
"error": "Generation failed due to content policy violation"
}
Get Text-to-Image Status
Poll for generation results:
curl "https://runapi.ai/api/v1/flux_kontext/text_to_image/281e5b0f39b9" \
-H "Authorization: Bearer YOUR_API_TOKEN"
require 'net/http'
require 'uri'
task_id = '281e5b0f39b9'
uri = URI("https://runapi.ai/api/v1/flux_kontext/text_to_image/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
task_id = '281e5b0f39b9'
url = f'https://runapi.ai/api/v1/flux_kontext/text_to_image/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = '281e5b0f39b9';
fetch(`https://runapi.ai/api/v1/flux_kontext/text_to_image/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Processing:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Completed:
{
"id": "281e5b0*********************f39b9",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png",
"origin_url": "https://file.runapi.ai/origin/image1.png"
}
]
}
Failed:
{
"id": "281e5b0*********************f39b9",
"status": "failed",
"error": "Generation failed due to content policy violation"
}
Retrieve the status and results of an text-to-image task.
HTTP Request
GET https://runapi.ai/api/v1/flux_kontext/text_to_image/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The ID of the task returned from the create request |
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
images |
array | Array of image objects (only present when status is completed) |
images[].url |
string | CDN URL of the generated image |
images[].origin_url |
string | Original high-resolution URL (valid for 10 minutes) |
error |
string | Error message (only present when status is failed) |
Status Values
- processing: Task is being processed (waiting, queuing, or generating)
- completed: Generation completed successfully
- failed: Generation failed
Flux 2
Flux 2 provides Black Forest Labs image generation through separate text-to-image and remix-image endpoints. Use text-to-image for prompt-only generation, and remix-image when one or more source images should guide the result.
Models
| Model | Endpoint |
|---|---|
flux-2-pro-text-to-image |
Text-to-image |
flux-2-flex-text-to-image |
Text-to-image |
flux-2-pro-remix-image |
Remix image |
flux-2-flex-remix-image |
Remix image |
Flux 2 Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create text-to-image task | Create Text-to-Image | POST |
| Check text-to-image task status | Get Text-to-Image Status | GET |
| Create remix-image task | Create Remix Image | POST |
| Check remix-image task status | Get Remix Image Status | GET |
Create Text-to-Image
Text-to-image request:
curl -X POST "https://runapi.ai/api/v1/flux_2/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-2-pro-text-to-image",
"prompt": "A photorealistic portrait of an astronaut floating in space with Earth in the background",
"aspect_ratio": "16:9",
"output_resolution": "2k"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/flux_2/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'flux-2-pro-text-to-image',
prompt: 'A photorealistic portrait of an astronaut floating in space with Earth in the background',
aspect_ratio: '16:9',
output_resolution: '2k'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/flux_2/text_to_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'flux-2-pro-text-to-image',
'prompt': 'A photorealistic portrait of an astronaut floating in space with Earth in the background',
'aspect_ratio': '16:9',
'output_resolution': '2k'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'flux-2-pro-text-to-image',
prompt: 'A photorealistic portrait of an astronaut floating in space with Earth in the background',
aspect_ratio: '16:9',
output_resolution: '2k'
};
fetch('https://runapi.ai/api/v1/flux_2/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
Create a new text-to-image task from a prompt.
HTTP Request
POST https://runapi.ai/api/v1/flux_2/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
flux-2-pro-text-to-image or flux-2-flex-text-to-image
|
prompt |
string | Yes | Text description of the desired image (3-5000 characters) |
aspect_ratio |
string | No |
1:1, 4:3, 3:4, 16:9, 9:16, 3:2, or 2:3
|
output_resolution |
string | No | Output image resolution: 1k or 2k
|
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | URL for completion callback |
Response
Returns a task ID for checking task status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial status: processing
|
Get Text-to-Image Status
Poll for task results:
curl "https://runapi.ai/api/v1/flux_2/text_to_image/281e5b0f39b9" \
-H "Authorization: Bearer YOUR_API_TOKEN"
require 'net/http'
require 'uri'
task_id = '281e5b0f39b9'
uri = URI("https://runapi.ai/api/v1/flux_2/text_to_image/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
task_id = '281e5b0f39b9'
url = f'https://runapi.ai/api/v1/flux_2/text_to_image/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = '281e5b0f39b9';
fetch(`https://runapi.ai/api/v1/flux_2/text_to_image/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Retrieve the status and results of a text-to-image task.
HTTP Request
GET https://runapi.ai/api/v1/flux_2/text_to_image/:id
Create Remix Image
Remix-image request:
curl -X POST "https://runapi.ai/api/v1/flux_2/remix_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-2-pro-remix-image",
"prompt": "Transform this photo into an oil painting style with warm tones",
"source_image_urls": [
"https://cdn.runapi.ai/public/samples/image.jpg"
],
"aspect_ratio": "auto"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/flux_2/remix_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'flux-2-pro-remix-image',
prompt: 'Transform this photo into an oil painting style with warm tones',
source_image_urls: [
'https://cdn.runapi.ai/public/samples/image.jpg'
],
aspect_ratio: 'auto'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/flux_2/remix_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'flux-2-pro-remix-image',
'prompt': 'Transform this photo into an oil painting style with warm tones',
'source_image_urls': [
'https://cdn.runapi.ai/public/samples/image.jpg'
],
'aspect_ratio': 'auto'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'flux-2-pro-remix-image',
prompt: 'Transform this photo into an oil painting style with warm tones',
source_image_urls: [
'https://cdn.runapi.ai/public/samples/image.jpg'
],
aspect_ratio: 'auto'
};
fetch('https://runapi.ai/api/v1/flux_2/remix_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
Create a prompt-guided remix task from source images.
HTTP Request
POST https://runapi.ai/api/v1/flux_2/remix_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
flux-2-pro-remix-image or flux-2-flex-remix-image
|
prompt |
string | Yes | Text instructions for the remix (3-5000 characters) |
source_image_urls |
array | Yes | Source image URLs (1-8 images) |
aspect_ratio |
string | No |
1:1, 4:3, 3:4, 16:9, 9:16, 3:2, 2:3, or auto
|
output_resolution |
string | No | Output image resolution: 1k or 2k
|
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | URL for completion callback |
Get Remix Image Status
Poll for remix results:
curl "https://runapi.ai/api/v1/flux_2/remix_image/281e5b0f39b9" \
-H "Authorization: Bearer YOUR_API_TOKEN"
require 'net/http'
require 'uri'
task_id = '281e5b0f39b9'
uri = URI("https://runapi.ai/api/v1/flux_2/remix_image/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
task_id = '281e5b0f39b9'
url = f'https://runapi.ai/api/v1/flux_2/remix_image/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = '281e5b0f39b9';
fetch(`https://runapi.ai/api/v1/flux_2/remix_image/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Retrieve the status and results of a remix-image task.
HTTP Request
GET https://runapi.ai/api/v1/flux_2/remix_image/:id
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
images |
array | Array of image objects (only present when status is completed) |
images[].url |
string | CDN URL of the generated image |
error |
string | Error message (only present when status is failed) |
Qwen 2
Qwen 2 supports text-to-image creation, prompt-guided image remixing, and prompt-guided image editing. All tasks are asynchronous and return a task ID that can be polled or delivered by webhook.
Models
| Model | Capability | Pricing |
|---|---|---|
qwen-2-text-to-image |
Text-to-image | Pricing |
qwen-2-remix-image |
Image remix | Pricing |
qwen-2-edit-image |
Edit image | Pricing |
Qwen 2 Endpoints
| Operation | Endpoint |
|---|---|
| Create text-to-image task | POST /api/v1/qwen_2/text_to_image |
| Get text-to-image task | GET /api/v1/qwen_2/text_to_image/:id |
| Create remix-image task | POST /api/v1/qwen_2/remix_image |
| Get remix-image task | GET /api/v1/qwen_2/remix_image/:id |
| Create edit-image task | POST /api/v1/qwen_2/edit_image |
| Get edit-image task | GET /api/v1/qwen_2/edit_image/:id |
Create Text-to-Image
curl -X POST "https://runapi.ai/api/v1/qwen_2/text_to_image" \
-H "Authorization: Bearer $RUNAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-2-text-to-image",
"prompt": "A cinematic glass city at sunrise, high detail",
"aspect_ratio": "16:9",
"output_format": "png"
}'
POST https://runapi.ai/api/v1/qwen_2/text_to_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be qwen-2-text-to-image
|
prompt |
string | Yes | Text prompt, up to 800 characters |
aspect_ratio |
string | No |
1:1, 3:4, 4:3, 9:16, or 16:9; default 16:9
|
seed |
integer | No | Seed for reproducible results |
output_format |
string | No |
png or jpeg; default png
|
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | Webhook URL for completion notification |
Create Remix Image
curl -X POST "https://runapi.ai/api/v1/qwen_2/remix_image" \
-H "Authorization: Bearer $RUNAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-2-remix-image",
"prompt": "Turn this scene into a soft watercolor illustration",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"strength": 0.8,
"output_format": "png"
}'
POST https://runapi.ai/api/v1/qwen_2/remix_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be qwen-2-remix-image
|
prompt |
string | Yes | Remix prompt, up to 5000 characters |
source_image_url |
string | Yes | Source image URL; accepts JPEG, PNG, and WebP up to 10 MB |
strength |
number | No | Denoising strength from 0 to 1; default 0.8
|
output_format |
string | No |
png or jpeg; default png
|
acceleration |
string | No |
none, regular, or high; default none
|
negative_prompt |
string | No | Negative prompt, up to 500 characters |
seed |
integer | No | Seed for reproducible results |
num_inference_steps |
integer | No | Number of inference steps from 2 to 250; default 30
|
guidance_scale |
number | No | Prompt adherence from 0 to 20; default 2.5
|
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | Webhook URL for completion notification |
Create Edit Image
curl -X POST "https://runapi.ai/api/v1/qwen_2/edit_image" \
-H "Authorization: Bearer $RUNAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-2-edit-image",
"prompt": "Replace the background with a neon-lit city skyline",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"aspect_ratio": "16:9",
"output_format": "png"
}'
POST https://runapi.ai/api/v1/qwen_2/edit_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be qwen-2-edit-image
|
prompt |
string | Yes | Edit instruction, up to 800 characters |
source_image_url |
string | Yes | Source image URL; accepts JPEG, PNG, and WebP up to 10 MB |
aspect_ratio |
string | No |
1:1, 2:3, 3:2, 3:4, 4:3, 9:16, 16:9, or 21:9
|
output_format |
string | No |
jpeg or png; default png
|
seed |
integer | No | Seed for reproducible results |
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | Webhook URL for completion notification |
Response
Create endpoints return 202 Accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
Polling and webhook responses use the same shape:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/tools/example/result.png"
}
]
}
Recraft
Recraft on RunAPI provides two image post-processing endpoints: image upscale and background removal.
Models
| Model | Type | Credits |
|---|---|---|
recraft-crisp-upscale |
Image upscale | 1 |
recraft-remove-background |
Background removal | 1 |
Recraft Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Upscale image | Upscale Image | POST |
| Remove background | Remove Background | POST |
| Check task status | Get Recraft Task Status | GET |
Upscale Recraft Image
Upscale an image:
curl -X POST "https://runapi.ai/api/v1/recraft/upscale_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "recraft-crisp-upscale",
"source_image_url": "https://cdn.runapi.ai/public/samples/upscale.jpg"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/recraft/upscale_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'recraft-crisp-upscale',
source_image_url: 'https://cdn.runapi.ai/public/samples/upscale.jpg'
}.to_json
response = http.request(request)
puts response.body
import requests
response = requests.post(
'https://runapi.ai/api/v1/recraft/upscale_image',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'recraft-crisp-upscale',
'source_image_url': 'https://cdn.runapi.ai/public/samples/upscale.jpg'
}
)
print(response.json())
fetch('https://runapi.ai/api/v1/recraft/upscale_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'recraft-crisp-upscale',
source_image_url: 'https://cdn.runapi.ai/public/samples/upscale.jpg'
})
})
.then(res => res.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/recraft/upscale_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be recraft-crisp-upscale
|
source_image_url |
string | Yes | Source image URL |
callback_url |
string | No | URL for completion callback |
Remove Recraft Background
Remove an image background:
curl -X POST "https://runapi.ai/api/v1/recraft/remove_background" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "recraft-remove-background",
"source_image_url": "https://cdn.runapi.ai/public/samples/remove-bg.jpg"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/recraft/remove_background')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'recraft-remove-background',
source_image_url: 'https://cdn.runapi.ai/public/samples/remove-bg.jpg'
}.to_json
response = http.request(request)
puts response.body
import requests
response = requests.post(
'https://runapi.ai/api/v1/recraft/remove_background',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'recraft-remove-background',
'source_image_url': 'https://cdn.runapi.ai/public/samples/remove-bg.jpg'
}
)
print(response.json())
fetch('https://runapi.ai/api/v1/recraft/remove_background', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'recraft-remove-background',
source_image_url: 'https://cdn.runapi.ai/public/samples/remove-bg.jpg'
})
})
.then(res => res.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/recraft/remove_background
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be recraft-remove-background
|
source_image_url |
string | Yes | Source image URL |
callback_url |
string | No | URL for completion callback |
Get Recraft Task Status
Use the matching GET endpoint for the task you created:
| Create Endpoint | Status Endpoint |
|---|---|
POST /api/v1/recraft/upscale_image |
GET /api/v1/recraft/upscale_image/:id |
POST /api/v1/recraft/remove_background |
GET /api/v1/recraft/remove_background/:id |
Poll an upscale task:
curl "https://runapi.ai/api/v1/recraft/upscale_image/281e5b0f39b9" \
-H "Authorization: Bearer YOUR_API_TOKEN"
{
"id": "281e5b0*********************f39b9",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/recraft/result.png"
}
]
}
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
images |
array | Result image objects when the task is completed |
images[].url |
string | CDN URL of the processed image |
error |
string | Error message when the task fails |
Z-Image
Z-Image generates images from text prompts. It supports five aspect ratios and asynchronous callbacks.
Models
| Model | Type | Credits |
|---|---|---|
z-image |
Text-to-image | 1 |
Z-Image Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create text-to-image | Create Text-to-Image | POST |
| Check status | Get Text-to-Image Status | GET |
Create Text-to-Image
Create a text-to-image task:
curl -X POST "https://runapi.ai/api/v1/z_image/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"prompt": "Generate a photorealistic image of a cafe terrace in Paris on a crisp spring morning",
"aspect_ratio": "1:1",
"enable_safety_checker": true
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/z_image/text_to_image')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'z-image',
prompt: 'Generate a photorealistic image of a cafe terrace in Paris on a crisp spring morning',
aspect_ratio: '1:1',
enable_safety_checker: true
}.to_json
response = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(request) }
puts response.body
import requests
response = requests.post(
'https://runapi.ai/api/v1/z_image/text_to_image',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'z-image',
'prompt': 'Generate a photorealistic image of a cafe terrace in Paris on a crisp spring morning',
'aspect_ratio': '1:1',
'enable_safety_checker': True
}
)
print(response.json())
fetch('https://runapi.ai/api/v1/z_image/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'z-image',
prompt: 'Generate a photorealistic image of a cafe terrace in Paris on a crisp spring morning',
aspect_ratio: '1:1',
enable_safety_checker: true
})
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "281e5b0*********************f39b9",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/z_image/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be z-image
|
prompt |
string | Yes | Image description, up to 1000 characters |
aspect_ratio |
string | Yes | Output aspect ratio: 1:1, 4:3, 3:4, 16:9, 9:16
|
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | URL for completion callback |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial status: processing
|
Get Text-to-Image Status
Poll for generation results:
curl "https://runapi.ai/api/v1/z_image/text_to_image/281e5b0f39b9" \
-H "Authorization: Bearer YOUR_API_TOKEN"
HTTP Request
GET https://runapi.ai/api/v1/z_image/text_to_image/{id}
Response
Completed tasks include generated image URLs:
{
"id": "281e5b0*********************f39b9",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png"
}
]
}
Ideogram V3
Ideogram V3 is RunAPI's Ideogram-powered image family covering text-to-image, character-guided generation, inpainting edits, remixes, and reframing.
Models
| Model | Type | Pricing |
|---|---|---|
ideogram-v3-text-to-image |
Text-to-image | Pricing |
ideogram-v3-edit |
Image inpaint | Pricing |
ideogram-v3-remix |
Image remix | Pricing |
ideogram-v3-character |
Character-guided text-to-image | Pricing |
ideogram-v3-character-edit |
Character-guided inpaint | Pricing |
ideogram-v3-character-remix |
Character-guided remix | Pricing |
ideogram-v3-reframe |
Image reframe | Pricing |
Ideogram V3 Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Generate image from text | Create Text-to-Image | POST |
| Get text-to-image status | Get Text-to-Image Status | GET |
| Inpaint an image | Create Edit | POST |
| Get edit status | Get Edit Status | GET |
| Remix an image | Create Remix | POST |
| Get remix status | Get Remix Status | GET |
| Reframe an image | Create Reframe | POST |
| Get reframe status | Get Reframe Status | GET |
Create Ideogram V3 Text-to-Image
curl -X POST "https://runapi.ai/api/v1/ideogram_v3/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "ideogram-v3-text-to-image",
"prompt": "A cinematic lakeside at twilight with neon reeds",
"rendering_speed": "balanced",
"style": "realistic",
"aspect_ratio": "1:1"
}'
POST https://runapi.ai/api/v1/ideogram_v3/text_to_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be ideogram-v3-text-to-image
|
prompt |
string | Yes | Up to 5000 characters |
reference_image_urls |
array | Yes for ideogram-v3-character
|
Character reference image URLs; only the first image is used, JPEG/PNG/WEBP, max 10 MB total |
callback_url |
string | No | Webhook URL for completion notification |
rendering_speed |
string | No |
turbo, balanced (default), quality
|
style |
string | No |
auto, general, realistic, design; ideogram-v3-character supports auto, realistic, fiction
|
enable_prompt_expansion |
boolean | No | Use MagicPrompt to expand the prompt |
aspect_ratio |
string | No |
1:1, 3:4, 9:16, 4:3, 16:9
|
output_count |
number | No |
1, 2, 3, 4
|
seed |
integer | No | Seed for the random number generator |
negative_prompt |
string | No | Up to 5000 characters. Exclusions |
Use model: "ideogram-v3-character" on this endpoint for character-guided generation. Character requests require reference_image_urls and may use style: "fiction" plus output_count.
Response
{
"id": "281e5b0f39b9",
"status": "processing"
}
Get Ideogram V3 Text-to-Image Status
curl "https://runapi.ai/api/v1/ideogram_v3/text_to_image/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
GET https://runapi.ai/api/v1/ideogram_v3/text_to_image/:id
Completed response
{
"id": "281e5b0f39b9",
"status": "completed",
"images": [
{ "url": "https://file.runapi.ai/ideogram/result.png" }
]
}
Failed response
{
"id": "281e5b0f39b9",
"status": "failed",
"error": "Prompt rejected"
}
Create Ideogram V3 Edit
curl -X POST "https://runapi.ai/api/v1/ideogram_v3/edit_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "ideogram-v3-edit",
"prompt": "A dog wearing a cowboy hat",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"mask_url": "https://cdn.runapi.ai/public/samples/mask.png",
"rendering_speed": "balanced"
}'
POST https://runapi.ai/api/v1/ideogram_v3/edit_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be ideogram-v3-edit
|
prompt |
string | Yes | Fill text for the masked area. Up to 5000 characters |
source_image_url |
string | Yes | URL of the source image. JPEG/PNG/WEBP, max 10 MB |
mask_url |
string | Yes | URL of the inpaint mask. Must match source dimensions |
reference_image_urls |
array | Yes for ideogram-v3-character-edit
|
Character reference image URLs; only the first image is used, JPEG/PNG/WEBP, max 10 MB total |
callback_url |
string | No | Webhook URL for completion notification |
rendering_speed |
string | No |
turbo, balanced (default), quality
|
style |
string | No |
ideogram-v3-character-edit only: auto, realistic, fiction
|
enable_prompt_expansion |
boolean | No | Use MagicPrompt to expand the prompt |
output_count |
number | No |
1, 2, 3, 4
|
seed |
integer | No | Seed for the random number generator |
Use model: "ideogram-v3-character-edit" on this endpoint for character-guided inpainting. Character edit requests require reference_image_urls.
Response
{
"id": "281e5b0f39b9",
"status": "processing"
}
Get Ideogram V3 Edit Status
curl "https://runapi.ai/api/v1/ideogram_v3/edit_image/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
GET https://runapi.ai/api/v1/ideogram_v3/edit_image/:id
Response shape matches the text-to-image status endpoint.
Create Ideogram V3 Remix
curl -X POST "https://runapi.ai/api/v1/ideogram_v3/remix_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "ideogram-v3-remix",
"prompt": "Change the cube into a sphere",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"rendering_speed": "balanced",
"strength": 0.8,
"output_count": 1
}'
POST https://runapi.ai/api/v1/ideogram_v3/remix_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be ideogram-v3-remix
|
prompt |
string | Yes | Up to 5000 characters |
source_image_url |
string | Yes | URL of the source image. JPEG/PNG/WEBP, max 10 MB |
reference_image_urls |
array | Yes for ideogram-v3-character-remix
|
Character reference image URLs; only the first image is used, JPEG/PNG/WEBP, max 10 MB total |
callback_url |
string | No | Webhook URL for completion notification |
rendering_speed |
string | No |
turbo, balanced (default), quality
|
style |
string | No |
auto, general, realistic, design; ideogram-v3-character-remix supports auto, realistic, fiction
|
enable_prompt_expansion |
boolean | No | Use MagicPrompt to expand the prompt |
aspect_ratio |
string | No |
1:1, 3:4, 9:16, 4:3, 16:9
|
output_count |
number | No |
1, 2, 3, 4
|
seed |
integer | No | Seed for the random number generator |
strength |
number | No | 0.01–1 for ideogram-v3-remix; 0.1–1 for ideogram-v3-character-remix
|
negative_prompt |
string | No | Up to 5000 characters for ideogram-v3-remix; up to 500 for ideogram-v3-character-remix
|
style_reference_image_urls |
array | No |
ideogram-v3-character-remix only: style reference image URLs |
reference_mask_urls |
array | No |
ideogram-v3-character-remix only: masks for character references; ignored without reference_image_urls
|
Use model: "ideogram-v3-character-remix" on this endpoint for character-guided remixing. Character remix requests require reference_image_urls.
Response
{
"id": "281e5b0f39b9",
"status": "processing"
}
Get Ideogram V3 Remix Status
curl "https://runapi.ai/api/v1/ideogram_v3/remix_image/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
GET https://runapi.ai/api/v1/ideogram_v3/remix_image/:id
Response shape matches the text-to-image status endpoint.
Create Ideogram V3 Reframe
curl -X POST "https://runapi.ai/api/v1/ideogram_v3/reframe_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "ideogram-v3-reframe",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"aspect_ratio": "3:4",
"rendering_speed": "balanced",
"style": "auto",
"output_count": 1
}'
POST https://runapi.ai/api/v1/ideogram_v3/reframe_image
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be ideogram-v3-reframe
|
source_image_url |
string | Yes | URL of the source image. JPEG/PNG/WEBP, max 10 MB |
aspect_ratio |
string | Yes |
1:1, 3:4, 9:16, 4:3, 16:9
|
callback_url |
string | No | Webhook URL for completion notification |
rendering_speed |
string | No |
turbo, balanced (default), quality
|
style |
string | No |
auto, general, realistic, design
|
output_count |
number | No |
1, 2, 3, 4
|
seed |
integer | No | Seed for the random number generator |
Response
{
"id": "281e5b0f39b9",
"status": "processing"
}
Get Ideogram V3 Reframe Status
curl "https://runapi.ai/api/v1/ideogram_v3/reframe_image/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
GET https://runapi.ai/api/v1/ideogram_v3/reframe_image/:id
Response shape matches the text-to-image status endpoint.
ElevenLabs
ElevenLabs on RunAPI provides text-to-speech, multi-speaker dialogue generation, text-to-sound, speech-to-text, and isolate audio through a consistent async task interface.
ElevenLabs Endpoints
| Task | Endpoint | Models |
|---|---|---|
| Create speech | Create ElevenLabs Speech |
text-to-speech-turbo-v2.5, text-to-speech-multilingual-v2
|
| Get speech | Get ElevenLabs Speech | - |
| Create dialogue | Create ElevenLabs Dialogue | text-to-dialogue-v3 |
| Get dialogue | Get ElevenLabs Dialogue | - |
| Create text-to-sound | Create ElevenLabs Text to Sound | sound-effect-v2 |
| Get text-to-sound | Get ElevenLabs Text to Sound | - |
| Create speech-to-text | Create ElevenLabs Speech to Text | speech-to-text |
| Get speech-to-text | Get ElevenLabs Speech to Text | - |
| Create isolate-audio | Create ElevenLabs Isolate Audio | audio-isolation |
| Get isolate-audio | Get ElevenLabs Isolate Audio | - |
Create ElevenLabs Speech
curl -X POST "https://runapi.ai/api/v1/elevenlabs/text_to_speech" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "text-to-speech-turbo-v2.5",
"text": "Hello from RunAPI",
"voice": "EkK5I93UQWFDigLMpZcX",
"callback_url": "https://example.com/callback"
}'
HTTP 202
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
POST https://runapi.ai/api/v1/elevenlabs/text_to_speech
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
text-to-speech-turbo-v2.5 or text-to-speech-multilingual-v2
|
text |
string | Yes | Speech text, max 5000 chars |
voice |
string | Conditional | Voice name or voice ID; required for multilingual, optional for turbo. Turbo defaults to EkK5I93UQWFDigLMpZcX when omitted. |
callback_url |
string | No | HTTPS callback URL |
stability |
number | No | 0-1 |
similarity_boost |
number | No | 0-1 |
style |
number | No | 0-1 |
speed |
number | No | 0.7-1.2 |
timestamps |
boolean | No | Return word timestamps |
previous_text |
string | No | Context text before current request |
next_text |
string | No | Context text after current request |
language_code |
string | No | Language hint |
Get ElevenLabs Speech
curl "https://runapi.ai/api/v1/elevenlabs/text_to_speech/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"audios": [
{ "url": "https://file.runapi.ai/audio.mp3" }
]
}
Create ElevenLabs Dialogue
curl -X POST "https://runapi.ai/api/v1/elevenlabs/text_to_dialogue" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"dialogue": [
{ "text": "Hello", "voice": "Adam" },
{ "text": "How are you?", "voice": "Brian" }
],
"stability": 0.5
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
dialogue |
array | Yes | Dialogue lines with text and voice; total text max 5000 chars |
stability |
number | No |
0, 0.5, or 1
|
language_code |
string | No | Language hint |
callback_url |
string | No | HTTPS callback URL |
Get ElevenLabs Dialogue
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"audios": [
{ "url": "https://tempfile.runapi.ai/dialogue.mp3" }
]
}
Create ElevenLabs Text to Sound
curl -X POST "https://runapi.ai/api/v1/elevenlabs/text_to_sound" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"text": "Thunder crash and heavy rain",
"output_format": "mp3_44100_128"
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
text |
string | Yes | Sound effect prompt, max 5000 chars |
loop |
boolean | No | Generate a looping sound |
duration_seconds |
number | No | 0.5-22 |
prompt_influence |
number | No | 0-1 |
output_format |
string | No | Output codec and bitrate |
callback_url |
string | No | HTTPS callback URL |
Get ElevenLabs Text to Sound
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"audios": [
{ "url": "https://file.runapi.ai/text-to-sound.mp3" }
]
}
Create ElevenLabs Speech to Text
curl -X POST "https://runapi.ai/api/v1/elevenlabs/speech_to_text" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_audio_url": "https://cdn.runapi.ai/public/samples/voice.mp3",
"diarize": true
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
source_audio_url |
string | Yes | Source audio URL |
language_code |
string | No | Language hint |
tag_audio_events |
boolean | No | Tag applause, laughter, etc. |
diarize |
boolean | No | Label speakers |
callback_url |
string | No | HTTPS callback URL |
Get ElevenLabs Speech to Text
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"text": "Hello from ElevenLabs"
}
Create ElevenLabs Isolate Audio
curl -X POST "https://runapi.ai/api/v1/elevenlabs/isolate_audio" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_audio_url": "https://cdn.runapi.ai/public/samples/voice.mp3"
}'
| Parameter | Type | Required | Description |
|---|---|---|---|
source_audio_url |
string | Yes | Source audio URL |
callback_url |
string | No | HTTPS callback URL |
Get ElevenLabs Isolate Audio
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"audios": [
{ "url": "https://file.runapi.ai/isolated.mp3" }
]
}
InfiniteTalk
InfiniteTalk on RunAPI creates lip-sync videos from a single image, a driving audio clip, and a prompt through the same async task interface used across the platform.
InfiniteTalk Endpoints
| Task | Endpoint | Models |
|---|---|---|
| Create from-audio task | Create InfiniteTalk From Audio | infinitetalk-from-audio |
| Get task status | Get InfiniteTalk From Audio Status | - |
Create InfiniteTalk From Audio
curl -X POST "https://runapi.ai/api/v1/infinitetalk/audio_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "infinitetalk-from-audio",
"source_image_url": "https://cdn.runapi.ai/public/samples/portrait.jpg",
"source_audio_url": "https://cdn.runapi.ai/public/samples/voice.mp3",
"prompt": "A young woman with long dark hair talking on a podcast.",
"output_resolution": "480p",
"seed": 12345,
"callback_url": "https://example.com/callback"
}'
HTTP 202
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
POST https://runapi.ai/api/v1/infinitetalk/audio_to_video
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be infinitetalk-from-audio
|
source_image_url |
string | Yes | Source image URL |
source_audio_url |
string | Yes | Source audio URL |
prompt |
string | Yes | Prompt text, max 5000 chars |
output_resolution |
string | No |
480p or 720p
|
seed |
integer | No | Integer between 10000 and 1000000
|
callback_url |
string | No | HTTPS callback URL |
Get InfiniteTalk From Audio Status
curl "https://runapi.ai/api/v1/infinitetalk/audio_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{ "url": "https://file.runapi.ai/infinitetalk/video.mp4" }
]
}
Gemini Omni
Gemini Omni on RunAPI creates reusable voice resources, character resources, and multimodal video tasks.
Gemini Omni Endpoints
| Task | Endpoint | Models |
|---|---|---|
| Create audio voice | Create Gemini Omni Audio | gemini-omni-audio |
| Create character | Create Gemini Omni Character | gemini-omni-character |
| Text-to-video | Gemini Omni Text-to-Video | gemini-omni-text-to-video |
| Check video status | Get Gemini Omni Text-to-Video Status | - |
Create Gemini Omni Audio
curl -X POST "https://runapi.ai/api/v1/gemini_omni/create_audio" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"audio_id": "achernar",
"name": "Acher Narrator",
"voice_description": "A calm, clear, and friendly male voice suitable for tech explainers and daily conversation.",
"example_dialogue": "Hello, I am achernar"
}'
HTTP 200
{
"id": "a8f1c2d3e4f5",
"audio": {
"id": "a8f1c2d3e4f5",
"name": "Acher Narrator"
}
}
POST https://runapi.ai/api/v1/gemini_omni/create_audio
| Parameter | Type | Required | Description |
|---|---|---|---|
audio_id |
string | Yes | Preset voice ID, such as achernar, achird, charon, puck, or zephyr
|
name |
string | Yes | Voice name, max 210 characters |
voice_description |
string | No | Timbre, style, pace, and emotion description, max 20000 characters |
example_dialogue |
string | No | Example line spoken by the voice, max 120 characters |
Create Gemini Omni Character
curl -X POST "https://runapi.ai/api/v1/gemini_omni/create_character" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"descriptions": "A young female character with short silver hair and a futuristic utility jacket, calm, agile, and strongly cyberpunk in style.",
"reference_image_url": "https://cdn.runapi.ai/public/samples/reference-1.jpg",
"audio_ids": ["audio_01hx8p0demo"],
"character_name": "Jenny"
}'
HTTP 200
{
"id": "b09dbf56",
"character": {
"id": "b09dbf56",
"name": "Jenny",
"images": [
{
"url": "https://file.runapi.ai/gemini/jenny.png"
}
]
}
}
POST https://runapi.ai/api/v1/gemini_omni/create_character
| Parameter | Type | Required | Description |
|---|---|---|---|
descriptions |
string | Yes | Character appearance, identity, style, clothing, or personality description |
reference_image_url |
string | Yes | Character reference image URL; the image must be no larger than 20MB |
audio_ids |
array[string] | No | Audio IDs returned by Create Gemini Omni Audio, used as voice or persona guidance |
character_name |
string | No | Character name, max 210 characters |
Gemini Omni Text-to-Video
curl -X POST "https://runapi.ai/api/v1/gemini_omni/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Create a futuristic night city short film with a slow push-in shot as the character walks out from a neon-lit street.",
"reference_image_urls": ["https://cdn.runapi.ai/public/samples/reference-1.jpg"],
"audio_ids": ["audio_01hx8p0demo"],
"character_ids": ["character_01hx8p0demo"],
"duration_seconds": 8,
"aspect_ratio": "16:9",
"output_resolution": "1080p",
"seed": 12345,
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
POST https://runapi.ai/api/v1/gemini_omni/text_to_video
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | Video prompt, max 20000 characters |
reference_image_urls |
array[string] | No | Reference image URLs, max 7 images, max 20MB each |
audio_ids |
array[string] | No | Audio IDs returned by Create Gemini Omni Audio, max 3 |
character_ids |
array[string] | No | Character IDs returned by Create Gemini Omni Character, max 3 |
video_list |
array[object] | No | Source video clips, max 1 clip; each clip has url, start, and ends, and uses 2 reference units |
duration_seconds |
number | Yes | One of 4, 6, 8, or 10; ignored when video_list is provided |
aspect_ratio |
string | No |
16:9 or 9:16
|
output_resolution |
string | No |
720p, 1080p, or 4k; defaults to 720p
|
seed |
integer | No | Integer from 0 to 2147483647 |
callback_url |
string | No | HTTPS callback URL for completion events |
Reference inputs share a 7-unit limit: each reference image counts as 1, each video clip counts as 2, and each character ID counts as 1.
Get Gemini Omni Text-to-Video Status
curl "https://runapi.ai/api/v1/gemini_omni/text_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
HTTP 200
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/gemini-omni/output.mp4"
}
]
}
GET https://runapi.ai/api/v1/gemini_omni/text_to_video/:id
| Parameter | Description |
|---|---|
id |
Task ID returned by POST /api/v1/gemini_omni/text_to_video
|
If callback_url is provided when creating the task, RunAPI sends the same completed or failed response body when the task finishes.
Wan
Wan is Alibaba's state-of-the-art video and image generation model family. It supports text-to-video, image-to-video, speech-to-video, animation, image generation, reference-guided text-to-video, and video editing across multiple model versions.
Models
| Model | Type | Pricing |
|---|---|---|
wan-2.2-a14b-text-to-video-turbo |
Text to video | Pricing |
wan-2.5-text-to-video |
Text to video | Pricing |
wan-2.6-text-to-video |
Text to video | Pricing |
wan-2.7-text-to-video |
Text to video | Pricing |
wan-2.2-a14b-image-to-video-turbo |
Image to video | Pricing |
wan-2.5-image-to-video |
Image to video | Pricing |
wan-2.6-image-to-video |
Image to video | Pricing |
wan-2.6-flash-image-to-video |
Image to video | Pricing |
wan-2.7-image-to-video |
Image to video | Pricing |
wan-2.6-edit-video |
Video edit | Pricing |
wan-2.6-flash-edit-video |
Video edit | Pricing |
wan-2.2-a14b-speech-to-video-turbo |
Speech to video | Pricing |
wan-2.2-animate-move |
Animation | Pricing |
wan-2.2-animate-replace |
Animation | Pricing |
wan-2.7-image |
Image generation | Pricing |
wan-2.7-image-pro |
Image generation | Pricing |
wan-2.7-r2v |
R2V text to video | Pricing |
wan-2.7-edit-video |
Video edit | Pricing |
Wan Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Generate text-to-video | Text to Video | POST |
| Get text-to-video status | Get Text-to-Video | GET |
| Generate image-to-video | Image to Video | POST |
| Get image-to-video status | Get Image-to-Video | GET |
| Generate speech-to-video | Speech to Video | POST |
| Get speech-to-video status | Get Speech-to-Video | GET |
| Generate animation | Animation | POST |
| Get animation status | Get Animation | GET |
| Generate image | Image Generation | POST |
| Get image status | Get Image | GET |
| Edit video | Video Edit | POST |
| Get video edit status | Get Video Edit | GET |
Wan Text to Video
Text-to-video generation:
curl -X POST "https://runapi.ai/api/v1/wan/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.6-text-to-video",
"prompt": "A timelapse of cherry blossoms falling in a Japanese garden",
"output_resolution": "720p",
"aspect_ratio": "16:9"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/wan/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'wan-2.6-text-to-video',
prompt: 'A timelapse of cherry blossoms falling in a Japanese garden',
output_resolution: '720p',
aspect_ratio: '16:9'
}.to_json
response = http.request(request)
puts response.body
import requests
response = requests.post(
'https://runapi.ai/api/v1/wan/text_to_video',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'wan-2.6-text-to-video',
'prompt': 'A timelapse of cherry blossoms falling in a Japanese garden',
'output_resolution': '720p',
'aspect_ratio': '16:9',
}
)
print(response.json())
fetch('https://runapi.ai/api/v1/wan/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'wan-2.6-text-to-video',
prompt: 'A timelapse of cherry blossoms falling in a Japanese garden',
output_resolution: '720p',
aspect_ratio: '16:9'
})
}).then(r => r.json()).then(console.log);
Response:
{
"id": "task_abc123"
}
POST /api/v1/wan/text_to_video
Creates an asynchronous text-to-video generation task. Poll the status endpoint to retrieve the result.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model version. One of: wan-2.2-a14b-text-to-video-turbo, wan-2.5-text-to-video, wan-2.6-text-to-video, wan-2.7-text-to-video, wan-2.7-r2v
|
prompt |
string | Yes | Text description of the video to generate |
callback_url |
string | No | Webhook URL for async completion notification |
duration_seconds |
number | No | Duration in seconds. Required for wan-2.5-*
|
output_resolution |
string | No | Output resolution (e.g. 720p, 1080p) |
aspect_ratio |
string | No | Aspect ratio: 16:9, 9:16, 1:1, 4:3, 3:4. Not applicable for wan-2.7-*
|
ratio |
string | No | Alternative ratio format for wan-2.7-* only |
negative_prompt |
string | No | What to avoid in the video. Supported by wan-2.5-* and wan-2.7-*
|
reference_image_urls |
array | No | Reference image URLs for wan-2.7-r2v. At least one reference image or video URL is required |
reference_video_urls |
array | No | Reference video URLs for wan-2.7-r2v. At least one reference image or video URL is required |
first_frame_image_url |
string | No | First frame image URL for wan-2.7-r2v
|
reference_audio_url |
string | No | Reference audio URL for wan-2.7-r2v
|
enable_prompt_expansion |
boolean | No | Auto-expand the prompt |
seed |
integer | No | Random seed for reproducibility |
acceleration |
string | No | Generation speed setting. wan-2.2-* only |
enable_safety_checker |
boolean | No | Content safety check toggle |
watermark |
boolean | No | Add watermark to the output. wan-2.7-* only |
background_audio_url |
string | No | Background audio URL. wan-2.7-text-to-video only |
Wan 2.7 R2V Text to Video
Generate a prompt-led video guided by reference images/videos:
curl -X POST "https://runapi.ai/api/v1/wan/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.7-r2v",
"prompt": "A person walking through autumn leaves",
"reference_image_urls": ["https://cdn.runapi.ai/public/samples/portrait.jpg"],
"output_resolution": "1080p"
}'
response = requests.post(
'https://runapi.ai/api/v1/wan/text_to_video',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'wan-2.7-r2v',
'prompt': 'A person walking through autumn leaves',
'reference_image_urls': ['https://cdn.runapi.ai/public/samples/portrait.jpg'],
'output_resolution': '1080p',
}
)
Response
| Field | Type | Description |
|---|---|---|
id |
string | Task ID used to poll for results |
Get Wan Text to Video
Poll for status:
curl "https://runapi.ai/api/v1/wan/text_to_video/TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
response = Net::HTTP.get(URI("https://runapi.ai/api/v1/wan/text_to_video/TASK_ID"),
'Authorization' => 'Bearer YOUR_API_TOKEN')
puts response
response = requests.get(
'https://runapi.ai/api/v1/wan/text_to_video/TASK_ID',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
fetch('https://runapi.ai/api/v1/wan/text_to_video/TASK_ID', {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
}).then(r => r.json()).then(console.log);
Completed response:
{
"id": "task_abc123",
"status": "completed",
"videos": [
{ "url": "https://file.runapi.ai/result.mp4" }
]
}
GET /api/v1/wan/text_to_video/:id
Retrieves the current status of a text-to-video task.
Response
| Field | Type | Description |
|---|---|---|
id |
string | Task ID |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video objects with url field (when completed) |
error |
string | Error message (when failed) |
Wan Image to Video
Image-to-video generation (2-6):
curl -X POST "https://runapi.ai/api/v1/wan/image_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.6-image-to-video",
"prompt": "The cat gently swishes its tail",
"first_frame_image_url": "https://cdn.runapi.ai/public/samples/image-to-video.jpg",
"output_resolution": "720p"
}'
response = requests.post(
'https://runapi.ai/api/v1/wan/image_to_video',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'wan-2.6-image-to-video',
'prompt': 'The cat gently swishes its tail',
'first_frame_image_url': 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
'output_resolution': '720p',
}
)
fetch('https://runapi.ai/api/v1/wan/image_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
model: 'wan-2.6-image-to-video',
prompt: 'The cat gently swishes its tail',
first_frame_image_url: 'https://cdn.runapi.ai/public/samples/image-to-video.jpg',
output_resolution: '720p'
})
}).then(r => r.json()).then(console.log);
POST /api/v1/wan/image_to_video
Creates an asynchronous image-to-video generation task.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model version. One of: wan-2.2-a14b-image-to-video-turbo, wan-2.5-image-to-video, wan-2.6-image-to-video, wan-2.6-flash-image-to-video, wan-2.7-image-to-video
|
prompt |
string | Conditional | Text prompt. Required for wan-2.6-* and wan-2.7-*; required for flash models |
callback_url |
string | No | Webhook URL for async completion notification |
first_frame_image_url |
string | Conditional | First frame source image URL. Required for wan-2.2-*, wan-2.5-*, and wan-2.6-*; optional for wan-2.7-* frame anchoring |
duration_seconds |
number | Conditional | Duration in seconds. Required for wan-2.5-*
|
output_resolution |
string | No | Output resolution |
aspect_ratio |
string | No | Aspect ratio. Supported by wan-2.2-* and wan-2.5-*
|
ratio |
string | No | Alternative ratio format. wan-2.7-* only |
negative_prompt |
string | No | What to avoid. Supported by wan-2.5-* and wan-2.7-*
|
enable_prompt_expansion |
boolean | No | Auto-expand prompt |
seed |
integer | No | Random seed |
enable_safety_checker |
boolean | No | Content safety check toggle |
watermark |
boolean | No | Add watermark. wan-2.7-* only |
audio |
boolean | No | Generate audio. Flash models only |
multi_shots |
boolean | No | Multi-shot mode. Flash models only |
last_frame_image_url |
string | No | Last frame image URL. wan-2.7-* only |
source_video_url |
string | No | Source video URL for continuation. wan-2.7-* only |
driving_audio_url |
string | No | Driving audio URL. wan-2.7-* only |
background_audio_url |
string | No | Background audio URL. wan-2.7-* only |
Get Wan Image to Video
GET /api/v1/wan/image_to_video/:id
Same response format as Get Text-to-Video.
Wan Speech to Video
Speech-driven talking-head video:
curl -X POST "https://runapi.ai/api/v1/wan/speech_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.2-a14b-speech-to-video-turbo",
"source_image_url": "https://cdn.runapi.ai/public/samples/portrait.jpg",
"source_audio_url": "https://cdn.runapi.ai/public/samples/voice.mp3"
}'
response = requests.post(
'https://runapi.ai/api/v1/wan/speech_to_video',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'wan-2.2-a14b-speech-to-video-turbo',
'source_image_url': 'https://cdn.runapi.ai/public/samples/portrait.jpg',
'source_audio_url': 'https://cdn.runapi.ai/public/samples/voice.mp3',
}
)
POST /api/v1/wan/speech_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | wan-2.2-a14b-speech-to-video-turbo |
source_image_url |
string | Yes | Portrait source image URL |
source_audio_url |
string | Yes | Driving speech audio URL |
prompt |
string | No | Additional description prompt |
callback_url |
string | No | Webhook URL for async completion notification |
num_frames |
integer | No | Number of output frames |
frames_per_second |
integer | No | Frames per second |
output_resolution |
string | No | Output resolution |
negative_prompt |
string | No | What to avoid |
seed |
integer | No | Random seed |
num_inference_steps |
integer | No | Denoising steps |
guidance_scale |
float | No | Classifier-free guidance scale |
shift |
float | No | Noise shift |
enable_safety_checker |
boolean | No | Content safety check toggle |
Get Wan Speech to Video
GET /api/v1/wan/speech_to_video/:id
Same response format as Get Text-to-Video.
Wan Animation
Animate a character using a motion reference video:
curl -X POST "https://runapi.ai/api/v1/wan/animate" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.2-animate-move",
"reference_video_url": "https://cdn.runapi.ai/public/samples/video.mp4",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg"
}'
response = requests.post(
'https://runapi.ai/api/v1/wan/animate',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'wan-2.2-animate-move',
'reference_video_url': 'https://cdn.runapi.ai/public/samples/video.mp4',
'source_image_url': 'https://cdn.runapi.ai/public/samples/image.jpg',
}
)
POST /api/v1/wan/animate
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
wan-2.2-animate-move or wan-2.2-animate-replace
|
reference_video_url |
string | Yes | Motion reference video URL |
source_image_url |
string | Yes | Character/subject image URL |
callback_url |
string | No | Webhook URL for async completion notification |
output_resolution |
string | No | Output resolution |
enable_safety_checker |
boolean | No | Content safety check toggle |
Get Wan Animation
GET /api/v1/wan/animate/:id
Same response format as Get Text-to-Video.
Wan Image Generation
Generate images from text:
curl -X POST "https://runapi.ai/api/v1/wan/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.7-image",
"prompt": "A surreal dreamscape with floating islands and waterfalls",
"aspect_ratio": "1:8"
}'
response = requests.post(
'https://runapi.ai/api/v1/wan/text_to_image',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'wan-2.7-image',
'prompt': 'A surreal dreamscape with floating islands and waterfalls',
'aspect_ratio': '1:8',
}
)
POST /api/v1/wan/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
wan-2.7-image or wan-2.7-image-pro
|
prompt |
string | Yes | Text description of the image |
callback_url |
string | No | Webhook URL for async completion notification |
aspect_ratio |
string | No |
1:1, 16:9, 4:3, 21:9, 3:4, 9:16, 8:1, 1:8
|
output_resolution |
string | No | Output resolution |
output_count |
integer | No | Number of images to generate |
enable_sequential |
boolean | No | Sequential generation mode |
thinking_mode |
boolean | No | Enhanced reasoning mode. wan-2.7-image-pro only |
watermark |
boolean | No | Add watermark |
seed |
integer | No | Random seed |
enable_safety_checker |
boolean | No | Content safety check toggle |
source_image_urls |
array | No | Source image URLs for image editing |
color_palette |
array | No | Color palette constraints. Each item: { hex, ratio }
|
bbox_list |
array | No | Bounding box constraints |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Task ID |
status |
string |
processing, completed, or failed
|
images |
array | Generated image objects with url field (when completed) |
error |
string | Error message (when failed) |
Get Wan Image
GET /api/v1/wan/text_to_image/:id
Returns the same format as the Image Generation response above.
Wan Video Edit
Edit an existing video with a text prompt:
curl -X POST "https://runapi.ai/api/v1/wan/edit_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "wan-2.7-edit-video",
"source_video_url": "https://cdn.runapi.ai/public/samples/video.mp4",
"prompt": "Make the sky more dramatic with storm clouds"
}'
response = requests.post(
'https://runapi.ai/api/v1/wan/edit_video',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'},
json={
'model': 'wan-2.7-edit-video',
'source_video_url': 'https://cdn.runapi.ai/public/samples/video.mp4',
'prompt': 'Make the sky more dramatic with storm clouds',
}
)
POST /api/v1/wan/edit_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
wan-2.6-edit-video, wan-2.6-flash-edit-video, or wan-2.7-edit-video
|
source_video_url |
string | Required for wan-2.7-edit-video
|
Source video URL |
source_video_urls |
array | Required for wan-2.6-edit-video and wan-2.6-flash-edit-video
|
Source video URLs |
prompt |
string | No | Text description of the desired edit |
callback_url |
string | No | Webhook URL for async completion notification |
negative_prompt |
string | No | What to avoid |
reference_image_url |
string | No | Reference image URL for style guidance |
output_resolution |
string | No | Output resolution |
aspect_ratio |
string | No | Output aspect ratio |
duration_seconds |
number | No | Output duration in seconds |
audio_setting |
string | No | Audio handling setting |
audio |
boolean | No | Generate audio. Flash model only |
multi_shots |
boolean | No | Multi-shot mode. Flash model only |
enable_prompt_expansion |
boolean | No | Auto-expand the prompt |
watermark |
boolean | No | Add watermark |
seed |
integer | No | Random seed |
enable_safety_checker |
boolean | No | Content safety check toggle |
Get Wan Video Edit
GET /api/v1/wan/edit_video/:id
Same response format as Get Text-to-Video.
Luma
Luma video modification turns an existing public video into a new variation guided by a prompt. Submit the source video once, poll the task status, or provide callback_url to receive the final result asynchronously.
Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create a video modification task | Create Luma Video Modification | POST |
| Check task status | Get Luma Video Modification | GET |
Create Luma Video Modification
Create a Luma video modification task:
curl -X POST "https://runapi.ai/api/v1/luma/modify_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"prompt": "Turn the street into a rainy cyberpunk night with neon reflections",
"source_video_url": "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4",
"callback_url": "https://your-domain.com/api/callbacks/luma",
"watermark": "demo-watermark"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/luma/modify_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
prompt: 'Turn the street into a rainy cyberpunk night with neon reflections',
source_video_url: 'https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4',
callback_url: 'https://your-domain.com/api/callbacks/luma',
watermark: 'demo-watermark'
}.to_json
response = http.request(request)
puts response.body
import requests
response = requests.post(
'https://runapi.ai/api/v1/luma/modify_video',
headers={
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
json={
'prompt': 'Turn the street into a rainy cyberpunk night with neon reflections',
'source_video_url': 'https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4',
'callback_url': 'https://your-domain.com/api/callbacks/luma',
'watermark': 'demo-watermark'
}
)
print(response.json())
fetch('https://runapi.ai/api/v1/luma/modify_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify({
prompt: 'Turn the street into a rainy cyberpunk night with neon reflections',
source_video_url: 'https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4',
callback_url: 'https://your-domain.com/api/callbacks/luma',
watermark: 'demo-watermark'
})
}).then(r => r.json()).then(console.log);
HTTP 202 - Task accepted:
{
"id": "task_abc123",
"status": "processing"
}
POST /api/v1/luma/modify_video
Creates an asynchronous video modification task.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
prompt |
string | Yes | English prompt describing how to transform the source video |
source_video_url |
string | Yes | Publicly accessible source video URL. Supports MP4, MOV, and AVI |
callback_url |
string | No | HTTPS webhook URL for completion events |
watermark |
string | No | Watermark identifier added to the generated video |
Response
| Field | Type | Description |
|---|---|---|
id |
string | Task ID used for polling or callbacks |
status |
string | Initial async status, typically processing
|
Callback Format
If callback_url is provided, a POST request will be sent when processing finishes.
Success callback example:
{
"id": "task_abc123",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/generated-video.mp4"
}
],
"sources": [
{
"url": "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4"
}
]
}
Failed callback example:
{
"id": "task_abc123",
"status": "failed",
"error": "Modify record generation failed."
}
Get Luma Video Modification
Get task status and result:
curl -X GET "https://runapi.ai/api/v1/luma/modify_video/task_abc123" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = 'task_abc123'
uri = URI("https://runapi.ai/api/v1/luma/modify_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
task_id = 'task_abc123'
response = requests.get(
f'https://runapi.ai/api/v1/luma/modify_video/{task_id}',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
const taskId = 'task_abc123';
fetch(`https://runapi.ai/api/v1/luma/modify_video/${taskId}`, {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
}).then(r => r.json()).then(console.log);
HTTP 200 - Completed:
{
"id": "task_abc123",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/generated-video.mp4"
}
],
"sources": [
{
"url": "https://storage.googleapis.com/gtv-videos-bucket/sample/ForBiggerJoyrides.mp4"
}
]
}
GET /api/v1/luma/modify_video/:id
Returns the latest task status. Luma's provider-specific callback failure state is normalized to completed because the generation itself has succeeded.
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Task ID |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video URLs when the task is complete |
sources |
array | Source video URLs used for the modification |
error |
string | Error message when the task fails |
Hailuo
Hailuo on RunAPI provides two async resources: text-to-video and image-to-video.
Models
| Model | Endpoint | Notes |
|---|---|---|
hailuo-02-text-to-video-pro |
POST /api/v1/hailuo/text_to_video |
Fixed pro text-to-video profile |
hailuo-02-text-to-video-standard |
POST /api/v1/hailuo/text_to_video |
Standard text-to-video, supports duration_seconds
|
hailuo-02-image-to-video-pro |
POST /api/v1/hailuo/image_to_video |
Fixed pro image-to-video profile |
hailuo-02-image-to-video-standard |
POST /api/v1/hailuo/image_to_video |
Supports duration_seconds, output_resolution, optional last_frame_image_url
|
hailuo-2.3-image-to-video-pro |
POST /api/v1/hailuo/image_to_video |
Supports duration_seconds, output_resolution
|
hailuo-2.3-image-to-video-standard |
POST /api/v1/hailuo/image_to_video |
Supports duration_seconds, output_resolution
|
Create Text-to-Video
POST https://runapi.ai/api/v1/hailuo/text_to_video
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
hailuo-02-text-to-video-pro or hailuo-02-text-to-video-standard
|
prompt |
string | Yes | Up to 1500 characters |
duration_seconds |
number | No |
6 or 10. Supported by hailuo-02-text-to-video-standard only. Defaults to 6. |
prompt_optimizer |
boolean | No | Enable Hailuo prompt optimization |
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | HTTPS callback URL |
Create Image-to-Video
POST https://runapi.ai/api/v1/hailuo/image_to_video
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Any supported Hailuo image-to-video model |
prompt |
string | Yes | Up to 1500 characters on hailuo-02-*; up to 5000 on hailuo-2.3-*
|
first_frame_image_url |
string | Yes | Public first-frame image URL |
last_frame_image_url |
string | No | Last-frame image URL. hailuo-02-* only. |
duration_seconds |
number | No |
6 or 10 on standard / 2.3 models |
output_resolution |
string | No |
512p / 768p on hailuo-02-image-to-video-standard; 768p / 1080p on 2.3 models |
prompt_optimizer |
boolean | No |
hailuo-02-* only |
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | HTTPS callback URL |
Query Task Status
GET /api/v1/hailuo/text_to_video/:idGET /api/v1/hailuo/image_to_video/:id
Completed response:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://tempfile.runapi.ai/hailuo/output.mp4"
}
]
}
HappyHorse
HappyHorse on RunAPI creates asynchronous text, image, and edit-video tasks with 720p or 1080p output. Text-to-video also supports a character-guided model that uses ordered reference images.
HappyHorse Endpoints
| Task | Endpoint | Models |
|---|---|---|
| Text-to-video | Create HappyHorse Text-to-Video |
happyhorse-text-to-video, happyhorse-character
|
| Image-to-video | Create HappyHorse Image-to-Video | happyhorse-image-to-video |
| Edit video | Create HappyHorse Edit Video | happyhorse-edit-video |
| Check video status | Get HappyHorse Text-to-Video Status | - |
| Check image-to-video status | Get HappyHorse Image-to-Video Status | - |
| Check edit-video status | Get HappyHorse Edit Video Status | - |
Create HappyHorse Text-to-Video
curl -X POST "https://runapi.ai/api/v1/happyhorse/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-text-to-video",
"prompt": "A tiny paper horse gallops through a miniature cardboard city at night, cinematic camera move, warm window lights.",
"output_resolution": "1080p",
"aspect_ratio": "16:9",
"duration_seconds": 5,
"seed": 491001,
"callback_url": "https://your-domain.com/api/callback"
}'
Character-guided text-to-video uses the same endpoint with the happyhorse-character model and reference_image_urls:
curl -X POST "https://runapi.ai/api/v1/happyhorse/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-character",
"prompt": "Character1 walks through a paper city and waves at character2 during a gentle cinematic camera move.",
"reference_image_urls": [
"https://cdn.runapi.ai/public/samples/reference-1.jpg",
"https://cdn.runapi.ai/public/samples/reference-2.jpg"
],
"output_resolution": "1080p",
"aspect_ratio": "16:9",
"duration_seconds": 5,
"seed": 493001,
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
POST https://runapi.ai/api/v1/happyhorse/text_to_video
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes |
happyhorse-text-to-video or happyhorse-character
|
prompt |
string | Yes | Video prompt, max 5000 non-Chinese characters or 2500 Chinese characters |
reference_image_urls |
array | Required for happyhorse-character
|
1-9 public reference image URLs; order maps to character1, character2, and so on |
output_resolution |
string | No |
720p or 1080p; defaults to 1080p
|
aspect_ratio |
string | No |
16:9, 9:16, 1:1, 4:3, or 3:4; defaults to 16:9
|
duration_seconds |
integer | No | Integer from 3 to 15 seconds; defaults to 5 |
seed |
integer | No | Integer from 0 to 2147483647 |
callback_url |
string | No | HTTPS callback URL for completion events |
Get HappyHorse Text-to-Video Status
curl "https://runapi.ai/api/v1/happyhorse/text_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
HTTP 200
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/happyhorse/output.mp4"
}
]
}
GET https://runapi.ai/api/v1/happyhorse/text_to_video/:id
| Parameter | Description |
|---|---|
id |
Task ID returned by POST /api/v1/happyhorse/text_to_video
|
If callback_url is provided when creating the task, RunAPI sends the same completed or failed response body when the task finishes.
Create HappyHorse Edit Video
curl -X POST "https://runapi.ai/api/v1/happyhorse/edit_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-edit-video",
"prompt": "Make the horse-headed humanoid character in the video wear the striped sweater from the reference image.",
"source_video_url": "https://cdn.runapi.ai/public/samples/video.mp4",
"reference_image_urls": [
"https://cdn.runapi.ai/public/samples/reference-1.jpg"
],
"output_resolution": "1080p",
"audio_setting": "auto",
"seed": 494001,
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
POST https://runapi.ai/api/v1/happyhorse/edit_video
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be happyhorse-edit-video
|
prompt |
string | Yes | Edit instruction, max 5000 non-Chinese characters or 2500 Chinese characters |
source_video_url |
string | Yes | One public MP4 or MOV source video URL; 3-60 seconds |
reference_image_urls |
array | No | 0-5 public reference image URLs |
output_resolution |
string | No |
720p or 1080p; defaults to 1080p
|
audio_setting |
string | No |
auto or original; defaults to auto
|
seed |
integer | No | Integer from 0 to 2147483647 |
callback_url |
string | No | HTTPS callback URL for completion events |
Get HappyHorse Edit Video Status
curl "https://runapi.ai/api/v1/happyhorse/edit_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
HTTP 200
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/happyhorse/output.mp4"
}
]
}
GET https://runapi.ai/api/v1/happyhorse/edit_video/:id
| Parameter | Description |
|---|---|
id |
Task ID returned by POST /api/v1/happyhorse/edit_video
|
If callback_url is provided when creating the task, RunAPI sends the same completed or failed response body when the task finishes.
Create HappyHorse Image-to-Video
curl -X POST "https://runapi.ai/api/v1/happyhorse/image_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "happyhorse-image-to-video",
"first_frame_image_url": "https://cdn.runapi.ai/public/samples/image-to-video.jpg",
"prompt": "Bring the still frame to life with a gentle cinematic camera move.",
"output_resolution": "1080p",
"duration_seconds": 5,
"seed": 492001,
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
POST https://runapi.ai/api/v1/happyhorse/image_to_video
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be happyhorse-image-to-video
|
first_frame_image_url |
string | Yes | Public first-frame image URL |
prompt |
string | No | Video prompt, max 5000 non-Chinese characters or 2500 Chinese characters |
output_resolution |
string | No |
720p or 1080p; defaults to 1080p
|
duration_seconds |
integer | No | Integer from 3 to 15 seconds; defaults to 5 |
seed |
integer | No | Integer from 0 to 2147483647 |
callback_url |
string | No | HTTPS callback URL for completion events |
Get HappyHorse Image-to-Video Status
curl "https://runapi.ai/api/v1/happyhorse/image_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
HTTP 200
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/happyhorse/output.mp4"
}
]
}
GET https://runapi.ai/api/v1/happyhorse/image_to_video/:id
| Parameter | Description |
|---|---|
id |
Task ID returned by POST /api/v1/happyhorse/image_to_video
|
If callback_url is provided when creating the task, RunAPI sends the same completed or failed response body when the task finishes.
GPT Image
GPT Image is an advanced image generation model line supporting text-to-image generation and image editing with high-quality output in multiple aspect ratios.
Models
| Model | Type |
|---|---|
gpt-image-1.5 |
Text-to-image |
gpt-image-1.5 |
Edit image |
GPT Image Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create text-to-image | Create Text-to-Image | POST |
| Check text-to-image status | Get Text-to-Image Status | GET |
| Edit image | Edit Image | POST |
| Check edit status | Get Edit Status | GET |
Create Text-to-Image
Text-to-image generation:
curl -X POST "https://runapi.ai/api/v1/gpt_image/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-1.5",
"prompt": "A photorealistic portrait of an astronaut floating in space with Earth in the background",
"aspect_ratio": "1:1",
"quality": "high"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/gpt_image/text_to_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'gpt-image-1.5',
prompt: 'A photorealistic portrait of an astronaut floating in space with Earth in the background',
aspect_ratio: '1:1',
quality: 'high'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/gpt_image/text_to_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'gpt-image-1.5',
'prompt': 'A photorealistic portrait of an astronaut floating in space with Earth in the background',
'aspect_ratio': '1:1',
'quality': 'high'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'gpt-image-1.5',
prompt: 'A photorealistic portrait of an astronaut floating in space with Earth in the background',
aspect_ratio: '1:1',
quality: 'high'
};
fetch('https://runapi.ai/api/v1/gpt_image/text_to_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing"
}
Create a new text-to-image task using GPT Image.
HTTP Request
POST https://runapi.ai/api/v1/gpt_image/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | gpt-image-1.5 |
prompt |
string | Yes | Text description of the desired image |
aspect_ratio |
string | Yes | Output aspect ratio: 1:1, 2:3, 3:2
|
quality |
string | Yes | Output quality: medium, high
|
callback_url |
string | No | URL for completion callback |
Response
Returns a task ID for checking text-to-image status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial status: processing
|
Callback Format
If callback_url is provided, a POST request will be sent when the task completes.
Success Response:
- Includes status: "completed"
- Contains images array with generated image URLs
Failed Response:
- Includes status: "failed"
- Contains error message describing the failure reason
Success callback example:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png"
}
]
}
Failed callback example:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "failed",
"error": "Task failed"
}
Get Text-to-Image Status
Poll for text-to-image results:
curl "https://runapi.ai/api/v1/gpt_image/text_to_image/a1b2c3d4-e5f6-7890-abcd-ef1234567890" \
-H "Authorization: Bearer YOUR_API_TOKEN"
require 'net/http'
require 'uri'
task_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
uri = URI("https://runapi.ai/api/v1/gpt_image/text_to_image/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
task_id = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890'
url = f'https://runapi.ai/api/v1/gpt_image/text_to_image/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = 'a1b2c3d4-e5f6-7890-abcd-ef1234567890';
fetch(`https://runapi.ai/api/v1/gpt_image/text_to_image/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Processing:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing"
}
Completed:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/generated/image1.png"
}
]
}
Failed:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "failed",
"error": "Generation failed."
}
Retrieve the status and results of a GPT Image text-to-image task.
HTTP Request
GET https://runapi.ai/api/v1/gpt_image/text_to_image/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The ID of the task returned from the create request |
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
images |
array | Array of image objects (only present when status is completed) |
images[].url |
string | CDN URL of the generated image |
error |
string | Error message (only present when status is failed) |
Status Values
- processing: Task is being processed (waiting, queuing, or generating)
- completed: Generation completed successfully
- failed: Generation failed
Edit Image
Edit image:
curl -X POST "https://runapi.ai/api/v1/gpt_image/edit_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-1.5",
"prompt": "Transform this photo into a vibrant oil painting with warm autumn colors",
"source_image_urls": [
"https://cdn.runapi.ai/public/samples/image.jpg"
],
"aspect_ratio": "3:2",
"quality": "high"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/gpt_image/edit_image')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'gpt-image-1.5',
prompt: 'Transform this photo into a vibrant oil painting with warm autumn colors',
source_image_urls: [
'https://cdn.runapi.ai/public/samples/image.jpg'
],
aspect_ratio: '3:2',
quality: 'high'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/gpt_image/edit_image'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'gpt-image-1.5',
'prompt': 'Transform this photo into a vibrant oil painting with warm autumn colors',
'source_image_urls': [
'https://cdn.runapi.ai/public/samples/image.jpg'
],
'aspect_ratio': '3:2',
'quality': 'high'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'gpt-image-1.5',
prompt: 'Transform this photo into a vibrant oil painting with warm autumn colors',
source_image_urls: [
'https://cdn.runapi.ai/public/samples/image.jpg'
],
aspect_ratio: '3:2',
quality: 'high'
};
fetch('https://runapi.ai/api/v1/gpt_image/edit_image', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"status": "processing"
}
Create a new image editing task using GPT Image.
HTTP Request
POST https://runapi.ai/api/v1/gpt_image/edit_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | gpt-image-1.5 |
prompt |
string | Yes | Text description of the desired edit |
source_image_urls |
array | Yes | Source image URLs (max 16 images) |
aspect_ratio |
string | Yes | Output aspect ratio: 1:1, 2:3, 3:2
|
quality |
string | Yes | Output quality: medium, high
|
callback_url |
string | No | URL for completion callback |
Response
Returns a task ID for checking edit status:
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Initial status: processing
|
Callback Format
If callback_url is provided, a POST request will be sent when editing completes.
Success callback example:
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/edited/image1.png"
}
]
}
Failed callback example:
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"status": "failed",
"error": "Edit failed"
}
Get Edit Status
Poll for edit results:
curl "https://runapi.ai/api/v1/gpt_image/edit_image/b2c3d4e5-f6a7-8901-bcde-f01234567891" \
-H "Authorization: Bearer YOUR_API_TOKEN"
require 'net/http'
require 'uri'
task_id = 'b2c3d4e5-f6a7-8901-bcde-f01234567891'
uri = URI("https://runapi.ai/api/v1/gpt_image/edit_image/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
task_id = 'b2c3d4e5-f6a7-8901-bcde-f01234567891'
url = f'https://runapi.ai/api/v1/gpt_image/edit_image/{task_id}'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(url, headers=headers)
print(response.json())
const taskId = 'b2c3d4e5-f6a7-8901-bcde-f01234567891';
fetch(`https://runapi.ai/api/v1/gpt_image/edit_image/${taskId}`, {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Processing:
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"status": "processing"
}
Completed:
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/edited/image1.png"
}
]
}
Failed:
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"status": "failed",
"error": "Edit failed."
}
Retrieve the status and results of a GPT Image image editing task.
HTTP Request
GET https://runapi.ai/api/v1/gpt_image/edit_image/:id
URL Parameters
| Parameter | Description |
|---|---|
id |
The ID of the task returned from the create request |
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Unique task identifier |
status |
string | Task status: processing, completed, failed
|
images |
array | Array of image objects (only present when status is completed) |
images[].url |
string | CDN URL of the edited image |
error |
string | Error message (only present when status is failed) |
Status Values
- processing: Task is being processed (waiting, queuing, or generating)
- completed: Edit completed successfully
- failed: Edit failed
GPT Image 2
GPT Image 2 supports text-to-image generation and image editing.
Models
| Model | Type |
|---|---|
gpt-image-2 |
Text-to-image |
gpt-image-2 |
Edit image |
Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create text-to-image | Create Text-to-Image | POST |
| Check text-to-image status | Get Text-to-Image Status | GET |
| Edit image | Edit Image | POST |
| Check edit status | Get Edit Status | GET |
GPT Image 2 Create Text-to-Image
Text-to-image generation:
curl -X POST "https://runapi.ai/api/v1/gpt_image_2/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "A cinematic night city poster with neon reflections on a rainy street",
"aspect_ratio": "16:9",
"output_resolution": "2k"
}'
POST https://runapi.ai/api/v1/gpt_image_2/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | gpt-image-2 |
prompt |
string | Yes | Text prompt, maximum 20,000 characters |
aspect_ratio |
string | No |
auto, 1:1, 9:16, 16:9, 4:3, 3:4
|
output_resolution |
string | No |
1k, 2k, 4k. A 1:1 image cannot be 4k; auto/unset only supports 1k |
callback_url |
string | No | URL for completion callback |
Response
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing"
}
GPT Image 2 Get Text-to-Image Status
GET https://runapi.ai/api/v1/gpt_image_2/text_to_image/:id
Returns the current task status and images when the task completes.
GPT Image 2 Edit
Edit an image:
curl -X POST "https://runapi.ai/api/v1/gpt_image_2/edit_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-image-2",
"prompt": "Transform this product image into a premium e-commerce poster style",
"source_image_urls": ["https://raw.githubusercontent.com/github/explore/main/topics/python/python.png"],
"aspect_ratio": "16:9",
"output_resolution": "2k"
}'
POST https://runapi.ai/api/v1/gpt_image_2/edit_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | gpt-image-2 |
prompt |
string | Yes | Text prompt, maximum 20,000 characters |
source_image_urls |
array | Yes | Array of source image URLs, maximum 16 images |
aspect_ratio |
string | No |
auto, 1:1, 9:16, 16:9, 4:3, 3:4
|
output_resolution |
string | No |
1k, 2k, 4k. A 1:1 image cannot be 4k; auto/unset only supports 1k |
callback_url |
string | No | URL for completion callback |
Response
{
"id": "b2c3d4e5-f6a7-8901-bcde-f01234567891",
"status": "processing"
}
GPT Image 2 Get Edit Status
GET https://runapi.ai/api/v1/gpt_image_2/edit_image/:id
Returns the current task status and images when the task completes.
GPT-4o Image
GPT-4o Image supports prompt-only, image-guided, and variant image tasks.
Models
| Model | Type |
|---|---|
gpt-4o-image |
Text-to-image / image-guided / variants |
Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Create text-to-image task | Create Text-to-Image | POST |
| Check task status | Get Text-to-Image Status | GET |
GPT-4o Image Create Text-to-Image
Prompt-only, image-guided, or variant request:
curl -X POST "https://runapi.ai/api/v1/gpt_4o_image/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o-image",
"prompt": "A premium watch product photo on black stone",
"aspect_ratio": "1:1",
"source_image_urls": ["https://raw.githubusercontent.com/github/explore/main/topics/python/python.png"],
"output_count": 2
}'
POST https://runapi.ai/api/v1/gpt_4o_image/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | gpt-4o-image |
prompt |
string | Conditionally | Required when source_image_urls is blank |
aspect_ratio |
string | Yes |
1:1, 3:2, 2:3
|
source_image_urls |
array | No | Up to 5 source image URLs |
mask_url |
string | No | Mask image URL for selective changes |
output_count |
integer | No | Number of generated images: 1, 2, or 4
|
enable_prompt_expansion |
boolean | No | Prompt expansion toggle |
callback_url |
string | No | URL for completion callback |
Response
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "processing"
}
GPT-4o Image Get Text-to-Image Status
GET https://runapi.ai/api/v1/gpt_4o_image/text_to_image/:id
Returns the current task status, progress, and images when the task completes.
Grok-Imagine
Grok-Imagine is a multimodal generation family on RunAPI. It supports text-to-video, image-to-video, text-to-image, image editing, plus extend and upscale operations on prior video tasks.
Model Variants
| Model | Description | Best For |
|---|---|---|
grok-imagine-text-to-video |
Text-driven video generation with motion style control | Ideation and stylised shorts |
grok-imagine-image-to-video |
Animate one or more reference images into video | Bringing stills to motion |
grok-imagine-text-to-image |
Text-driven image generation with optional Pro quality | Key-art and concept images |
grok-imagine-edit-image |
Edit a single source image | Style re-interpretation |
Grok-Imagine Endpoints
| Task | Endpoint | Model |
|---|---|---|
| Create text-to-video task | Create Text-to-Video | grok-imagine-text-to-video |
| Create image-to-video task | Create Image-to-Video | grok-imagine-image-to-video |
| Create text-to-image task | Create Text-to-Image | grok-imagine-text-to-image |
| Create edit-image task | Create Edit Image | grok-imagine-edit-image |
| Extend a prior video | Extend Video | - |
| Upscale a prior video | Upscale Video | - |
| Check task status | Get Task Status | - |
Lifecycle
All six create endpoints are asynchronous. A successful POST returns HTTP 202 with { "id", "status": "processing" }. Poll the matching GET /:id endpoint, or supply a callback_url, to receive the final result.
Create Grok-Imagine Text-to-Video
Create a text-to-video task:
curl -X POST "https://runapi.ai/api/v1/grok_imagine/text_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-text-to-video",
"prompt": "A neon dragon gliding over a rain-soaked Tokyo street at night",
"aspect_ratio": "16:9",
"motion_style": "normal",
"duration_seconds": 10,
"output_resolution": "720p",
"enable_safety_checker": false,
"callback_url": "https://your-domain.com/api/callback"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/grok_imagine/text_to_video')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
model: 'grok-imagine-text-to-video',
prompt: 'A neon dragon gliding over a rain-soaked Tokyo street at night',
aspect_ratio: '16:9',
motion_style: 'normal',
duration_seconds: 10,
output_resolution: '720p',
enable_safety_checker: false,
callback_url: 'https://your-domain.com/api/callback'
}.to_json
response = http.request(request)
puts response.body
import requests
url = 'https://runapi.ai/api/v1/grok_imagine/text_to_video'
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'model': 'grok-imagine-text-to-video',
'prompt': 'A neon dragon gliding over a rain-soaked Tokyo street at night',
'aspect_ratio': '16:9',
'motion_style': 'normal',
'duration_seconds': 10,
'output_resolution': '720p',
'enable_safety_checker': False,
'callback_url': 'https://your-domain.com/api/callback'
}
response = requests.post(url, headers=headers, json=data)
print(response.json())
const data = {
model: 'grok-imagine-text-to-video',
prompt: 'A neon dragon gliding over a rain-soaked Tokyo street at night',
aspect_ratio: '16:9',
motion_style: 'normal',
duration_seconds: 10,
output_resolution: '720p',
enable_safety_checker: false,
callback_url: 'https://your-domain.com/api/callback'
};
fetch('https://runapi.ai/api/v1/grok_imagine/text_to_video', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/grok_imagine/text_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be grok-imagine-text-to-video
|
prompt |
string | Yes | Up to 5000 characters |
aspect_ratio |
string | No |
2:3, 3:2, 1:1, 16:9, 9:16. Defaults to 2:3
|
motion_style |
string | No |
fun, normal, or spicy. Defaults to normal
|
duration_seconds |
integer | No | Seconds, 6 through 30
|
output_resolution |
string | No |
480p or 720p. Defaults to 480p
|
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | HTTPS callback URL for completion events |
Create Grok-Imagine Image-to-Video
Create an image-to-video task from reference images:
curl -X POST "https://runapi.ai/api/v1/grok_imagine/image_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-image-to-video",
"source_image_urls": [
"https://cdn.runapi.ai/public/samples/image-to-video.jpg"
],
"prompt": "Slow dolly-in on the subject as wind ruffles the hair",
"motion_style": "normal",
"duration_seconds": 8,
"output_resolution": "720p",
"aspect_ratio": "16:9",
"enable_safety_checker": false,
"callback_url": "https://your-domain.com/api/callback"
}'
Alternatively, animate an image generated by a prior
grok-imagine-text-to-imagetask:
curl -X POST "https://runapi.ai/api/v1/grok_imagine/image_to_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-image-to-video",
"source_task_id": "a1b2c3d4-e5f6-7890-abcd-1234567890ab",
"index": 0,
"prompt": "Gentle camera orbit around the subject",
"duration_seconds": 10,
"output_resolution": "720p"
}'
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/grok_imagine/image_to_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be grok-imagine-image-to-video
|
source_image_urls |
array | Conditional | Exactly one source image URL. Required unless source_task_id is provided |
source_task_id |
string | Conditional | Task id from a prior grok-imagine-text-to-image task. Required unless source_image_urls is provided |
index |
integer | No | When using source_task_id, selects which generated image to animate (0-5) |
prompt |
string | No | Motion guidance prompt |
motion_style |
string | No |
fun, normal, or spicy. spicy is not available when source_image_urls is used |
duration_seconds |
integer | No | Seconds, 6 through 30
|
output_resolution |
string | No |
480p or 720p
|
aspect_ratio |
string | No |
2:3, 3:2, 1:1, 16:9, 9:16. Defaults to 16:9
|
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | HTTPS callback URL |
Create Grok-Imagine Text-to-Image
Create a text-to-image task:
curl -X POST "https://runapi.ai/api/v1/grok_imagine/text_to_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-text-to-image",
"prompt": "A cyberpunk fox warrior standing on a rooftop overlooking a neon city",
"aspect_ratio": "1:1",
"enable_pro": true,
"enable_safety_checker": false,
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/grok_imagine/text_to_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be grok-imagine-text-to-image
|
prompt |
string | Yes | Up to 5000 characters |
aspect_ratio |
string | No |
2:3, 3:2, 1:1, 16:9, 9:16. Defaults to 1:1
|
enable_pro |
boolean | No | Enable Pro quality mode. Content safety check toggle |
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | HTTPS callback URL |
Create Grok-Imagine Edit Image
Create an edit-image task:
curl -X POST "https://runapi.ai/api/v1/grok_imagine/edit_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "grok-imagine-edit-image",
"source_image_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"prompt": "Reimagine the subject as an oil painting",
"enable_safety_checker": false,
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/grok_imagine/edit_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Must be grok-imagine-edit-image
|
source_image_url |
string | Yes | Source image URL |
prompt |
string | No | Edit prompt |
enable_safety_checker |
boolean | No | Content safety check toggle |
callback_url |
string | No | HTTPS callback URL |
Extend Grok-Imagine Video
Extend a previously generated Grok-Imagine video:
curl -X POST "https://runapi.ai/api/v1/grok_imagine/extend_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_task_id": "a1b2c3d4-e5f6-7890-abcd-1234567890ab",
"prompt": "Continue with the dragon soaring above the skyline",
"start_seconds": 6,
"extension_duration_seconds": 6,
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/grok_imagine/extend_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
source_task_id |
string | Yes | Task id of a prior Grok-Imagine video task (grok-imagine-text-to-video or grok-imagine-image-to-video) owned by the same account |
prompt |
string | Yes | Prompt guiding the extension |
start_seconds |
integer | Yes | Seconds into the source video where the extension begins |
extension_duration_seconds |
integer | Yes |
6 or 10 — the duration of the appended segment in seconds |
callback_url |
string | No | HTTPS callback URL |
Upscale Grok-Imagine Video
Upscale a previously generated Grok-Imagine video:
curl -X POST "https://runapi.ai/api/v1/grok_imagine/upscale_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"source_task_id": "a1b2c3d4-e5f6-7890-abcd-1234567890ab",
"callback_url": "https://your-domain.com/api/callback"
}'
HTTP 202 - Task accepted:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "processing"
}
HTTP Request
POST https://runapi.ai/api/v1/grok_imagine/upscale_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
source_task_id |
string | Yes | Task id of a prior Grok-Imagine video task owned by the same account. Only 480p source videos are supported for upscale. |
callback_url |
string | No | HTTPS callback URL |
Get Grok-Imagine Task Status
Each create endpoint has a matching status endpoint at the same path plus /:id. Poll it to check whether the task is still running, or inspect the final result.
| Created via | Status endpoint |
|---|---|
POST /api/v1/grok_imagine/text_to_video |
GET /api/v1/grok_imagine/text_to_video/:id |
POST /api/v1/grok_imagine/image_to_video |
GET /api/v1/grok_imagine/image_to_video/:id |
POST /api/v1/grok_imagine/text_to_image |
GET /api/v1/grok_imagine/text_to_image/:id |
POST /api/v1/grok_imagine/edit_image |
GET /api/v1/grok_imagine/edit_image/:id |
POST /api/v1/grok_imagine/extend_video |
GET /api/v1/grok_imagine/extend_video/:id |
POST /api/v1/grok_imagine/upscale_image |
GET /api/v1/grok_imagine/upscale_image/:id |
Check a Grok-Imagine task:
curl "https://runapi.ai/api/v1/grok_imagine/text_to_video/YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_TOKEN"
task_id = 'YOUR_TASK_ID'
uri = URI("https://runapi.ai/api/v1/grok_imagine/text_to_video/#{task_id}")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
task_id = 'YOUR_TASK_ID'
response = requests.get(
f'https://runapi.ai/api/v1/grok_imagine/text_to_video/{task_id}',
headers={'Authorization': 'Bearer YOUR_API_TOKEN'}
)
print(response.json())
const taskId = 'YOUR_TASK_ID';
fetch(`https://runapi.ai/api/v1/grok_imagine/text_to_video/${taskId}`, {
headers: { 'Authorization': 'Bearer YOUR_API_TOKEN' }
})
.then(res => res.json())
.then(data => console.log(data));
Completed video response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"videos": [
{
"url": "https://file.runapi.ai/grok-imagine/generated-video.mp4"
}
]
}
Completed image response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "completed",
"images": [
{
"url": "https://file.runapi.ai/grok-imagine/generated-image.png"
}
]
}
Failed response example:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"status": "failed",
"error": "Generation failed"
}
Response Fields
| Field | Type | Description |
|---|---|---|
id |
string | Task identifier |
status |
string |
processing, completed, or failed
|
videos |
array | Generated video URLs when a video task completes |
images |
array | Generated image URLs when an image task completes |
error |
string | Error message when the task fails |
Callback Format
If callback_url was provided when creating the task, a POST request with the same body as the completed/failed response above is sent when the task finishes.
Topaz
Topaz provides dedicated RunAPI endpoints for image and video upscaling.
Models
| Model | Type | Pricing |
|---|---|---|
topaz-upscale-image |
Image upscale | Pricing |
topaz-upscale-video |
Video upscale | Pricing |
Endpoints
| Task | Endpoint | Method |
|---|---|---|
| Upscale image | Upscale Image | POST |
| Check image status | Get Upscale Image Status | GET |
| Upscale video | Upscale Video | POST |
| Check video status | Get Upscale Video Status | GET |
Topaz Upscale Image
curl -X POST "https://runapi.ai/api/v1/topaz/upscale_image" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "topaz-upscale-image",
"source_image_url": "https://cdn.runapi.ai/public/samples/upscale.jpg",
"upscale_factor": 4
}'
POST https://runapi.ai/api/v1/topaz/upscale_image
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | topaz-upscale-image |
source_image_url |
string | Yes | Public input image URL |
upscale_factor |
number | Yes |
1, 2, 4, or 8
|
callback_url |
string | No | URL for completion callback |
Response
{
"id": "img-task-123",
"status": "processing"
}
Topaz Get Upscale Image Status
GET https://runapi.ai/api/v1/topaz/upscale_image/:id
Returns the current task status and images when the task completes.
Topaz Upscale Video
curl -X POST "https://runapi.ai/api/v1/topaz/upscale_video" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "topaz-upscale-video",
"source_video_url": "https://cdn.runapi.ai/public/samples/video-lowres.mp4",
"upscale_factor": 2
}'
POST https://runapi.ai/api/v1/topaz/upscale_video
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | topaz-upscale-video |
source_video_url |
string | Yes | Public input video URL |
upscale_factor |
number | No |
1, 2, or 4; omitted uses the service default |
callback_url |
string | No | URL for completion callback |
Response
{
"id": "vid-task-123",
"status": "processing"
}
Topaz Get Upscale Video Status
GET https://runapi.ai/api/v1/topaz/upscale_video/:id
Returns the current task status and videos when the task completes.
GPT
Access OpenAI GPT language models through RunAPI. The API supports both the Chat Completions format (GPT 5.2) and the Responses API format (GPT 5.5, GPT 5.4, GPT Codex).
SDK Quickstart
Using the OpenAI Python SDK:
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_TOKEN",
base_url="https://runapi.ai/v1"
)
# Chat Completions (GPT 5.2)
response = client.chat.completions.create(
model="gpt-5.2",
messages=[
{"role": "user", "content": "Hello!"}
]
)
print(response.choices[0].message.content)
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'YOUR_API_TOKEN',
baseURL: 'https://runapi.ai/v1',
});
// Chat Completions (GPT 5.2)
const response = await client.chat.completions.create({
model: 'gpt-5.2',
messages: [
{ role: 'user', content: 'Hello!' }
],
});
console.log(response.choices[0].message.content);
require "openai"
client = OpenAI::Client.new(
access_token: "YOUR_API_TOKEN",
uri_base: "https://runapi.ai/v1"
)
response = client.chat(
parameters: {
model: "gpt-5.2",
messages: [
{ role: "user", content: "Hello!" }
]
}
)
puts response.dig("choices", 0, "message", "content")
curl -X POST "https://runapi.ai/v1/chat/completions" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.2",
"messages": [
{"role": "user", "content": "Hello!"}
]
}'
Set base_url to https://runapi.ai/v1 and use your RunAPI API key.
Models
| Model | Version String | Endpoint | Capabilities |
|---|---|---|---|
| GPT 5.5 | gpt-5.5 |
/v1/responses |
Chat, multimodal input, web search, function calling, reasoning |
| GPT 5.2 | gpt-5.2 |
/v1/chat/completions |
Chat, web search, reasoning effort |
| GPT 5.4 | gpt-5.4 |
/v1/responses |
Chat, multimodal input, web search, function calling, reasoning |
| GPT Codex | gpt-5-codex |
/v1/responses |
Code generation, multimodal, web search, function calling, reasoning |
| GPT Codex | gpt-5.1-codex |
/v1/responses |
Code generation, multimodal, web search, function calling, reasoning |
| GPT Codex | gpt-5.2-codex |
/v1/responses |
Code generation, multimodal, web search, function calling, reasoning |
| GPT Codex | gpt-5.3-codex |
/v1/responses |
Code generation, multimodal, web search, function calling, reasoning |
| GPT Codex | gpt-5.4-codex |
/v1/responses |
Code generation, multimodal, web search, function calling, reasoning |
List Models
List GPT models in OpenAI-compatible format:
curl -X GET "https://runapi.ai/v1/models" \
-H "Authorization: Bearer YOUR_API_TOKEN"
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_TOKEN", base_url="https://runapi.ai/v1")
models = client.models.list()
for model in models.data:
print(model.id)
{
"object": "list",
"data": [
{
"id": "gpt-5.5",
"object": "model",
"created": 1714900000,
"owned_by": "system"
}
]
}
GET /v1/models
Returns available GPT models in OpenAI List Models format. If the API key has allowed_models restrictions, only permitted models are returned.
Chat Completions
Endpoint: POST https://runapi.ai/v1/chat/completions
For GPT 5.2. Compatible with the OpenAI Chat Completions API format.
Chat Completions request:
curl -X POST "https://runapi.ai/v1/chat/completions" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.2",
"messages": [
{"role": "user", "content": "Explain quantum computing in one sentence."}
],
"reasoning_effort": "high"
}'
response = client.chat.completions.create(
model="gpt-5.2",
messages=[
{"role": "user", "content": "Explain quantum computing in one sentence."}
]
)
const response = await client.chat.completions.create({
model: 'gpt-5.2',
messages: [
{ role: 'user', content: 'Explain quantum computing in one sentence.' }
],
});
response = client.chat(
parameters: {
model: "gpt-5.2",
messages: [
{ role: "user", content: "Explain quantum computing in one sentence." }
]
}
)
Response:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1700000000,
"model": "gpt-5.2",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Quantum computing uses qubits..."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 15,
"completion_tokens": 50,
"total_tokens": 65
}
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model ID: gpt-5.2
|
messages |
array | Yes | Conversation messages with role and content
|
tools |
array | No | Tool definitions. Supports web_search
|
reasoning_effort |
string | No |
"low" or "high" (default: "high") |
stream |
boolean | No | Enable SSE streaming (default: false) |
Messages Format
Messages support multimodal content with text and images:
{
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{"type": "image_url", "image_url": {"url": "https://cdn.runapi.ai/public/samples/image.jpg"}}
]
}
]
}
Responses
Endpoint: POST https://runapi.ai/v1/responses
For GPT 5.5, GPT 5.4, and GPT Codex models. Uses the OpenAI Responses API format with multimodal input, reasoning, and tool support.
Responses API request:
curl -X POST "https://runapi.ai/v1/responses" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4",
"input": "Explain the theory of relativity.",
"reasoning": {"effort": "medium"}
}'
import httpx
response = httpx.post(
"https://runapi.ai/v1/responses",
headers={"x-api-key": "YOUR_API_TOKEN"},
json={
"model": "gpt-5.4",
"input": "Explain the theory of relativity.",
"reasoning": {"effort": "medium"}
}
)
print(response.json())
const response = await fetch('https://runapi.ai/v1/responses', {
method: 'POST',
headers: {
'x-api-key': 'YOUR_API_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'gpt-5.4',
input: 'Explain the theory of relativity.',
reasoning: { effort: 'medium' },
}),
});
const data = await response.json();
require "net/http"
require "json"
uri = URI("https://runapi.ai/v1/responses")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request["x-api-key"] = "YOUR_API_TOKEN"
request["Content-Type"] = "application/json"
request.body = {
model: "gpt-5.4",
input: "Explain the theory of relativity.",
reasoning: { effort: "medium" }
}.to_json
response = http.request(request)
puts JSON.parse(response.body)
Response:
{
"output": [
{
"type": "message",
"id": "msg_abc123",
"role": "assistant",
"content": [
{"type": "output_text", "text": "The theory of relativity..."}
],
"status": "completed"
}
],
"usage": {
"input_tokens": 12,
"output_tokens": 200,
"total_tokens": 212
},
"status": "completed"
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model ID: gpt-5.5, gpt-5.4, gpt-5-codex, gpt-5.1-codex, gpt-5.2-codex, gpt-5.3-codex, gpt-5.4-codex
|
input |
string or array | Yes | Text string or array of input items |
stream |
boolean | No | Enable SSE streaming (default: false) |
reasoning |
object | No | `{"effort": "minimal"\ |
tools |
array | No | Web search or function calling tools (mutually exclusive) |
tool_choice |
string | No | Set to "auto" when using function tools |
Input Format
The input field accepts a simple string or an array of message objects with multimodal content:
{
"input": [
{"role": "user", "content": [
{"type": "input_text", "text": "Describe this image"},
{"type": "input_image", "image_url": "https://cdn.runapi.ai/public/samples/image.jpg"}
]}
]
}
Supported content types: input_text, input_image, input_file.
Web Search Tool
{
"tools": [{"type": "web_search"}]
}
Function Calling
{
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
}
],
"tool_choice": "auto"
}
Streaming
Streaming request (Chat Completions):
curl -X POST "https://runapi.ai/v1/chat/completions" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.2",
"stream": true,
"messages": [
{"role": "user", "content": "Write a haiku about coding."}
]
}'
Streaming request (Responses API):
curl -X POST "https://runapi.ai/v1/responses" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4",
"stream": true,
"input": "Write a haiku about coding."
}'
Set "stream": true to receive Server-Sent Events (SSE). The response streams incrementally with Content-Type: text/event-stream.
Responses API streaming events
| Event | Description |
|---|---|
response.output_text.delta |
Incremental text content |
response.function_call_arguments.delta |
Incremental function call arguments |
response.completed |
Final event with usage data |
Authentication
| Method | Header | Example |
|---|---|---|
| API Key | x-api-key |
x-api-key: YOUR_API_TOKEN |
| Bearer | Authorization |
Authorization: Bearer YOUR_API_TOKEN |
Billing
Token-based billing: credits are calculated from actual input and output token usage. A provisional amount is reserved before the request, then adjusted to the actual usage after completion.
Gemini
Access Google Gemini language models through RunAPI using the OpenAI Chat Completions format.
SDK Quickstart
Using the OpenAI Python SDK:
from openai import OpenAI
client = OpenAI(
api_key="YOUR_API_TOKEN",
base_url="https://runapi.ai/v1beta/openai"
)
response = client.chat.completions.create(
model="gemini-2.5-flash",
messages=[
{"role": "user", "content": "Hello!"}
]
)
print(response.choices[0].message.content)
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: 'YOUR_API_TOKEN',
baseURL: 'https://runapi.ai/v1beta/openai',
});
const response = await client.chat.completions.create({
model: 'gemini-2.5-flash',
messages: [
{ role: 'user', content: 'Hello!' }
],
});
console.log(response.choices[0].message.content);
require "openai"
client = OpenAI::Client.new(
access_token: "YOUR_API_TOKEN",
uri_base: "https://runapi.ai/v1beta/openai"
)
response = client.chat(
parameters: {
model: "gemini-2.5-flash",
messages: [
{ role: "user", content: "Hello!" }
]
}
)
puts response.dig("choices", 0, "message", "content")
curl -X POST "https://runapi.ai/v1beta/openai/chat/completions" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-2.5-flash",
"messages": [
{"role": "user", "content": "Hello!"}
]
}'
For the closest drop-in replacement experience, set base_url to https://runapi.ai/v1beta/openai and use your RunAPI API key.
Models
| Model | Version String | Endpoint | Capabilities |
|---|---|---|---|
| Gemini 2.5 Flash | gemini-2.5-flash |
/v1beta/openai/chat/completions |
Chat, multimodal input, Google Search, structured output, thoughts |
| Gemini 2.5 Pro | gemini-2.5-pro |
/v1beta/openai/chat/completions |
Chat, multimodal input, Google Search, structured output, thoughts, reasoning effort |
| Gemini 3.1 Pro | gemini-3.1-pro-preview |
/v1beta/openai/chat/completions |
Chat, multimodal input, Google Search, thoughts, reasoning effort |
| Gemini 3.5 Flash | gemini-3.5-flash |
/v1beta/models/gemini-3.5-flash:streamGenerateContent |
Streaming contents requests, multimodal input, function calling, thoughts |
| Gemini 3 Flash | gemini-3-flash-preview |
/v1beta/openai/chat/completions or /v1beta/models/gemini-3-flash-preview:streamGenerateContent
|
Chat, multimodal, function calling, structured output, thoughts, reasoning effort — also supports the contents streaming endpoint |
| Gemini 3 Pro | gemini-3-pro-preview |
/v1beta/openai/chat/completions |
Chat, multimodal input, Google Search, structured output, thoughts, reasoning effort |
List Models
List Gemini models:
curl -X GET "https://runapi.ai/v1beta/models" \
-H "x-api-key: YOUR_API_TOKEN"
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_TOKEN", base_url="https://runapi.ai/v1beta/openai")
models = client.models.list()
for model in models.data:
print(model.id)
{
"models": [
{
"name": "models/gemini-3.5-flash",
"version": "001",
"displayName": "Gemini 3.5 Flash",
"description": "Gemini 3.5 Flash language model",
"inputTokenLimit": 1048576,
"outputTokenLimit": 65536,
"supportedGenerationMethods": ["streamGenerateContent"]
}
]
}
GET /v1beta/models
Returns available Gemini models in the Google AI models.list format. If the API key has allowed_models restrictions, only permitted models are returned.
Alternatively, when using the OpenAI SDK with base_url="https://runapi.ai/v1beta/openai", calling client.models.list() will hit /v1beta/openai/models which returns OpenAI-format model list.
Chat Completions
Endpoint: POST https://runapi.ai/v1beta/openai/chat/completions
Compatible with the OpenAI Chat Completions request format.
Gemini request:
curl -X POST "https://runapi.ai/v1beta/openai/chat/completions" \
-H "x-api-key: YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-2.5-pro",
"messages": [
{"role": "user", "content": "Summarize the latest AI news."}
],
"include_thoughts": true,
"reasoning_effort": "high"
}'
Structured output:
{
"model": "gemini-3-pro-preview",
"messages": [
{"role": "user", "content": "Extract the title and sentiment."}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "article_summary",
"schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"sentiment": {"type": "string"}
},
"required": ["title", "sentiment"]
}
}
}
}
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
model |
string | Yes | Model ID: gemini-2.5-flash, gemini-2.5-pro, gemini-3.1-pro-preview, gemini-3-flash-preview, or gemini-3-pro-preview
|
messages |
array | Yes | Conversation messages with role and content
|
tools |
array | No | Tool definitions. Google Search uses googleSearch; custom functions follow OpenAI-style tool objects |
stream |
boolean | No | Enable SSE streaming |
include_thoughts |
boolean | No | Include Gemini thought traces in the response |
reasoning_effort |
string | No |
low or high; supported on all Gemini models here except gemini-2.5-flash
|
response_format |
object | No | Structured output schema. response_format.type must be json_schema
|
Notes
-
response_formatand custom function calling are mutually exclusive -
response_formatcan be used with the fixed Google Search tool -
reasoning_effortis not available ongemini-2.5-flash - Messages support multimodal content using the standard OpenAI-style content array
Additional Examples
{
"model": "gemini-3.1-pro-preview",
"messages": [
{"role": "user", "content": "Summarize the latest market news."}
],
"include_thoughts": true,
"reasoning_effort": "high",
"tools": [
{"type": "function", "function": {"name": "googleSearch"}}
]
}
Gemini Contents Streaming
Endpoint: POST https://runapi.ai/v1beta/models/gemini-3-flash-preview:streamGenerateContent or POST https://runapi.ai/v1beta/models/gemini-3.5-flash:streamGenerateContent
Gemini 3 Flash and Gemini 3.5 Flash support contents requests at the streaming endpoint. Model is inferred from the URL path.
{
"stream": true,
"contents": [
{
"role": "user",
"parts": [
{ "text": "What is the weather in Beijing today?" }
]
}
],
"tools": [
{
"functionDeclarations": [
{
"name": "get_weather_forecast",
"description": "Get the weather forecast for a given location",
"parameters": {
"type": "OBJECT",
"properties": {
"location": { "type": "STRING" }
},
"required": ["location"]
}
}
]
}
],
"generationConfig": {
"thinkingConfig": {
"includeThoughts": true,
"thinkingLevel": "high"
}
}
}
- You can authenticate with
Authorization: Bearer,x-goog-api-key, or?key= - Model routing is inferred from the request path, so
modelis not required in the request body -
contentsis required -
generationConfig.thinkingConfig.thinkingLevelsupportsloworhigh
{
"model": "gemini-3-flash-preview",
"messages": [
{"role": "user", "content": "Call a tool if needed."}
],
"reasoning_effort": "low",
"tools": [
{
"type": "function",
"function": {
"name": "extract_entities",
"parameters": {
"type": "object"
}
}
}
]
}
DeepSeek
DeepSeek's reasoning-first language models are available through RunAPI on two surfaces: the OpenAI-compatible Chat Completions API and the Anthropic-compatible Messages API. Point either SDK at RunAPI and pass a DeepSeek model id.
Models: deepseek-v4-flash (fast, low-cost; optional thinking mode) and deepseek-v4-pro (frontier reasoning and agentic work).
SDK Quickstart
OpenAI SDK — Chat Completions (
https://runapi.ai/v1):
curl https://runapi.ai/v1/chat/completions \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-flash",
"messages": [{"role": "user", "content": "Hello!"}]
}'
from openai import OpenAI
client = OpenAI(api_key="YOUR_API_TOKEN", base_url="https://runapi.ai/v1")
resp = client.chat.completions.create(
model="deepseek-v4-flash",
messages=[{"role": "user", "content": "Hello!"}],
)
print(resp.choices[0].message.content)
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: 'YOUR_API_TOKEN', baseURL: 'https://runapi.ai/v1' });
const resp = await client.chat.completions.create({
model: 'deepseek-v4-flash',
messages: [{ role: 'user', content: 'Hello!' }],
});
console.log(resp.choices[0].message.content);
Anthropic SDK — Messages (
https://runapi.ai):
import anthropic
client = anthropic.Anthropic(api_key="YOUR_API_TOKEN", base_url="https://runapi.ai")
msg = client.messages.create(
model="deepseek-v4-flash",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
print(msg.content[0].text)
Models
| Model | Description |
|---|---|
deepseek-v4-flash |
Fast, low-cost tier. Thinking (reasoning) mode is an optional request parameter. |
deepseek-v4-pro |
Frontier reasoning for the most complex and agentic workloads. |
Both models appear in GET /v1/models under both the Anthropic and OpenAI SDK list formats.
Billing
Token-based: credits are calculated from actual input/output token usage. Cached prompt tokens are billed at a reduced cache-read rate.
File Upload
Uploaded files and their returned download_url links are temporary and expire after 24 hours.
Upload via URL
Download a file from a URL and upload it to storage.
HTTP Request
POST https://runapi.ai/api/v1/files/from_url
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file_url |
string | Yes | HTTPS URL of the file to download |
file_name |
string | No | Custom filename for the uploaded file |
Example Request
Upload file from URL:
curl -X POST "https://runapi.ai/api/v1/files/from_url" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"file_url": "https://cdn.runapi.ai/public/samples/image.jpg",
"file_name": "downloaded-image.png"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/files/from_url')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
file_url: 'https://cdn.runapi.ai/public/samples/image.jpg',
file_name: 'downloaded-image.png'
}.to_json
response = http.request(request)
puts response.body
import requests
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'file_url': 'https://cdn.runapi.ai/public/samples/image.jpg',
'file_name': 'downloaded-image.png'
}
response = requests.post(
'https://runapi.ai/api/v1/files/from_url',
headers=headers,
json=data
)
print(response.json())
const data = {
file_url: 'https://cdn.runapi.ai/public/samples/image.jpg',
file_name: 'downloaded-image.png'
};
fetch('https://runapi.ai/api/v1/files/from_url', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
File Limits
- Maximum size: 50MB
- Allowed types:
- Images: JPEG, PNG, WebP
- Audio: MPEG, WAV
Security
- Only HTTPS URLs are allowed
- Private/loopback IP addresses are blocked
- Maximum 5 redirects allowed
- 30 second download timeout
Response
HTTP 201 - Upload successful:
{
"file_name": "downloaded-image.png",
"download_url": "https://your-cdn.com/...",
"file_size": 204800,
"mime_type": "image/png",
"uploaded_at": "2025-01-24T10:30:00Z"
}
The returned download_url can be used as upload_url in audio endpoints (for example, Suno).
The link is temporary and expires after 24 hours.
Error Responses
Error example:
{ "error": "Missing required parameter: file_url" }
Upload via Stream
Upload a file directly using multipart/form-data.
HTTP Request
POST https://runapi.ai/api/v1/files/from_stream
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
file |
file | Yes | The file to upload |
file_name |
string | No | Custom filename for the uploaded file |
Example Request
Upload file from stream:
curl -X POST "https://runapi.ai/api/v1/files/from_stream" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: multipart/form-data" \
-F "file=@/path/to/file.png" \
-F "file_name=my-uploaded-image.png"
require 'net/http'
require 'uri'
uri = URI('https://runapi.ai/api/v1/files/from_stream')
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
form_data = [
['file', File.open('/path/to/file.png')],
['file_name', 'my-uploaded-image.png']
]
request.set_form(form_data, 'multipart/form-data')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
response = http.request(request)
puts response.body
import requests
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
files = {
'file': open('/path/to/file.png', 'rb')
}
data = {
'file_name': 'my-uploaded-image.png'
}
response = requests.post(
'https://runapi.ai/api/v1/files/from_stream',
headers=headers,
files=files,
data=data
)
print(response.json())
const formData = new FormData();
formData.append('file', fileInput.files[0]);
formData.append('file_name', 'my-uploaded-image.png');
fetch('https://runapi.ai/api/v1/files/from_stream', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
},
body: formData
})
.then(response => response.json())
.then(data => console.log(data));
File Limits
- Maximum size: 50MB
- Allowed types:
- Images: JPEG, PNG, WebP
- Audio: MPEG, WAV
Response
HTTP 201 - Upload successful:
{
"file_name": "my-uploaded-image.png",
"download_url": "https://your-cdn.com/...",
"file_size": 204800,
"mime_type": "image/png",
"uploaded_at": "2025-01-24T10:30:00Z"
}
The returned download_url is temporary and expires after 24 hours.
Error Responses
Error example:
{ "error": "Missing required parameter: file" }
Upload via Base64
Upload a file from Base64 encoded data.
HTTP Request
POST https://runapi.ai/api/v1/files/from_base64
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
base64_data |
string | Yes | Base64 encoded file data (with or without data URL prefix) |
file_name |
string | No | Custom filename for the uploaded file |
Example Request
Upload file from Base64:
curl -X POST "https://runapi.ai/api/v1/files/from_base64" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"base64_data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...",
"file_name": "base64-image.png"
}'
require 'net/http'
require 'uri'
require 'json'
uri = URI('https://runapi.ai/api/v1/files/from_base64')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
request['Content-Type'] = 'application/json'
request.body = {
base64_data: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...',
file_name: 'base64-image.png'
}.to_json
response = http.request(request)
puts response.body
import requests
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
}
data = {
'base64_data': 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...',
'file_name': 'base64-image.png'
}
response = requests.post(
'https://runapi.ai/api/v1/files/from_base64',
headers=headers,
json=data
)
print(response.json())
const data = {
base64_data: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...',
file_name: 'base64-image.png'
};
fetch('https://runapi.ai/api/v1/files/from_base64', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN',
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
})
.then(response => response.json())
.then(data => console.log(data));
File Limits
- Maximum size: 10MB (decoded size)
- Allowed types:
- Images: JPEG, PNG, WebP
- Audio: MPEG, WAV
Base64 Format
- With data URL prefix:
data:image/png;base64,iVBORw0KG... - Raw base64:
iVBORw0KG...
Response
HTTP 201 - Upload successful:
{
"file_name": "base64-image.png",
"download_url": "https://your-cdn.com/...",
"file_size": 68,
"mime_type": "image/png",
"uploaded_at": "2025-01-24T10:30:00Z"
}
The returned download_url is temporary and expires after 24 hours.
Error Responses
Error example:
{ "error": "Missing required parameter: base64_data" }
Common
Get Balance
Retrieve the current account balance and usage statistics.
HTTP Request
GET https://runapi.ai/api/v1/me/balance
Authentication
Include your API key in the Authorization header:
Authorization: Bearer YOUR_API_TOKEN
Example Request
Get user credits:
curl "https://runapi.ai/api/v1/me/balance" \
-H "Authorization: Bearer YOUR_API_TOKEN"
require 'net/http'
require 'uri'
uri = URI('https://runapi.ai/api/v1/me/balance')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Authorization'] = 'Bearer YOUR_API_TOKEN'
response = http.request(request)
puts response.body
import requests
headers = {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
response = requests.get(
'https://runapi.ai/api/v1/me/balance',
headers=headers
)
print(response.json())
fetch('https://runapi.ai/api/v1/me/balance', {
method: 'GET',
headers: {
'Authorization': 'Bearer YOUR_API_TOKEN'
}
})
.then(response => response.json())
.then(data => console.log(data));
Response
HTTP 200 - Success:
{
"balance_cents": 10000,
"spent_cents_today": 150,
"spent_cents_total": 5420
}
Response Fields
-
balance_cents: Current remaining balance available for API usage, in USD cents -
spent_cents_today: Total amount consumed today, in USD cents -
spent_cents_total: Cumulative amount consumed across all time, in USD cents
Error Response
Error example:
{
"error": "Unauthorized"
}
Notes
- Balance is account-specific
- If a user belongs to multiple accounts, switch accounts to view different balances
Management Keys
Use management keys to create, list, update, and revoke standard API keys programmatically.
Authentication
Include your management key in the Authorization header:
Authorization: Bearer YOUR_MANAGEMENT_KEY
Generate management keys from the Management Keys page.
List API Keys
HTTP Request
GET https://runapi.ai/api/v1/keys
Example Request
curl "https://runapi.ai/api/v1/keys" \
-H "Authorization: Bearer YOUR_MANAGEMENT_KEY"
Response
[
{
"name": "Production",
"last_used_at": "2026-04-08T02:58:00Z",
"created_at": "2026-04-08T02:58:00Z",
"credit_limit_cents": 100,
"credit_limit_reset_interval": "daily",
"allowed_models": null,
"enabled": true,
"id": "token_123",
"masked_token": "abcd••••••••wxyz",
"guardrail_id": "guardrail_abc123"
}
]
Create API Key
HTTP Request
POST https://runapi.ai/api/v1/keys
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | No | Friendly name for the API key |
credit_limit_cents |
integer | No | Per-key spending cap in USD cents |
credit_limit_reset_interval |
string | No | Reset cadence: daily, weekly, or monthly. Omit to make credit_limit_cents a lifetime cap. |
allowed_models |
string[] | No | Restrict this key to a fixed set of models (e.g. ["gpt-5.4", "claude-opus-4-6"]). Omit, send null, or an empty array to allow all models. |
enabled |
boolean | No | When false, requests authenticated with this key are rejected with 401 Unauthorized. Defaults to true. |
guardrail_id |
string | No | Attach a guardrail policy to the new key (e.g. "guardrail_abc123"). |
When credit_limit_cents is set without credit_limit_reset_interval, the cap is a lifetime budget that does not refresh. With an interval, the cap resets each daily/weekly/monthly window.
When allowed_models is set, requests using a model outside the list are rejected with 422 Unprocessable Content.
To attach a reusable guardrail at creation time, include guardrail_id. See Guardrails below for the full lifecycle.
Example Request
curl -X POST "https://runapi.ai/api/v1/keys" \
-H "Authorization: Bearer YOUR_MANAGEMENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production",
"credit_limit_cents": 100,
"credit_limit_reset_interval": "daily",
"allowed_models": ["gpt-5.4", "claude-opus-4-6"]
}'
Response
{
"name": "Production",
"last_used_at": null,
"created_at": "2026-04-08T02:58:00Z",
"credit_limit_cents": 100,
"credit_limit_reset_interval": "daily",
"allowed_models": ["gpt-5.4", "claude-opus-4-6"],
"enabled": true,
"id": "token_123",
"masked_token": "abcd••••••••wxyz",
"guardrail_id": "guardrail_abc123",
"key": "abcd1234efgh5678"
}
The key value is only returned once when the API key is created.
Get API Key
HTTP Request
GET https://runapi.ai/api/v1/keys/:id
Example Request
curl "https://runapi.ai/api/v1/keys/token_123" \
-H "Authorization: Bearer YOUR_MANAGEMENT_KEY"
Response
{
"name": "Production",
"last_used_at": null,
"created_at": "2026-04-08T02:58:00Z",
"credit_limit_cents": 100,
"credit_limit_reset_interval": "daily",
"allowed_models": ["gpt-5.4", "claude-opus-4-6"],
"enabled": true,
"id": "token_123",
"masked_token": "abcd••••••••wxyz",
"guardrail_id": "guardrail_abc123",
"credit_usage_cents_total": 25,
"credit_usage_cents_today": 25,
"credit_usage_cents_this_week": 25,
"credit_usage_cents_this_month": 25
}
Update API Key
HTTP Request
PATCH https://runapi.ai/api/v1/keys/:id
Example Request
curl -X PATCH "https://runapi.ai/api/v1/keys/token_123" \
-H "Authorization: Bearer YOUR_MANAGEMENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Production (rotated)",
"credit_limit_cents": 500,
"credit_limit_reset_interval": "weekly",
"allowed_models": ["gpt-5.4"]
}'
To clear a limit, send credit_limit_cents as null or an empty string.
allowed_models is overwritten absolutely on each PATCH. Send null or [] to clear the whitelist; omit the field to leave it unchanged.
Send "enabled": false to immediately revoke authentication for this key, or "enabled": true to re-enable it. The flag is independent of credit_limit_cents — clearing the cap and disabling the key in the same request both take effect.
To replace or remove the attached guardrail, use the Guardrail Assignments endpoints — PATCH /api/v1/keys/:id does not accept guardrail_id.
Delete API Key
HTTP Request
DELETE https://runapi.ai/api/v1/keys/:id
Example Request
curl -X DELETE "https://runapi.ai/api/v1/keys/token_123" \
-H "Authorization: Bearer YOUR_MANAGEMENT_KEY"
Response
HTTP 204 No Content
Credit Limit Notes
-
dailyresets at midnight UTC -
weeklyresets every Monday at midnight UTC -
monthlyresets on the first day of each calendar month at midnight UTC - Requests that push usage over the limit still complete; subsequent requests are blocked until the next reset
Guardrails
Guardrails are reusable policy templates owned by an account. A guardrail can specify model and provider access rules (allowlist or blocklist), a total spending cap, and per-model spending caps with their own reset cadence. Each API key or account member may have at most one direct guardrail; on every request, the key's own limits and any attached guardrails are checked independently.
Any management key can create guardrails in its account. Account admins can update or delete any guardrail. Non-admin management keys can update or delete guardrails they created only while those guardrails are assigned solely to their own API keys; shared-key assignments and member-level assignments require admin.
List Guardrails
HTTP Request
GET https://runapi.ai/api/v1/guardrails
Response
{
"guardrails": [
{
"id": "guardrail_abc123",
"name": "Tier 1",
"description": "Standard developer tier",
"enabled": true,
"credit_limit_cents": 100000,
"credit_limit_reset_interval": "monthly",
"allowed_models": ["gpt-5.4", "claude-sonnet-4-6"],
"blocked_models": [],
"allowed_providers": [],
"blocked_providers": [],
"model_budgets": [
{
"match_mode": "exclude",
"models": ["gpt-5.4", "claude-sonnet-4-6"],
"credit_limit_cents": 1000,
"credit_limit_reset_interval": null
}
],
"assignment_count": 4,
"created_at": "2026-05-05T10:00:00Z"
}
]
}
Create Guardrail
HTTP Request
POST https://runapi.ai/api/v1/guardrails
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Unique guardrail name within the account (case-insensitive) |
description |
string | No | Free-form description |
enabled |
boolean | No | Defaults to true
|
credit_limit_cents |
integer | No | Total spend cap across all keys/members assigned to this guardrail. Must be set together with credit_limit_reset_interval. |
credit_limit_reset_interval |
string | No |
daily, weekly, or monthly
|
allowed_models |
string[] | No | Allowlist of public model identifiers. Empty/null means "all allowed". |
blocked_models |
string[] | No | Blocklist; always wins over the allowlist. |
allowed_providers |
string[] | No | Allowlist of provider names. Empty/null means "all allowed". |
blocked_providers |
string[] | No | Blocklist of provider names. |
model_budgets |
object[] | No | Scoped model spend caps. Each entry: `{ "match_mode": "include" |
Model budgets only count requests in their scope; they do not reject requests outside the scope. include counts only listed models. exclude counts every model except listed models, which is fail-safe for future model launches because new models are counted by default.
Example Request
curl -X POST "https://runapi.ai/api/v1/guardrails" \
-H "Authorization: Bearer YOUR_MANAGEMENT_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Tier 1",
"credit_limit_cents": 100000,
"credit_limit_reset_interval": "monthly",
"allowed_models": ["gpt-5.4", "claude-sonnet-4-6"],
"model_budgets": [
{
"match_mode": "exclude",
"models": ["gpt-5.4", "claude-sonnet-4-6"],
"credit_limit_cents": 1000,
"credit_limit_reset_interval": null
}
]
}'
Response
201 Created — the response body is identical to the list shape above, wrapped in {"guardrail": {...}}.
Get Guardrail
GET https://runapi.ai/api/v1/guardrails/:id
Returns {"guardrail": {...}}.
Update Guardrail
PATCH https://runapi.ai/api/v1/guardrails/:id
Account admins can update any guardrail. Non-admin management keys can update guardrails they created only while those guardrails are assigned solely to their own API keys.
Scalar fields (name, description, enabled, credit_limit_cents, credit_limit_reset_interval) are overwritten when present and preserved when omitted.
Rule arrays (allowed_models, blocked_models, allowed_providers, blocked_providers) and model_budgets are absolute replacement when present. Send null or [] to clear; omit to leave unchanged.
Delete Guardrail
DELETE https://runapi.ai/api/v1/guardrails/:id
Account admins can delete any guardrail. Non-admin management keys can delete guardrails they created only while those guardrails are assigned solely to their own API keys.
Soft-deletes the guardrail and destroys all its assignments. Returns 204 No Content. The name becomes available for reuse.
Guardrail Assignments
A guardrail assignment links a single guardrail to a single API key OR a single account member. Each target may have at most one direct assignment.
List Assignments
GET https://runapi.ai/api/v1/guardrail_assignments
Returns {"guardrail_assignments": [...]}. Account admins see all assignments in the account; non-admin management keys see only assignments on their own API keys.
Create Assignment
POST https://runapi.ai/api/v1/guardrail_assignments
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
guardrail_id |
string | Yes | Prefix ID of the guardrail (e.g. guardrail_abc123) |
api_token_id |
string | One of | Prefix ID of the API key to attach the guardrail to |
account_user_id |
string | One of | Prefix ID of the account member (admin only) |
Provide exactly one of api_token_id or account_user_id.
Permissions
- Non-admin management keys may only assign guardrails to their own API keys.
- Account admins may assign to any standard API key in the account.
- Member-level assignment requires admin.
Replacement
To replace the guardrail attached to a key or member, DELETE the existing assignment and create a new one. 422 Unprocessable Content is returned if the target already has a direct assignment.
Response
{
"guardrail_assignment": {
"id": "guardrail_assignment_xyz",
"guardrail_id": "guardrail_abc123",
"api_token_id": "token_123",
"account_user_id": null,
"created_at": "2026-05-05T10:00:00Z"
}
}
Delete Assignment
DELETE https://runapi.ai/api/v1/guardrail_assignments/:id
Returns 204 No Content. Removes the guardrail from its target without affecting the guardrail or the target itself.
Errors
The runapi.ai API uses standard HTTP response codes to indicate the success or failure of requests.
HTTP Status Codes
| Status Code | Meaning |
|---|---|
| 200 | OK - Request succeeded |
| 201 | Created - Resource created successfully |
| 400 | Bad Request - Invalid parameters or malformed request |
| 401 | Unauthorized - Invalid or missing API key |
| 402 | Payment Required - Account has insufficient credits |
| 403 | Forbidden - Valid key but insufficient permissions or API key credit limit reached |
| 404 | Not Found - Resource doesn't exist |
| 422 | Unprocessable Entity - Validation failed |
| 429 | Too Many Requests - Rate limit exceeded |
| 500 | Internal Server Error - Something went wrong on our end |
| 503 | Service Unavailable - Temporary service interruption |
Error Response Format
Error response example:
{
"error": "Missing required parameter: prompt"
}
All error responses follow a simple JSON format with an error field containing a human-readable error message.
Common Error Messages
| Status Code | Error Message | Description |
|---|---|---|
| 400 | Missing required parameter: xxx |
Required parameter is missing |
| 401 | Unauthorized |
Invalid or missing API key |
| 402 | Insufficient credits |
Account has insufficient credits |
| 403 | API key credit limit exceeded |
The API key reached its configured spend limit for the current reset period |
| 404 | Task not found |
The specified task doesn't exist |
| 404 | Record does not exist |
The requested resource doesn't exist |
| 408 | Download timeout |
File download timed out |
| 413 | File size exceeds maximum allowed size |
Uploaded file is too large (max 10MB) |
| 415 | File type not supported |
Unsupported file type |
| 422 | Records are being generated |
Resource is still processing |
| 500 | Internal server error |
Server encountered an error |
Task-Specific Errors
When a task fails, the error message is included in the task response:
{
"id": "task-id",
"status": "failed",
"error": "Generation failed due to content policy violation"
}