Create and edit media with one Tokenless API key. Studio uses this same API and your existing balance. Media prices are quoted separately from text-token pricing; the text API’s 50% discount does not apply to media.
Upload references, request a quote, then submit a job. Jobs continue after you disconnect. Polling is free. Quotes expire after five minutes and become invalid when inputs change. Two media jobs can run per account at a time. Files are retained for 30 days; result download links last 15 minutes and can be refreshed by retrieving the job.
| GET /media/models | Model capabilities, availability, and enabled pricing configurations. Public. |
| POST /media/uploads | JSON {contentType, bytes}; returns an asset ID and a signed form upload valid for 15 minutes. |
| POST /media/quotes | Validate normalized inputs and uploaded file contents; returns quote ID, chargeMicros, quantity, unit, expiresAt. |
| POST /media/jobs | JSON {quoteId, input} plus Idempotency-Key. Returns the reserved job with HTTP 202. |
| GET /media/jobs | Your 20 newest jobs. Use ?before=nextCursor for older records. |
| GET /media/jobs/:id | Owned job, status, original inputs, reserved/charged amount, and outputs. |
| GET /media/assets/:id | Refresh a validated reference asset’s temporary read URL. |
Base URL: https://api.tokenless.store/api/v1. The /v1 prefix is also supported. Authenticate with Authorization: Bearer sk-tk-…. These normalized media endpoints are separate from OpenAI-compatible text endpoints.
operation is image, image_edit, video, video_edit, replace_people, or motion. Supply a model ID, prompt, image asset IDs, optional source video and lastFrame asset IDs, and settings. Model capabilities determine valid durations, resolutions, quality modes, audio, aspect ratios, and reference limits. Editing uses the uploaded video duration; omit settings.duration for edits and motion transfer.
References must be uploaded asset IDs owned by your account, not arbitrary URLs. Use still JPG, PNG, or WebP files up to 10 MB and at least 300 pixels on each side. Video uploads accept MP4/MOV up to 200 MB, with duration limits specific to the selected model. Original audio preservation is model-dependent.
// Node.js 22+. Keep TOKENLESS_API_KEY on your server.
import { readFile } from "node:fs/promises";
import { randomUUID } from "node:crypto";
const base = "https://api.tokenless.store/api/v1";
const headers = {
Authorization: "Bearer " + process.env.TOKENLESS_API_KEY,
"Content-Type": "application/json"
};
async function api(path, body, extra = {}) {
const response = await fetch(base + path, {
method: body ? "POST" : "GET", headers: {...headers, ...extra},
...(body ? {body: JSON.stringify(body)} : {})
});
const data = await response.json();
if (!response.ok) throw new Error(data.error?.message || response.statusText);
return data;
}
async function upload(path, contentType) {
const bytes = await readFile(path);
const asset = await api("/media/uploads", {contentType, bytes: bytes.length});
const form = new FormData();
for (const [key, value] of Object.entries(asset.upload.fields)) form.append(key, value);
form.append("file", new Blob([bytes], {type: contentType}), "upload");
const response = await fetch(asset.upload.url, {method: "POST", body: form});
if (!response.ok) throw new Error("Upload failed");
return asset.id;
}
// Discover availability and validated settings before selecting a model.
const {data: models} = await api("/media/models");
console.log(models.filter(m => m.available));
const imageId = await upload("reference.png", "image/png");
const input = {
model: "gemini-3.1-flash-image", operation: "image_edit",
prompt: "Change the background to a bright photography studio.",
images: [imageId], people: [],
settings: {resolution: "1K", aspectRatio: "16:9", audio: false, keepAudio: true}
};
const quote = await api("/media/quotes", input);
console.log("Maximum charge in USD:", quote.chargeMicros / 1e6);
// Submit only after your user accepts this quote. Preserve this key on retries.
const idempotencyKey = randomUUID();
let job = await api("/media/jobs", {quoteId: quote.id, input}, {
"Idempotency-Key": idempotencyKey
});
while (["queued", "submitting", "processing"].includes(job.status)) {
await new Promise(resolve => setTimeout(resolve, 8000));
job = await api("/media/jobs/" + job.id);
}
if (job.status === "reconciling") {
console.log("Do not resubmit: this job is awaiting reconciliation.");
} else if (job.status === "succeeded") {
console.log(job.outputs); // Temporary download URLs and retention expiry
} else {
console.log(job.error);
}Upload the source video and one replacement photo per person. Identify each original person by clothing or position. The API binds numbered references to real attachments and translates the prompt for the selected model. Choose 1–4 people within that model’s validated limit. Exact background, identity, or motion preservation is not guaranteed.
{
"model": "kling-video-o1",
"operation": "replace_people",
"prompt": "Keep the scene and original camera movement.",
"video": "SOURCE_VIDEO_ASSET_ID",
"images": [],
"people": [
{
"image": "PHOTO_1_ASSET_ID",
"description": "Left foreground person in a striped shirt"
},
{
"image": "PHOTO_2_ASSET_ID",
"description": "Right foreground person wearing orange"
}
],
"settings": {
"mode": "pro",
"aspectRatio": "16:9",
"audio": false,
"keepAudio": true
}
}Replace the placeholder IDs with real upload IDs, request a quote, and submit it using the same lifecycle above.
Prices use integer micro-dollars: 1,000,000 micros = $1. The maximum quoted charge is reserved on submission. A successful job settles once; a confirmed failure releases the reservation. Usage and billing include the model and media quantity. Settings without verified pricing cannot be submitted.
Keep the same Idempotency-Key, quoteId, and input when retrying a submission after a network failure. Reusing the key with different inputs returns 409. A new generation needs a new quote and key. Closing Studio does not cancel a job.
States: queued, submitting, processing, reconciling, succeeded, failed. A reconciling job may already have been accepted, so its reservation stays held while its status is checked. Do not start another generation to recover it. Contact support with the job ID if it remains unresolved.
Errors return {"error":{"code":"…","message":"…"}}: 400 invalid/unsupported input; 401 invalid key; 402 insufficient balance; 404 missing or unowned asset/job; 409 expired/changed/used quote or idempotency conflict; 429 active job limit; 503 unavailable model or infrastructure. A failed generation is represented by the job’s failed status.
Only available models with a validated configuration accept quotes. Unverified variants remain listed for discovery and cannot charge your balance. Exact combinations are returned by /media/models; obtain a quote for the total price.
| Model ID | Operations | Availability / price |
|---|---|---|
Kling O1kling-video-o1 | video, video edit, replace people | Validation pending. |
Kling 3 Omnikling-v3-omni | video, video edit, replace people | Validation pending. |
Kling 2.5.turbokling-v2-5-turbo | video | Validation pending. |
Kling 2.6kling-v2-6 | video | Validation pending. |
Kling 3kling-v3 | video | Validation pending. |
Kling 3.0 Turbokling-3.0-turbo | video | Validation pending. |
Kling Motion Controlkling-motion-control | motion | Validation pending. |
Kling Omni Imagekling-image-o1 | image, image edit | Validation pending. |
Seedance 1.0 Prodoubao-seedance-1-0-pro-250528 | video | Validation pending. |
Seedance 1.0 Pro Fastdoubao-seedance-1-0-pro-fast-251015 | video | Validation pending. |
Seedance 1.5 Prodoubao-seedance-1-5-pro-251215 | video | Validation pending. |
Seedance 2.0doubao-seedance-2-0-260128 | video | Validation pending. |
Seedance 2.5doubao-seedance-2-5-260628 | video | Validation pending. |
Veo 3.1veo_3_1 | video | Validation pending. |
Veo 3.1 Fastveo_3_1-fast | video | Validation pending. |
Veo 3.1 Referencesveo_3_1-components | video | Validation pending. |
Wan 2.5-i2v-previewwan2.5-i2v-preview | video | Validation pending. |
Wan 2.6-i2vwan2.6-i2v | video | Validation pending. |
Wan 3.0-videowan3.0-video | video | Validation pending. |
Wan 2.7 Image Prowan2.7-image-pro | image, image edit | Validation pending. |
Grok Imagine Videogrok-imagine-video | video, video edit | Validation pending. |
Grok Imagine Video 1.5 Previewgrok-imagine-video-1.5-preview | video | Validation pending. |
Grok imagine imagegrok-imagine-image | image, image edit | Validation pending. |
Grok imagine image qualitygrok-imagine-image-quality | image, image edit | Validation pending. |
Grok imagine image 2.0grok-imagine-image-2.0 | image, image edit | Validation pending. |
Nano Bananagemini-2.5-flash-image | image, image edit | Validation pending. |
Nano Banana Progemini-3-pro-image | image, image edit | Validation pending. |
Nano Banana Pro Previewgemini-3-pro-image-preview | image, image edit | Validation pending. |
Nano Banana 2gemini-3.1-flash-image | image, image edit | Validation pending. |
Nano Banana 2 Previewgemini-3.1-flash-image-preview | image, image edit | Validation pending. |
Nano Banana Litegemini-3.1-flash-lite-image | image, image edit | Validation pending. |
Seedream 3doubao-seedream-3-0-t2i-250415 | image | Validation pending. |
Seedream 4doubao-seedream-4-0-250828 | image, image edit | Validation pending. |
Seedream 4.5doubao-seedream-4-5-251128 | image, image edit | Validation pending. |
Seedream 5 Litedoubao-seedream-5-0-260128 | image, image edit | Validation pending. |
Seedream 5 Prodoubao-seedream-5-0-pro-260628 | image, image edit | Validation pending. |
Google Omniomni-flash | Pending availability | Not currently available. |
Sora 2sora-2 | Pending availability | Not currently available. |
Sora 2 Prosora-2-pro | Pending availability | Not currently available. |