edets API
Upload a still, describe the motion, and receive a clip. Billing is 1:1: a 5-second clip costs 5 seconds of prepaid balance. An 8-second clip costs 8 seconds.
Base URL
https://edets.com/api/v1Auth
Bearer edets_live_… minted at /api/keys. Every /api/v1 call sends that header.
Billing
1:1 seconds — a 5-second clip burns 5 seconds of prepaid balance.
Endpoints
| Method | Path | What it does |
|---|---|---|
| POST | /api/v1/generations | Create a single clip |
| GET | /api/v1/generations/{id} | Poll queued → running → succeeded/failed |
| GET | /api/v1/generations/{id}/clip | Download MP4 |
| POST | /api/v1/campaigns | Multi-shot IG/TT/FB |
| GET | /api/v1/campaigns/{id} | Poll campaign |
| POST | /api/v1/campaigns/{id}/compose | Stitch master + packs |
| GET | /api/v1/campaigns/{id}/master | Download master |
| GET | /api/v1/campaigns/{id}/pack/{format} | Platform pack |
| GET | /api/v1/balance | Prepaid seconds remaining + dollar equivalent |
Your first request
curl -sS -X POST "https://edets.com/api/v1/generations" \
-H "Authorization: Bearer edets_live_…" \
-F "still=@portrait.jpg" \
-F "prompt=She peels the cream sheet down off her face" \
-F "seconds=5" \
-F "quality=max" \
-F "aspect=9:16" \
-F "size=720"The handler returns this JSON. id is a UUID. poll_url is relative to the host.
{
"id": "7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"status": "queued",
"poll_url": "/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0"
}Poll GET /api/v1/generations/{id} until status is succeeded, then download the clip.
Authentication
Every /api/v1 call needs a Bearer key. Mint it at /api/keys. The secret is shown once. Prefix is edets_live_.
Authorization: Bearer edets_live_…The key burns that account’s prepaid seconds 1:1. Buy packs at /api/seconds: Starter 45s $8, Pro 120s $20, Studio 300s $45.
Concepts
| Name | Values | Notes |
|---|---|---|
| Seconds | 5–15 integer | Prepaid balance. A 5-second clip burns 5 seconds; an 8-second clip burns 8. |
| Quality | max · motion | max is edets Max. motion is edets Motion. |
| Aspect | 9:16 · 1:1 · 4:5 | Default 9:16. The still is padded to this frame before submit, not cropped. |
| Size | 480 · 720 | Public API sizes are 480 and 720. On edets Max, size=720 is 768P (short edge 768, not 720). On edets Motion, size=720 is 720P. Default 720. Short-edge resolution request. |
Generate clip
/api/v1/generationsCreates a job and returns id, status: "queued", and poll_url.
| Field | Required | Notes |
|---|---|---|
| still | yes* | Multipart file. PNG, JPG, or WEBP. Max 20 MB. |
| still_url | yes* | HTTPS URL if you are not uploading a file. |
| prompt | yes | Full shot sentence. A mood word is not enough. |
| quality | yes | max (edets Max) or motion (edets Motion). |
| seconds | yes | Integer 5–15. Burns 1:1. |
| aspect | no | 9:16, 1:1, or 4:5. Defaults to 9:16. The first-frame still is letterboxed to this aspect before submit. |
| size | no | Public API sizes are 480 and 720. On edets Max, size=720 is 768P (short edge 768, not 720). On edets Motion, size=720 is 720P. Default 720. |
* Send a file or a URL. Not both required.
Legacy aliases image and file (multipart) and JSON url/still as a URL string are accepted but not recommended.
curl -sS -X POST "https://edets.com/api/v1/generations" \
-H "Authorization: Bearer edets_live_…" \
-F "still=@portrait.jpg" \
-F "prompt=She peels the cream sheet down off her face" \
-F "seconds=5" \
-F "quality=max" \
-F "aspect=9:16" \
-F "size=720"curl -sS -X POST "https://edets.com/api/v1/generations" \
-H "Authorization: Bearer edets_live_…" \
-H "Content-Type: application/json" \
-d '{"still_url":"https://example.com/img.jpg","prompt":"She peels the cream sheet down off her face","seconds":5,"quality":"max"}'Response
{
"id": "7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"status": "queued",
"poll_url": "/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0"
}MCP equivalent: generate_video. edets MCP server.
Check status
/api/v1/generations/{id}Poll until queued → running → succeeded or failed. clip_url is a relative path and stays null until succeeded. JSON includes width, height, and aspect. Dimensions stay null until the mp4 is measured. A clip whose frame is not the requested aspect is failed and seconds go back to the pool.
Webhooks are not supported. Poll GET /api/v1/generations/{id} every 2–5 seconds. Typical generation time is 30–60 seconds.
curl -sS "https://edets.com/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0" \
-H "Authorization: Bearer edets_live_…"Queued
{
"id": "7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"status": "queued",
"clip_url": null,
"seconds_burned": 5,
"error": null,
"width": null,
"height": null,
"aspect": "9: 16"
}Running
{
"id": "7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"status": "running",
"clip_url": null,
"seconds_burned": 5,
"error": null,
"width": null,
"height": null,
"aspect": "9: 16"
}Succeeded
{
"id": "7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"status": "succeeded",
"clip_url": "/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0/clip",
"seconds_burned": 5,
"error": null,
"width": 768,
"height": 1366,
"aspect": "9: 16"
}Failed
{
"id": "7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"status": "failed",
"clip_url": null,
"seconds_burned": 5,
"error": "Generation failed. Upstream rejected the job.",
"width": null,
"height": null,
"aspect": "9: 16"
}Failed jobs return reserved seconds to the pool. seconds_burned is the attempt duration, not a leftover charge.
MCP equivalent: check_status. edets MCP server.
Download clip
/api/v1/generations/{id}/clipReturns the mp4 bytes. Not a CDN redirect. Send the same Bearer header. Response headers include Cache-Control: private, max-age=3600. If the clip is not ready, JSON 404: { "error": "Clip is not ready yet." }
curl -sS "https://edets.com/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0/clip" \
-H "Authorization: Bearer edets_live_…" \
-o clip.mp4MCP equivalent: after check_status succeeds, download clip_url. edets MCP server.
Campaigns
/api/v1/campaignsSeveral shots on the same generate pipe. Campaigns do not auto-compose. QC the clips, then POST compose. Stitch is pad-not-crop. Each shot burns seconds 1:1.
- Create —
POST /api/v1/campaigns - Poll clips —
GET /api/v1/campaigns/{id}untilsucceeded-with-clips - Rerun one shot if needed —
POST /api/v1/campaigns/{id}/shots/{index}/rerunor/replace - Compose —
POST /api/v1/campaigns/{id}/compose
| Field | Required | Notes |
|---|---|---|
| id | yes | 1–64 letters, numbers, dots, underscores, or hyphens. Not a UUID. |
| lane | yes | ig, tt, or fb. |
| intent | yes | product, character, or make_something. |
| aspect | no | 9:16, 1:1, or 4:5. IG/TT default 9:16, FB 4:5. |
| size | no | Public API sizes are 480 and 720. On edets Max, size=720 is 768P (short edge 768, not 720). On edets Motion, size=720 is 720P. Default 720. |
| stills | conditional | Required when intent is character (talent_url or a talent file). Optional for product and make_something. |
| shots | yes | { t, seconds, quality, prompt, still? | still_url? }. Seconds 5–15 each. |
| t | yes | Timeline position label (e.g. 0-5, 5-10). It does not affect rendering order — shots render in array order. |
| Intent | Use for |
|---|---|
| product | Product shots, objects, items |
| character | People, talent, portraits |
| make_something | Abstract scenes, environments, concepts |
curl -sS -X POST "https://edets.com/api/v1/campaigns" \
-H "Authorization: Bearer edets_live_…" \
-H "Content-Type: application/json" \
-d '{ "id": "IG-01", "lane": "ig", "intent": "character", "aspect": "9:16", "size": "720", "stills": { "talent_url": "https://example.com/talent.jpg" }, "shots": [ { "t": "0-5", "seconds": 5, "quality": "max", "prompt": "She peels the cream sheet down off her face", "still_url": "https://example.com/hold-a.jpg" }, { "t": "5-10", "seconds": 5, "quality": "max", "prompt": "She lifts the sheet toward camera", "still_url": "https://example.com/hold-b.jpg" } ] }'Create response
{
"id": "IG-01",
"status": "queued"
}Rerun or replace one shot
Index is 0-based. Does not compose. After the new clip succeeds, status is succeeded-with-clips.
curl -sS -X POST "https://edets.com/api/v1/campaigns/IG-01/shots/0/rerun" \
-H "Authorization: Bearer edets_live_…" \
-H "Content-Type: application/json" \
-d '{"prompt":"She peels the cream sheet down off her face"}'Poll campaign
/api/v1/campaigns/{id}Poll until succeeded-with-clips. Then QC, rerun a shot if needed, and compose. Typical wait is 30–60 seconds per shot. Poll every 2–5 seconds.
curl -sS "https://edets.com/api/v1/campaigns/IG-01" \
-H "Authorization: Bearer edets_live_…"{
"id": "IG-01",
"status": "succeeded-with-clips",
"shot_generation_ids": [
"7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"9a2b8c71-4d0e-41f6-b3aa-0c5e7d18f902"
],
"shot_clip_urls": [
"/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0/clip",
"/api/v1/generations/9a2b8c71-4d0e-41f6-b3aa-0c5e7d18f902/clip"
],
"clip_urls": [
"/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0/clip",
"/api/v1/generations/9a2b8c71-4d0e-41f6-b3aa-0c5e7d18f902/clip"
],
"master_url": null,
"pack": {
"ig_9x16": null,
"tt_9x16": null,
"fb_4x5": null
},
"seconds_burned": 10,
"error": null
}clip_urls is an alias for shot_clip_urls — same relative paths. Prefer shot_clip_urls.
Compose
/api/v1/campaigns/{id}/composeStitch the master and platform packs. Campaigns do not auto-compose — call this after clips succeed.
curl -sS -X POST "https://edets.com/api/v1/campaigns/IG-01/compose" \
-H "Authorization: Bearer edets_live_…"{
"id": "IG-01",
"status": "succeeded",
"shot_generation_ids": [
"7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0",
"9a2b8c71-4d0e-41f6-b3aa-0c5e7d18f902"
],
"shot_clip_urls": [
"/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0/clip",
"/api/v1/generations/9a2b8c71-4d0e-41f6-b3aa-0c5e7d18f902/clip"
],
"clip_urls": [
"/api/v1/generations/7c1e4d2a-9b0f-4c3e-8a11-2f6d9e4b81c0/clip",
"/api/v1/generations/9a2b8c71-4d0e-41f6-b3aa-0c5e7d18f902/clip"
],
"master_url": "/api/v1/campaigns/IG-01/master",
"pack": {
"ig_9x16": "/api/v1/campaigns/IG-01/pack/ig_9x16",
"tt_9x16": "/api/v1/campaigns/IG-01/pack/tt_9x16",
"fb_4x5": "/api/v1/campaigns/IG-01/pack/fb_4x5"
},
"seconds_burned": 10,
"error": null
}Download master
/api/v1/campaigns/{id}/masterReturns the stitched master MP4. Same Bearer. Write the file with curl -o.
curl -sS "https://edets.com/api/v1/campaigns/IG-01/master" \
-H "Authorization: Bearer edets_live_…" \
-o master.mp4Platform pack
/api/v1/campaigns/{id}/pack/{format}Formats: ig_9x16, tt_9x16, fb_4x5. Same Bearer.
curl -sS "https://edets.com/api/v1/campaigns/IG-01/pack/ig_9x16" \
-H "Authorization: Bearer edets_live_…" \
-o ig_9x16.mp4Errors
Bodies are { "error": "…" } strings. These are the live handler messages.
| Status | error | Fix |
|---|---|---|
| 401 | Unauthorized. | Send Authorization: Bearer edets_live_…. Mint a key at /api/keys. |
| 400 | quality must be max or motion. | Send max or motion. |
| 400 | size must be 480 or 720. | Public API accepts 480 or 720. size=1080 returns this 400. |
| 400 | aspect must be 9:16, 1:1, or 4:5. | Send one of those three values. |
| 400 | Choose a length between 5 and 15 seconds. | Integer 5–15. |
| 400 | Only PNG, JPG, or WEBP stills are supported. | Convert the still. |
| 400 | The still exceeds the 20 MB limit. | Shrink the still. |
| 400 | Send a still file or a still URL. | Attach still or still_url. |
| 400 | Not enough seconds. Buy credits at /api/seconds. | Buy seconds at /api/seconds. |
| 429 | Rate limit exceeded. | 10 generations/minute, 100/hour per API key. GET poll and download do not count. |
| 400 | Say what happens in the shot. A mood word is not enough. | Send a full shot sentence. |
| 404 | Not found. | Wrong id, or the key does not own that job. |
| 404 | Clip is not ready yet. | Poll until succeeded, then GET …/clip. |
| 200 | Clip aspect did not match 9:16. | Poll failed. The mp4 DAR did not match. Seconds returned. The still is padded, not cropped. |
Limits
| Limit | Value |
|---|---|
| Clip length | 5–15 seconds |
| Size | 480 or 720. Max 720 = 768P. |
| Aspect | 9:16, 1:1, 4:5 |
| Still | PNG, JPG, or WEBP. 20 MB max. |
| Quality | max (edets Max) or motion (edets Motion) |
| Seconds packs | Starter 45s $8 · Pro 120s $20 · Studio 300s $45 |
| Writes | 10 generations/minute, 100/hour per API key |
| Balance | GET /api/v1/balance — seconds remaining and dollar equivalent. |