Models and media
List the chat models your key may use, then generate images, video, speech and music, and transcribe audio. Image generation answers straight away; the other media jobs run in the background and you poll them.
This page covers the model catalogue and every media endpoint. Chat models are what you pass as ai_model_id to conversations and runs. Video, speech, music and transcription each have their own model catalogue and follow one shared job pattern.
All paths are relative to https://api.stickyprompts.com/api/v1 and every request carries Authorization: Bearer $STICKY_API_KEY.
Conventions
- IDs and slugs. Every generation and transcription has a numeric
IDand an 8-characterslug.GET /<resource>/:idandPOST /<resource>/:id/retryaccept either.DELETE /<resource>/:idaccepts only the numeric ID. - Decimals are strings. Credit prices and costs (
credit_cost,credit_cost_per_*,multiplier) come back as JSON strings such as"0.000015". - Field casing. Records show
ID,CreatedAt,UpdatedAt,DeletedAtin PascalCase next to snake_case fields. See Errors, pagination and quotas. - Lists are paginated with
page(default1) andsize(default10), newest first, withX-Total-Count,X-PageandX-Page-Sizeheaders. The body is a bare array. - Quota. Routes that start paid work answer
429 {"error":"Quota exceeded","reason":"..."}when the plan is out of room. - Restricted models. If a workspace rule blocks the model you chose, create calls answer
403 {"error":"the selected model has been restricted by your organization"}.
Chat models
Chat models are identified by their numeric ID. name is a display name, not an identifier. With an API key, your workspace’s model access rules and data residency policy are applied: models you cannot use come back with selectable: false and a restriction_reason.
GET /ai_model
/api/v1/ai_model Lists the chat-model catalogue in display order. Deprecated models are included unless you pass legacy=false.
-
searchstring query optional - Substring match on
name. -
powered_bystring query optional - Exact match on the maker label, for example
Anthropic. Values come fromGET /ai_model/filters. -
toolscomma list query optional - Model must have all listed capabilities, for example
web_search,file_upload. -
tagscomma list query optional - Model must have all listed tags, for example
coding. -
flagshiptrue query optional - Only flagship models.
-
legacyfalse query optional - Hide deprecated models.
-
page, sizeinteger query optional - Optional. Without them, every matching model is returned.
curl "https://api.stickyprompts.com/api/v1/ai_model?legacy=false&flagship=true" \
-H "Authorization: Bearer $STICKY_API_KEY"import os, requests
r = requests.get(
"https://api.stickyprompts.com/api/v1/ai_model",
params={"legacy": "false", "flagship": "true"},
headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
)
usable = [m for m in r.json() if m["selectable"]]
for m in usable:
print(m["ID"], m["name"], m["multiplier"])const res = await fetch(
"https://api.stickyprompts.com/api/v1/ai_model?legacy=false&flagship=true",
{ headers: { Authorization: `Bearer ${process.env.STICKY_API_KEY}` } },
);
const models = (await res.json()).filter((m) => m.selectable);
console.log(models.map((m) => [m.ID, m.name]));Response 200 (one element shown, values illustrative):
[
{
"ID": 42,
"name": "Claude Sonnet 5",
"description": "Balanced model for everyday work.",
"powered_by": "Anthropic",
"version": "claude-sonnet-x",
"multiplier": "1.6",
"credit_cost_per_input_token": "0.0003",
"credit_cost_per_output_token": "0.0015",
"flagship": true,
"premium": false,
"deprecated": false,
"thinking_model": true,
"reasoning_efforts": ["low", "medium", "high"],
"default_reasoning_effort": "medium",
"tools": ["file_upload", "web_search", "remote_mcp"],
"tags": ["analysis", "coding"],
"best_for": "Writing, analysis and code",
"knowledge_cutoff": "2026-01-01T00:00:00Z",
"supported_file_extensions": [],
"successor": null,
"selectable": true
}
]
Useful fields: tools lists capabilities (file_upload, file_generation, image_generation, web_search, web_fetch, code_execution, remote_mcp and more), reasoning_efforts lists the allowed reasoning tiers, successor names the replacement for a deprecated model, and supported_file_extensions restricts attachments (empty means no restriction). The catalogue has no context-window size, USD price or benchmark field.
GET /ai_model/:id
/api/v1/ai_model/:id Returns one model by numeric ID. successor is not loaded here.
GET /ai_model/filters
/api/v1/ai_model/filters Returns the distinct values you can filter the catalogue by:
{
"powered_by": ["Anthropic", "Google", "OpenAI", "xAI"],
"tools": ["code_execution", "file_upload", "image_generation", "web_search"],
"tags": ["analysis", "coding", "creative_writing", "reasoning"]
}
A list is null rather than [] when no model has a value for it.
GET /ai_model/restricted
/api/v1/ai_model/restricted Deprecated. It only reports models blocked by a model-level rule in your workspace. Use the selectable flag from GET /ai_model instead.
Image generation
Image generation is synchronous: the call returns once the image exists. The server uses the image model StickyPrompts has configured; the prompt is the only control (no size, count or aspect parameters).
POST /image_generation
/api/v1/image_generation -
promptstring required - What to create.
curl -X POST https://api.stickyprompts.com/api/v1/image_generation \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "Product photo of a laptop on an oak desk showing a retail dashboard with yellow charts, morning light"}'import os, requests
r = requests.post(
"https://api.stickyprompts.com/api/v1/image_generation",
headers={"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"},
json={"prompt": "Product photo of a laptop on an oak desk showing a retail dashboard"},
timeout=180,
)
image = r.json()
print(image["ID"], image["path"])const res = await fetch("https://api.stickyprompts.com/api/v1/image_generation", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.STICKY_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt: "Product photo of a laptop on an oak desk showing a retail dashboard" }),
});
const image = await res.json();
console.log(image.ID, image.path);Response 200, a file record:
{
"ID": 9812,
"name": "image_0.png",
"path": "https://<storage-host>/<bucket>/123/Xk3...q9.png",
"user_id": 123,
"fileType": "code_interpreter_output",
"partial": false
}
The image is at path, a direct storage URL. Keep the ID: it works as file_id for editing, as a video starting frame, and in the file endpoints. Note the camel-case fileType.
Errors: 400 without prompt; 500 {"error":"image generation produced no output file"} when the model returns nothing; 500 with the message when the model fails or data residency blocks the provider. Allow a generous client timeout.
POST /image_generation/edit
/api/v1/image_generation/edit Edits an image you own and returns a new file. The source file is not changed.
-
promptstring required - The edit, for example “Change the sweater to deep forest green. Keep everything else the same.”
-
file_idinteger required - A file you own: an earlier generation or an upload from
POST /file.
Response 200: a new file record. 404 {"error":"file not found or access denied"} if the file isn’t yours.
Background jobs: video, speech, music, transcription
These four resources share one pattern:
- Pick a model
GET /<kind>_modelreturns that kind’s catalogue. Choose a model by numericID. Thedefault: trueflag marks the app’s preselected model, but the API does not fall back to it: always send the model ID. - Create the job
POST /<kind>validates the request, checks quota and access, and answers201with the job in statusqueued. - Poll until it finishes
GET /<kind>/:iduntilstatusisdoneorerror. There are no webhooks or event streams for these jobs; poll every few seconds. - Collect the result
Video, speech and music: the file is at
output_file_data.path. Transcription:textandsegmentsare in the record itself.
import os, time, requests
API = "https://api.stickyprompts.com/api/v1"
H = {"Authorization": f"Bearer {os.environ['STICKY_API_KEY']}"}
job = requests.post(f"{API}/music_generation", headers=H, json={
"music_generation_model_id": 7, # from GET /music_generation_model
"prompt": "Upbeat product-launch track, electric piano, 112 BPM, no vocals",
"duration_seconds": 30,
"instrumental": True,
}).json()
while job["status"] not in ("done", "error"):
time.sleep(5)
job = requests.get(f"{API}/music_generation/{job['slug']}", headers=H).json()
print(job["status"], job.get("output_file_data", {}).get("path"), job["credit_cost"])const API = "https://api.stickyprompts.com/api/v1";
const H = { Authorization: `Bearer ${process.env.STICKY_API_KEY}`, "Content-Type": "application/json" };
let job = await (await fetch(`${API}/music_generation`, {
method: "POST",
headers: H,
body: JSON.stringify({
music_generation_model_id: 7, // from GET /music_generation_model
prompt: "Upbeat product-launch track, electric piano, 112 BPM, no vocals",
duration_seconds: 30,
instrumental: true,
}),
})).json();
while (!["done", "error"].includes(job.status)) {
await new Promise((r) => setTimeout(r, 5000));
job = await (await fetch(`${API}/music_generation/${job.slug}`, { headers: H })).json();
}
console.log(job.status, job.output_file_data?.path, job.credit_cost);Rules shared by all four:
- The model ID is required. Omitting it or sending an unknown ID returns a generic
500 "Failed to create ...", not a400. - Cost.
GET /<kind>/:idincludescredit_cost, the credits charged for the job. List responses always show"0"there. - Data residency. If your workspace’s residency policy forbids the model’s provider, the job is accepted and then fails with the policy message in
error. Models like that showselectable: falsein the catalogue, so check first. - Retry.
POST /<kind>/:id/retryre-queues a job in statuserror(otherwise400 "Only failed ... can be retried"), re-checking access and quota. - Delete.
DELETE /<kind>/:id(numeric ID) returns204, also when nothing matched. It is a soft delete and does not remove the output file. - Filter lists with
?status=<value>.
Every media model has ID, name, powered_by, version, description, multiplier, default, sort, selectable and, when relevant, restriction_reason. Catalogues are not paginated.
Video generation
Status: queued, then processing, then done or error.
GET /video_generation_model
/api/v1/video_generation_model Video models add: supported_aspect_ratios (empty means any), supported_resolutions (first is the default), resolution_duration_limits (per resolution, "*" as fallback, {min_duration_seconds, max_duration_seconds} with 0 meaning unbounded), resolution_pricing (credit_cost_per_second per resolution), interpolation_support (none, start_only or full), max_reference_images and reference_types.
POST /video_generation
/api/v1/video_generation -
video_generation_model_idinteger required - Model
ID. -
promptstring required - The scene to generate.
-
aspect_ratiostring optional default16:9 - Must be in the model’s
supported_aspect_ratioswhen that list is not empty. -
resolutionstring optional - Defaults to the model’s first supported resolution.
-
duration_secondsinteger optional - Defaults to the maximum for the resolution. Must be within its limits.
-
starting_image_file_idinteger optional - An image you own, as the first frame. The model must support
start_onlyorfull. -
last_frame_file_idinteger optional - Needs a starting image and a model with
fullsupport. -
reference_image_idsinteger[] optional - Images you own, up to
max_reference_images. Cannot be combined with starting or last frames.
curl -X POST https://api.stickyprompts.com/api/v1/video_generation \
-H "Authorization: Bearer $STICKY_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"video_generation_model_id": 4,
"prompt": "Slow dolly shot across a bright retail store at opening time, warm morning light",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration_seconds": 8
}'
Response 201 (trimmed):
{
"ID": 311,
"slug": "a8Kd02Lq",
"video_generation_model_id": 4,
"prompt": "Slow dolly shot across a bright retail store at opening time, warm morning light",
"aspect_ratio": "16:9",
"resolution": "720p",
"duration_seconds": 8,
"status": "queued",
"error": "",
"content_policy_violation": false,
"output_file_data_id": null,
"credit_cost": "0"
}
When done, output_file_data.path is the .mp4 and actual_duration_seconds and credit_cost are filled in. content_policy_violation: true means the provider’s safety filter refused the request.
Validation errors are 400 with a precise message, for example "resolution is not supported by this video generation model", "requested duration exceeds this video generation model's maximum", "a last frame image requires a starting image" or "attached file must be an image". Using someone else’s file returns 403 "you do not have permission to use this file".
GET /video_generation, GET /video_generation/:id
/api/v1/video_generation/:id List your video jobs (page, size, status) or fetch one by ID or slug, including credit_cost. 404 {"error":"Video generation not found"}.
POST /video_generation/:id/retry
/api/v1/video_generation/:id/retry DELETE /video_generation/:id
/api/v1/video_generation/:id Text to speech
Status: queued, then processing, then done or error.
GET /tts_generation_model
/api/v1/tts_generation_model Speech models add supported_languages (empty means any) and a tts_voice_set whose voices each have a voice_id (the value to send as voice), name, gender, language, accent, description, preview_audio_url and default.
POST /tts_generation
/api/v1/tts_generation -
tts_generation_model_idinteger required - Model
ID. -
textstring required - What to say.
-
voicestring optional - A
voice_idfrom the model’s voice set. Defaults to the voice markeddefault, otherwise the first. -
languagestring optional defaultauto - Must be in
supported_languageswhen that list is not empty.
Answers 201 with the job. When done, the audio (.wav or .mp3, whichever the provider returned) is at output_file_data.path. Errors: 400 "voice is not supported by this tts generation model", 400 "language is not supported by this tts generation model".
GET /tts_generation, GET /tts_generation/:id
/api/v1/tts_generation/:id POST /tts_generation/:id/retry
/api/v1/tts_generation/:id/retry DELETE /tts_generation/:id
/api/v1/tts_generation/:id Music generation
Status: queued, then processing, then done or error.
GET /music_generation_model
/api/v1/music_generation_model Music models add min_duration_seconds and max_duration_seconds (0 means unbounded), supported_output_formats (first is the default), credit_cost_per_generation and credit_cost_per_second.
POST /music_generation
/api/v1/music_generation -
music_generation_model_idinteger required - Model
ID. -
promptstring required - Style, instruments, tempo, mood.
-
duration_secondsinteger optional - Defaults to the model’s maximum. Must be within its limits.
-
output_formatstring optional - Defaults to the first supported format.
-
instrumentalboolean optional defaultfalse - No vocals when
true.
When done, the track is at output_file_data.path and any lyrics are in lyrics. The file extension follows what the provider actually returned, so it can differ from output_format. Errors: 400 for a duration outside the limits or an unsupported format.
GET /music_generation, GET /music_generation/:id
/api/v1/music_generation/:id POST /music_generation/:id/retry
/api/v1/music_generation/:id/retry DELETE /music_generation/:id
/api/v1/music_generation/:id Transcription
Status: queued, fetching, preparing, processing, then done or error. status_progress runs from 0 to 100; it moves in steps only for models that process audio in chunks.
GET /transcription_model
/api/v1/transcription_model Transcription models add response_format (only diarized_json models label speakers), supported_languages and credit prices per token and per second.
POST /transcription
/api/v1/transcription There is no multipart upload on this route: upload the audio or video with POST /file first, or give a URL. For video files the audio track is extracted for you.
-
transcription_model_idinteger required - Model
ID. -
file_data_idinteger optional - An uploaded audio or video file. Send this or
source_url, not both. -
source_urlstring optional - A publicly downloadable URL, fetched with a plain GET.
-
languagestring optional - A hint. Omit it to auto-detect; the detected language is returned in
language.
A finished record (trimmed):
{
"ID": 77,
"slug": "Qm7tR2xa",
"status": "done",
"status_progress": 100,
"text": "Hi team, quick update before Tuesday's launch...",
"segments": [
{ "start": 0.0, "end": 28.4, "text": "Hi team, quick update before Tuesday's launch...", "speaker": "speaker_0" },
{ "start": 29.0, "end": 37.0, "text": "Thanks Darian. One question from support...", "speaker": "speaker_1" }
],
"speakers": [
{ "label": "speaker_0", "name": "", "color": "#F59E0B" },
{ "label": "speaker_1", "name": "", "color": "#10B981" }
],
"language": "en",
"duration_seconds": 42.1,
"credit_cost": "4.22"
}
Adjacent segments from the same speaker are merged, and a gap of two seconds or more appears as its own segment with a pause value and empty text. speaker is null for models that don’t separate speakers.
Errors: 400 "Either file_data_id or source_url is required", 400 "Can't set both file_data_id and source_url", 400 "language is not supported by this transcription model".
GET /transcription, GET /transcription/:id
/api/v1/transcription/:id List your transcriptions (page, size, status) or fetch one by ID or slug, including credit_cost. Don’t send a workspace_id query parameter to the list.
PATCH /transcription/:id/speakers
/api/v1/transcription/:id/speakers Rename or recolour speakers, the same as Speakers in the app. Only the fields you send change; an unknown label is added as a new speaker.
{
"speakers": {
"speaker_0": { "name": "Darian", "color": "#3B82F6" },
"speaker_1": { "name": "Sam" }
}
}
Returns 200 with the full transcription.
POST /transcription/:id/retry
/api/v1/transcription/:id/retry DELETE /transcription/:id
/api/v1/transcription/:id Usage statistics
The /statistics/* and /credits/* routes need statistics_get, a permission only StickyPrompts staff can grant. Customer keys cannot call them. Your workspace’s usage is available in the app under Usage, credits and billing, and every media job reports its own credit_cost.