API Documentation
Generate media from a prompt, get permanent CDN URLs. Authenticate with an API key — no session required.
Skip the boilerplate — use the instant_media skill
The official instant_media skill wraps this entire API for any
SKILL.md-compatible AI agent: images, music (lyrics-to-song), narrated videos with subtitles,
TTS, transcription, screenshots — one call each, permanent URL back in ~50ms, placeholders
serving while the real render lands. Works with api.builder2.com
or any self-hosted instance.
git clone https://github.com/digimon99/instant_media.git <skills-dir>/instant_media
# then set: BUILDER2_API_KEY=bk2_... # or BUILDER2_BASE_URL for self-hosted
1 Authentication
All endpoints require an Authorization: Bearer header with an API key.
API Key
Keys start with bk2_. They don't expire. Manage them from the dashboard.
# Example: authenticated request
curl https://api.builder2.com/api/v1/user \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Missing or invalid key
{ "error": "Invalid API key" }
2 Generate Media
/api/v1/build-media
Send a prompt to generate media. Creates a job, generates variants, and returns a permanent CDN URL immediately.
| Field | Type | Required | Description |
|---|---|---|---|
| prompt | string | Yes | Text description of desired media |
| content_type | string | Yes | image | infographic | image_proxy | page_screenshot | video | tts | music |
| media_slug | string | No | URL-friendly name (auto-slugified from prompt if omitted) |
| count | int | No | Number of variants (1–4, default 1) |
| webhook_url | string | No | URL to receive a callback when the job completes |
| character_id | string | No | For image type: uses the character's default avatar as a reference image with an image-edit model. For other types: injects character traits into the prompt. |
| character_slug | string | No | Alternative to character_id — use the character's URL slug |
| character_image_url | string | No | Custom reference image URL for image-edit mode (image type only). Bypasses character lookup — use any image URL directly. |
| image_url | string | image_proxy | Source image URL to proxy. Downloads the external image and re-serves it via builder2 CDN. Response includes source_url for attribution. |
| page_url | string | page_screenshot | Webpage URL to screenshot. Renders the page via headless Chromium and captures a JPEG. Response includes source_page_url for attribution. |
| image_size | string | No | Image aspect ratio preset (image only). Default: landscape_16_9. Options: landscape_16_9 | landscape_4_3 | square | portrait_4_3 | portrait_16_9. The closest supported value is chosen per model. |
| model | string | No | Model override. For music: suno-v4.5 (default) | suno-v4.5plus | suno-v5. For images: any fal.ai model ID (e.g. fal-ai/flux-pro/v1.1). |
| style | string | music | Music style tags (e.g., pop, electronic, upbeat, female vocals). Used with custom_mode. |
| title | string | music | Song title (custom mode only) |
| custom_mode | bool | music | Enable custom mode for full control over lyrics and style (default: false). In simple mode, the prompt describes the song; in custom mode, the prompt IS the lyrics. |
| instrumental | bool | music | Generate instrumental only, no vocals (default: false) |
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a sunset over mountains, digital art",
"content_type": "image",
"media_slug": "sunset-mountains",
"count": 2,
"image_size": "landscape_16_9",
"webhook_url": "https://example.com/webhooks/builder2"
}'
# Response 202 Accepted
{
"job_id": "job_bbb60d20-028",
"media_slug": "sunset-mountains",
"permanent_url": "https://cdn.builder2.com/w/sunset-mountains/job_bbb60d20-028.jpeg",
"media_url": "https://media.builder2.com/job_bbb60d20-028/1.png",
"count": 2,
"default_variant_index": 1,
"status": "generating",
"model": "fal-ai/nano-banana-pro",
"mode": "standard",
"embed_markdown": "",
"embed_html": "<iframe src=\"https://www.builder2.com/embed/job_bbb60d20-028\" width=\"100%\" height=\"500\" frameborder=\"0\" style=\"border:none;border-radius:12px;overflow:hidden;\" loading=\"lazy\"></iframe>"
}
The permanent_url is the worker-served slot URL — for images it returns a compressed 1200x630 JPEG social card (Content-Type: image/jpeg). The media_url is the direct R2 URL (full-size, available once generation completes). Use /w/{type}/{slug}/{jobId}/{index}.png for a specific full-size variant.
# Image Proxy: re-serve an external image via builder2 CDN
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"content_type": "image_proxy",
"image_url": "https://example.com/photos/rtx-5090.jpg",
"media_slug": "rtx-5090-product",
"prompt": "NVIDIA RTX 5090 graphics card"
}'
# Response 202 Accepted
{
"job_id": "job_e1f2a3b4-567",
"media_slug": "rtx-5090-product",
"permanent_url": "https://cdn.builder2.com/w/i/rtx-5090-product/job_e1f2a3b4-567.jpeg",
"media_url": "https://media.builder2.com/job_e1f2a3b4-567/1.jpg",
"count": 1,
"status": "generating",
"source_url": "https://example.com/photos/rtx-5090.jpg",
"embed_markdown": "",
"embed_html": "..."
}
# source_url is returned for attribution/credit
# No prompt required, no fal.ai generation — image is downloaded and served via CDN
# Generate Music (Suno via Evolink)
# Simple mode — describe the song you want
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "A cheerful summer pop song about road trips and adventure",
"content_type": "music",
"media_slug": "summer-road-trip",
"model": "suno-v4.5"
}'
# Response 202 Accepted (returns immediately with placeholder MP3)
{
"job_id": "job_f1e2d3c4-567",
"media_slug": "summer-road-trip",
"permanent_url": "https://cdn.builder2.com/w/m/summer-road-trip/job_f1e2d3c4-567.mp3",
"media_url": "https://media.builder2.com/job_f1e2d3c4-567/1.mp3",
"count": 1,
"default_variant_index": 1,
"status": "generating",
"model": "suno-v4.5",
"mode": "standard",
"embed_markdown": "",
"embed_html": "..."
}
# The permanent_url serves a silent placeholder MP3 immediately.
# Suno generates 2 songs (~30-90s), both saved as variants.
# Variant 1 = default, Variant 2 = alternate. Switch via PUT /jobs/:job_id/default.
# Custom mode — full control with lyrics
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "[Verse]\nPack the car, hit the road\nSun is high, feeling bold\n[Chorus]\nSummer nights, living right\nDriving till the morning light",
"content_type": "music",
"media_slug": "summer-anthem",
"model": "suno-v5",
"custom_mode": true,
"style": "pop, upbeat, electronic, female vocals, 120bpm",
"title": "Summer Anthem"
}'
# Instrumental mode — no vocals
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "lo-fi chillhop for studying",
"content_type": "music",
"instrumental": true
}'
3 Webhooks
Pass a webhook_url in your generate or regenerate request. When the job finishes, we send a POST with the full result — no follow-up call needed.
Callback Payload
Sent as JSON with the following headers:
X-Builder2-Signature: v1=<signature>
# POST to your webhook_url when job completes
{
"job_id": "job_bbb60d20-028",
"status": "completed",
"slots": [
{
"key": "hero-banner",
"url": "https://cdn.builder2.com/m/2eb8f23b-f0f3/hero-banner",
"status": "ready"
},
{
"key": "thumbnail",
"url": "https://cdn.builder2.com/m/2eb8f23b-f0f3/thumbnail",
"status": "ready"
}
],
"variants": [
{
"variant_id": "job_bbb60d20-028/hero-banner/1",
"slot_key": "hero-banner",
"preview_url": "https://storage.nxdot.app/builder2-qitl/.../hero-banner_1.png",
"content_type": "image",
"width": 1024,
"height": 1024,
"is_active": true
},
{
"variant_id": "job_bbb60d20-028/hero-banner/2",
"slot_key": "hero-banner",
"preview_url": "https://storage.nxdot.app/builder2-qitl/.../hero-banner_2.png",
"content_type": "image",
"width": 1024,
"height": 1024,
"is_active": false
},
{
"variant_id": "job_bbb60d20-028/hero-banner/3",
"slot_key": "hero-banner",
"preview_url": "https://storage.nxdot.app/builder2-qitl/.../hero-banner_3.png",
"content_type": "image",
"width": 1024,
"height": 1024,
"is_active": false
},
{
"variant_id": "job_bbb60d20-028/thumbnail/1",
"slot_key": "thumbnail",
"preview_url": "https://storage.nxdot.app/builder2-qitl/.../thumbnail_1.png",
"content_type": "image",
"width": 1024,
"height": 1024,
"is_active": true
}
]
}
Your endpoint must respond with 2xx within 10 seconds. We do not retry failed deliveries.
4 Embeds
Every /build-media response includes embed_markdown and embed_html. Drop these into chat responses, web pages, or any text that renders markdown or HTML.
Markdown Embed
Works in any markdown renderer (ChatGPT, Discord, Slack, etc.). Serves a branded placeholder while generating, then 302-redirects to the actual image.

HTML Embed (Iframe)
Rich interactive experience for web-based chat UIs. Auto-refreshes from loading state to media. Supports image grids, video players, and audio players.
<iframe src="https://www.builder2.com/embed/job_bbb60d20-028"
width="100%" height="500" frameborder="0"
style="border:none;border-radius:12px;overflow:hidden;"
loading="lazy"></iframe>
Public Status Endpoint
No authentication required. Poll this to check job progress and get variant URLs.
/api/v1/embed/:job_id/status
curl https://www.builder2.com/api/v1/embed/job_bbb60d20-028/status
# While generating
{
"job_id": "job_bbb60d20-028",
"status": "processing",
"content_type": "image",
"variant_count": 0
}
# When ready
{
"job_id": "job_bbb60d20-028",
"status": "ready",
"content_type": "image",
"slug": "sunset-mountains",
"prompt": "a sunset over mountains, digital art",
"variant_count": 2,
"default_index": 1,
"variants": [
{ "url": "https://media.builder2.com/job_bbb60d20-028/1", "width": 1024, "height": 1024 },
{ "url": "https://media.builder2.com/job_bbb60d20-028/2", "width": 1024, "height": 1024 }
]
}
Content Type Rendering
| Content Type | Iframe Behavior | Markdown Image |
|---|---|---|
| image | Variant grid (1) or carousel (2+) | First variant |
| image_proxy | Single image display | Proxied image |
| page_screenshot | Single screenshot display | Screenshot image |
| video | HTML5 video player | Placeholder only |
| tts | Audio player(s) | Placeholder only |
| music | Audio player(s) | Placeholder only |
5 Characters
Generate images using a character as a visual reference. The system uses an image-edit model that takes the character's default avatar (or a custom URL) as input, producing images that match the character's appearance.
Two ways to specify a character
character_idorcharacter_slug— looks up the character's default avatar automaticallycharacter_image_url— any image URL, no character record needed
For non-image content types (tts, video, music), the character's physical & soul traits are injected into the prompt text instead.
Example 1: Using character_id
The character's default avatar is used as the reference image. The image-edit model receives image_urls + your prompt.
# Request: generate image with character reference
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "dancing in a neon-lit Berlin underground club, moody lighting, cinematic",
"content_type": "image",
"character_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"media_slug": "lucy-berlin-club",
"count": 2,
"image_size": "portrait_16_9"
}'
# Response 202 Accepted
{
"job_id": "job_c4d5e6f7-890",
"media_slug": "lucy-berlin-club",
"permanent_url": "https://cdn.builder2.com/w/image/lucy-berlin-club/job_c4d5e6f7-890.jpeg",
"media_url": "https://media.builder2.com/job_c4d5e6f7-890/1.png",
"count": 2,
"default_variant_index": 1,
"status": "generating",
"model": "fal-ai/bytedance/seedream/v4.5/edit",
"mode": "character",
"embed_markdown": "",
"embed_html": "<iframe src=\"https://www.builder2.com/embed/job_c4d5e6f7-890\" width=\"100%\" height=\"500\" frameborder=\"0\" style=\"border:none;border-radius:12px;overflow:hidden;\" loading=\"lazy\"></iframe>"
}
# What gets sent to fal.ai internally:
# POST https://queue.fal.run/fal-ai/bytedance/seedream/v4.5/edit
# {
# "prompt": "dancing in a neon-lit Berlin underground club...",
# "image_urls": ["https://media.builder2.com/avatars/char_a1b2/default.png"],
# "image_size": "portrait_16_9",
# "num_images": 2
# }
Example 2: Using character_image_url
Pass any image URL directly — no character record needed. Useful for ad-hoc image editing or external reference images.
# Request: image edit with a custom reference URL
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "wearing a red dress at a gala event, elegant, professional photography",
"content_type": "image",
"character_image_url": "https://example.com/photos/portrait.jpg",
"media_slug": "red-dress-gala",
"image_size": "portrait_4_3"
}'
# Response 202 Accepted
{
"job_id": "job_d5e6f7g8-901",
"media_slug": "red-dress-gala",
"permanent_url": "https://cdn.builder2.com/w/image/red-dress-gala/job_d5e6f7g8-901.jpeg",
"media_url": "https://media.builder2.com/job_d5e6f7g8-901/1.png",
"count": 1,
"default_variant_index": 1,
"status": "generating",
"model": "fal-ai/bytedance/seedream/v4.5/edit",
"mode": "character",
"embed_markdown": "",
"embed_html": "<iframe src=\"https://www.builder2.com/embed/job_d5e6f7g8-901\" width=\"100%\" height=\"500\" frameborder=\"0\" style=\"border:none;border-radius:12px;overflow:hidden;\" loading=\"lazy\"></iframe>"
}
# What gets sent to fal.ai internally:
# POST https://queue.fal.run/fal-ai/bytedance/seedream/v4.5/edit
# {
# "prompt": "wearing a red dress at a gala event...",
# "image_urls": ["https://example.com/photos/portrait.jpg"],
# "image_size": "portrait_4_3"
# }
Character Model Selection
When a character is attached, the system selects the character mode default model for the content type instead of the standard default. Configure defaults from the dashboard Models page.
| Content Type | Standard Model | Character Model |
|---|---|---|
| image | seedream/v4.5/text-to-image | seedream/v4.5/edit |
| infographic | nano-banana-pro | seedream/v4.5/edit |
| video | wan/v2.7/text-to-video | minimax/video-01-subject-reference |
| tts | bytedance/seed-audio-1.0 | gemini-3.1-flash-tts |
| music | diffrhythm | diffrhythm |
6 Manage Slots
/api/v1/slots
List all your slots with status.
curl https://api.builder2.com/api/v1/slots \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Response 200 OK
{
"slots": [
{
"key": "hero-banner",
"url": "https://cdn.builder2.com/m/2eb8f23b-f0f3/hero-banner",
"content_type": "image",
"status": "ready",
"created_at": "2026-06-13T01:32:59Z",
"updated_at": "2026-06-13T01:33:05Z"
},
{
"key": "thumbnail",
"url": "https://cdn.builder2.com/m/2eb8f23b-f0f3/thumbnail",
"content_type": "image",
"status": "generating",
"created_at": "2026-06-13T01:32:59Z",
"updated_at": "2026-06-13T01:32:59Z"
}
]
}
/api/v1/slots/:key
Get a single slot by key, including the active variant with preview URL.
curl https://api.builder2.com/api/v1/slots/hero-banner \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Response 200 OK
{
"key": "hero-banner",
"url": "https://cdn.builder2.com/m/2eb8f23b-f0f3/hero-banner",
"content_type": "image",
"status": "ready",
"created_at": "2026-06-13T01:32:59Z",
"updated_at": "2026-06-13T01:33:05Z",
"active_variant": {
"variant_id": "job_bbb60d20-028/hero-banner/1",
"preview_url": "https://storage.nxdot.app/builder2-qitl/2eb8f23b/job_bbb60d20/hero-banner_1.png",
"content_type": "image",
"is_active": true
}
}
/api/v1/slots/:key/regenerate
Regenerate media for an existing slot. The CDN URL stays the same.
| Field | Type | Required | Description |
|---|---|---|---|
| prompt | string | No | New prompt (reuses last if omitted) |
| content_type | string | No | Change type (keeps current if omitted) |
| qty | int | No | Variants (2–4, default 2) |
| webhook_url | string | No | Callback URL on completion |
curl -X POST https://api.builder2.com/api/v1/slots/hero-banner/regenerate \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "a dramatic mountain sunrise, cinematic lighting",
"qty": 2,
"webhook_url": "https://example.com/webhooks/builder2"
}'
# Response 202 Accepted
{
"job_id": "job_c7d8e9f0-135",
"slot_key": "hero-banner",
"slot_url": "https://cdn.builder2.com/m/2eb8f23b-f0f3/hero-banner"
}
/api/v1/slots/:key/variant
Select which variant to serve at the slot's CDN URL.
curl -X PUT https://api.builder2.com/api/v1/slots/hero-banner/variant \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"variant_id": "job_bbb60d20-028/hero-banner/2"}'
# Response 200 OK
{
"message": "Variant updated"
}
/api/v1/slots/:key
Delete a slot and all its variants. The CDN URL will stop serving.
curl -X DELETE https://api.builder2.com/api/v1/slots/hero-banner \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Response 204 No Content
7 Jobs & Variants
/api/v1/jobs
List all generation jobs.
curl https://api.builder2.com/api/v1/jobs \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Response 200 OK
{
"jobs": [
{
"job_id": "job_bbb60d20-028",
"status": "completed",
"content_type": "image",
"created_at": "2026-06-13T01:32:59Z",
"completed_at": "2026-06-13T01:33:05Z"
},
{
"job_id": "job_a1f2e3d4-567",
"status": "processing",
"content_type": "image",
"created_at": "2026-06-13T01:40:12Z",
"completed_at": null
}
]
}
/api/v1/jobs/:job_id
Get job detail. Status: pending → processing → completed | failed.
curl https://api.builder2.com/api/v1/jobs/job_bbb60d20-028 \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Response 200 OK
{
"job_id": "job_bbb60d20-028",
"status": "completed",
"prompt": "a sunset over mountains, digital art",
"content_type": "image",
"created_at": "2026-06-13T01:32:59Z",
"completed_at": "2026-06-13T01:33:05Z"
}
/api/v1/jobs/:job_id/variants
List all variants generated by a job, with preview URLs and dimensions.
curl https://api.builder2.com/api/v1/jobs/job_bbb60d20-028/variants \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Response 200 OK
{
"variants": [
{
"variant_id": "job_bbb60d20-028/hero-banner/1",
"slot_key": "hero-banner",
"preview_url": "https://storage.nxdot.app/builder2-qitl/2eb8f23b/job_bbb60d20/hero-banner_1.png",
"content_type": "image",
"width": 1024,
"height": 1024,
"is_active": true,
"created_at": "2026-06-13T01:33:03Z"
},
{
"variant_id": "job_bbb60d20-028/hero-banner/2",
"slot_key": "hero-banner",
"preview_url": "https://storage.nxdot.app/builder2-qitl/2eb8f23b/job_bbb60d20/hero-banner_2.png",
"content_type": "image",
"width": 1024,
"height": 1024,
"is_active": false,
"created_at": "2026-06-13T01:33:04Z"
}
]
}
8 Account
/api/v1/user
Get your profile, plan, and quota usage.
curl https://api.builder2.com/api/v1/user \
-H "Authorization: Bearer bk2_YOUR_KEY"
# Response 200 OK
{
"id": "2eb8f23b-f0f3-47ab-a69d-8f162f7c9e6c",
"email": "[email protected]",
"plan": "free",
"quota_used_this_month": 4,
"quota_limit": 10
}
9 Full Example: Generate & Serve
# 1. Generate media
curl -X POST https://api.builder2.com/api/v1/build-media \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"minimalist logo for a coffee app","content_type":"image","media_slug":"logo","count":3,"image_size":"landscape_16_9"}'
{
"job_id": "job_bbb60d20-028",
"media_slug": "logo",
"permanent_url": "https://cdn.builder2.com/w/logo/job_bbb60d20-028.jpeg",
"media_url": "https://media.builder2.com/job_bbb60d20-028/1.png",
"count": 3,
"status": "generating",
"model": "fal-ai/nano-banana-pro",
"embed_markdown": "",
"embed_html": "<iframe src=\"https://www.builder2.com/embed/job_bbb60d20-028\" ...></iframe>"
}
# 2. Use the permanent URL (serves default variant once ready)
<img src="https://cdn.builder2.com/w/logo/job_bbb60d20-028.jpeg" />
# 3. Embed in chat (markdown - works anywhere)

# 4. Embed in web (iframe - rich interactive experience)
<iframe src="https://www.builder2.com/embed/job_bbb60d20-028"
width="100%" height="500" frameborder="0"
style="border:none;border-radius:12px;overflow:hidden;"
loading="lazy"></iframe>
# 5. Access a specific variant (full-size)
<img src="https://cdn.builder2.com/w/logo/job_bbb60d20-028/2.png" />
# 6. Check job status (public, no auth needed)
curl https://www.builder2.com/api/v1/embed/job_bbb60d20-028/status
# 7. Change default variant
curl -X PUT https://api.builder2.com/api/v1/jobs/job_bbb60d20-028/default \
-H "Authorization: Bearer bk2_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{"variant_index": 3}'
{ "message": "Default variant updated" }
# Permanent URL now serves variant #3
<img src="https://cdn.builder2.com/w/logo/job_bbb60d20-028.jpeg" />