Frenkel.ai Studio API
The API is the same set of routes the studio itself uses, so anything you can do in the browser you can do from code. Base URL https://app.frenkel.ai. The machine-readable description lives at /api/openapi.json (OpenAPI 3.1).
Authentication
Create a key under Account → Developers & AI agents. Keys start with frk_, are shown once, and can be revoked at any time. Send it as a bearer token:
Authorization: Bearer frk_your_key
A key acts as the account that created it and can spend its credits. Keep it server-side; revoke it immediately if it leaks.
Generation lifecycle
- Look at the menu.
GET /api/catalog?locale=enreturns every option with live availability and credit prices: tasks, image looks, video styles and tiers, aspect ratios, product scenes, voices, cue tags, dubbing languages, logo options and limits. - Quote.
GET /api/pricing?task=text_to_video&style=seedance25&tier=720p&seconds=5runs the real router in preview mode and returns the exact credits. - Start.
POST /api/generationswith a JSON body (see the schema below). Credits are charged now; the response is the job with statusqueued. - Poll.
GET /api/jobs/{id}untilstatusiscompleted,failedorcancelled. Images take seconds, cloud video one to six minutes. Failed jobs are refunded. - Download. Each output is an asset with
urlandthumbUrlunder/api/files/…; send the same Authorization header. Reuse an output'sidasinputAssetIdsfor the next step.
Endpoints
| Method & path | Purpose |
|---|---|
GET /api/me | The account behind the key and its credit balance. |
GET /api/me/ledger | Credit movements (grants, purchases, charges, refunds). |
GET /api/catalog?locale=en | Everything the studio offers, with availability and prices. |
GET /api/pricing | Quote one choice in credits (task, quality, style, tier, seconds, count…). |
POST /api/generations | Start a generation: any task, from images to dubbing and logos. |
GET /api/jobs · GET /api/jobs/{id} · DELETE /api/jobs/{id} | Recent jobs, one job with outputs, cancel. |
GET /api/jobs/{id}/details | Timeline, translated prompt, engine, model, provider cost. |
GET /api/assets · GET|PATCH|DELETE /api/assets/{id} | The library: search, kinds, media types, projects, pagination. |
POST /api/assets/import | Add a file from a public URL or base64 (images ≤ 25 MB, video ≤ 600 MB, audio). |
POST /api/uploads | Multipart upload (file, optional kind, projectId). |
GET /api/files/{key} | Download a stored file (Range supported). |
GET|POST /api/projects | Folders for campaigns and clients. |
GET|POST /api/characters | People kept consistent across images. |
GET|POST /api/voices | Cloned voices; register one, then run a voice_clone generation. |
GET|POST /api/me/api-keys · DELETE /api/me/api-keys/{id} | Manage keys. |
Tasks
text_to_imageimage_editinpaintoutpaintbackground_replacebackground_removeupscalevariationproduct_photocharacter_referencetext_to_videoimage_to_videoreference_to_videotext_to_speechvideo_voiceovervoice_clonevideo_lipsyncvideo_dublogovectorizerecolormockup
The full request schema (GenerationRequest) with every field is in the OpenAPI document below. The most used fields: task, prompt, quality, aspectRatio, count, inputAssetIds, videoStyle, videoSeconds, cloudTier, voice, dub, logoOptions, mockupScene, projectId.
Examples
Create an image and wait for it (curl)
KEY=frk_your_key
JOB=$(curl -s https://app.frenkel.ai/api/generations \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"task":"text_to_image","prompt":"A ceramic cup on a wooden table, soft morning light","quality":"seedream","aspectRatio":"16:9"}' \
| jq -r .id)
until curl -s https://app.frenkel.ai/api/jobs/$JOB -H "Authorization: Bearer $KEY" | jq -e '.status|IN("completed","failed","cancelled")' >/dev/null; do sleep 3; done
curl -s https://app.frenkel.ai/api/jobs/$JOB -H "Authorization: Bearer $KEY" | jq '.outputs[0].url'
Animate that image (JavaScript)
const headers = { Authorization: `Bearer ${process.env.FRENKEL_KEY}`, "Content-Type": "application/json" };
const base = "https://app.frenkel.ai";
const job = await fetch(`${base}/api/generations`, { method: "POST", headers, body: JSON.stringify({
task: "image_to_video", inputAssetIds: [imageAssetId], videoStyle: "seedancemini", cloudTier: "720p", videoSeconds: 5,
prompt: "slow push-in, steam rising from the cup",
}) }).then(r => r.json());
let done = job;
while (!["completed", "failed", "cancelled"].includes(done.status)) {
await new Promise(r => setTimeout(r, 4000));
done = await fetch(`${base}/api/jobs/${job.id}`, { headers }).then(r => r.json());
}
console.log(done.status, done.outputs[0]?.url);
A voice clip with cues (Python)
import requests, time
H = {"Authorization": "Bearer frk_your_key"}
job = requests.post("https://app.frenkel.ai/api/generations", headers=H, json={
"task": "text_to_speech",
"prompt": "Welcome to Frenkel Studio. [excited] Today we are making something special. [wait:1] Let's begin.",
"voice": {"voice": "Sarah", "stability": 0.4},
}).json()
while job["status"] not in ("completed", "failed", "cancelled"):
time.sleep(2); job = requests.get(f"https://app.frenkel.ai/api/jobs/{job['id']}", headers=H).json()
print(job["outputs"][0]["url"])
Errors
Errors are JSON: {"error": "message", "code": "…"}. 401 missing or revoked key · 400 invalid request (the message names the field) · 402 not enough credits · 404 unknown job or asset · 429 too many concurrent jobs (the account runs up to 5 at once).
OpenAPI reference
Interactive reference generated from the live openapi.json. Paste your key under Authentication to try requests against your own account.
Frenkel.ai