Get started with the API
Programmatic access to the Jellypod platform.
The Jellypod API creates and manages hosts, sources, podcast episodes, videos, shorts, and voiceovers. A host in the API is a saved Character in Studio. Start with the Video, short, and Voiceover guide for creation, file uploads, progress, retries, and downloads.
For copyable request examples, see Source-to-media recipes.
Connect
API requests need a paid plan or the free trial. Create an organization API key
in Developers. The API Keys
tab is visible to admins and to members with permission to manage keys. Send
requests to https://api.jellypod.com/v1 with this header:
Authorization: Bearer YOUR_API_KEY
Credits and usage
The API uses the same product credit rates and plan limits as Studio.
Generation costs depend on the product and its settings. Check GET /account
for your plan and available balance. Podcast rendering and publishing are free;
status checks and downloads do not start new generation. See
Understanding Credits
for details.
Podcast generation and retries
For POST /episodes/generate and POST /episodes/import, send an optional
Idempotency-Key header. Reuse the key only with the same request. For 24
hours, Jellypod then returns the original episode and workflow IDs without
starting another generation. Reusing the key with different input returns
409. Without a key, every request starts a generation.
If acceptance is uncertain, check the episode ID in the error before creating another episode. Video, short, and voiceover create and retry endpoints accept the same optional key.
Choose episode outputs
Send outputs with POST /episodes/generate or POST /episodes/import to
choose the files you need:
outputs |
Result |
|---|---|
["audio"] |
MP3, without video work |
["video"] |
MP4 with sound |
["audio", "video"] |
Both files, available independently |
[] |
Editable content and narration, without exported files |
For example, add "outputs": ["audio"] to your existing generation request
to get an MP3 without waiting for video. Poll GET /episodes/{episode_id}.
Its outputs.audio and outputs.video each contain status, download_url,
current, and error. A status of available supplies a download URL;
preparing means the requested file is still being produced; failed reports
an error; not_requested means no file of that format was requested.
Audio can be available while video is still preparing. A video failure does
not remove a successful audio file.
The episode’s status still describes publication (draft, published, or
scheduled). Read progress.status for production: completed means
narration and all requested files finished. With outputs: [], it means the
editable content is ready. A completed request can still have content gaps,
reported in progress.needs_attention.
Existing requests keep their behavior: render: true produces audio and video;
render: false or omission of both fields leaves an editable draft. render
is deprecated. Send either outputs or render; sending both is rejected.
Generation uses credits; exporting saved content does not regenerate narration.
Export saved content
After editing in Studio, or generating with outputs: [], request files with:
POST /v1/episodes/{episode_id}/export
Content-Type: application/json
Idempotency-Key: your-unique-export-key
{"outputs": ["audio"]}
The export action accepts audio, video, or both and returns 202 with id,
outputs, and progress. Its Location header points to the episode. Poll
that URL at the returned interval. Export reuses current files when possible,
produces missing or outdated files from saved content, and never publishes the
episode. Publishing remains an explicit action.
A failed replacement preserves the previous successful download. current
indicates whether the available file reflects the episode’s current content.
Request export again with a new idempotency key to retry failed output work;
use the same key only when resending an identical request after an uncertain
response. The existing generation retry endpoint has different behavior and
can charge generation credits again.
Download links are signed and expire. Storage links last 24 hours; fetch the episode or its download endpoint again for a fresh link. Store the episode ID rather than treating a signed URL as a permanent identifier.
Episode progress, retries, and downloads
Episodes share the status, retry, and download endpoints that videos, shorts,
and voiceovers use. GET /episodes/{episode_id} still returns the full episode
with its embedded progress.
| Action | Endpoint |
|---|---|
| Progress | GET /episodes/{episode_id}/status |
| Retry | POST /episodes/{episode_id}/retry |
| Download | GET /episodes/{episode_id}/download |
| Export saved content | POST /episodes/{episode_id}/export |
| Captions and watermark | GET or PATCH /episodes/{episode_id}/video-settings |
The video settings endpoints (also under /videos, /shorts, and
/voiceovers) read and change caption visibility, caption style, and the
watermark corner the next render uses. A value saved on the item overrides the
podcast default for an episode and the organization default for a video, and
null returns that field to its default. Upload new watermark images in Studio.
The status endpoint returns id, object (episode), status, phase,
error, needs_attention, poll_after_seconds, and studio_url. While
poll_after_seconds is above zero, the Retry-After header carries the same
wait. failed means requested work could not finish; successful files remain
available. A usable episode with gaps reports needs_attention as counts of
unvoiced_lines, missing_visuals, and skipped_sources; fix them in Studio.
It is null when nothing is missing.
Retry restarts an episode whose first generation failed, from the request that
started it, and charges generation credits again. It returns 202 with the
episode in data, a Location header pointing to the status endpoint, and
Retry-After: 10. It accepts an optional Idempotency-Key. A 409 means
nothing started: the episode is still generating, already has playable audio,
or failed in a way another attempt cannot fix, and the error says what to do.
Retry failed file production with POST /episodes/{episode_id}/export; this
generation retry endpoint does not retry export work.
Download returns id, object, media, and delivery for the served render.
Published and scheduled episodes keep serving that version until publication
changes. Use the episode’s outputs download URLs to retrieve newer draft
exports. Draft episodes can download each requested file as it finishes.
Send media=video for the video file; the default is audio. delivery has
the same available, preparing, and unavailable forms as
product downloads.
Import script text
POST /episodes/import accepts script or transcript text. It maps bracketed
speaker labels to hosts that already exist in your organization, or assigns
Host IDs to labels explicitly with speaker_characters, and queues a new
episode. A label matching no host, with no explicit assignment, is refused,
rather than creating a Host for it: a 422 with reason: "unresolved_speakers"
and a speakers array naming the unmatched labels. Create any host you plan to
name first, with POST /hosts or in Studio, then use its exact name as the
label or its ID in speaker_characters.
{
"podcast_id": "podcast_id",
"script": "[Host One]\nWelcome to the show. Today we're talking about how small daily habits can make a big difference.\n\n[Host Two]\nI love that topic. Let's start with the easiest habit someone can try this week."
}
Text import removes clearly unspoken titles, production rules, and visual or duration directions while preserving narration and inline delivery tags. Uncertain lines stay; if classification fails, text is imported as written and production notes may be narrated. Structured scripts keep the supplied text.
Imported text can contain up to 75,000 characters. Text longer than your plan’s
maximum episode length is cut to that length. Content rejected by the
speech provider also returns 422 and creates nothing. The episode stays editable without rendering by
default. Set outputs: ["audio"] when you want an MP3, or request video
or both formats. Poll
GET /episodes/{episode_id} for progress and download URLs.
An unknown podcast_id, a speaker label matching no existing host, or text
with no usable segments also return an error and create nothing. Sent with an
Idempotency-Key, these input errors are rejected immediately, so fix the
input and retry with the same key when recovery.idempotency_key is reuse.
If cleanup failed or the rejection was stored under the older policy, recovery
instead specifies new.
Generate from a prompt
Send prompt to POST /episodes/generate. You can include a duration in the
prompt. Without one, the target is about 7 minutes.
You can also set the duration directly in options instead of relying on the
prompt:
{
"podcast_id": "podcast_id",
"prompt": "An episode about the history of beekeeping.",
"options": {
"episode_length_minutes": 15
}
}
episode_length_minutes accepts an integer from 1 to 75 and defaults to 7. The
retired episode_length and web_search options are still accepted but have no
effect.
Episode prompt mode always writes a new script from the prompt and attached
sources, even when they contain finished narration. To preserve raw script text,
use POST /episodes/import with optional speaker_characters assignments.
Generate from a script
Send a structured script to POST /episodes/generate to use your own chapters
and segments.
Use GET /hosts to fetch the host_id values available to your organization.
Every segment must reference one of those hosts, and each host must be active.
Imported script text is capped at 75,000 total characters across all segments.
{
"podcast_id": "podcast_id",
"script": {
"chapters": [
{
"title": "Introduction",
"segments": [
{
"host_id": "host_id_1",
"text": "Welcome to the show. Today we're talking about how small daily habits can make a big difference."
},
{
"host_id": "host_id_2",
"text": "I love that topic. Let's start with the easiest habit someone can try this week."
}
]
}
]
}
}
Rules for script generation:
- Send either
promptorscript, not both. source_idsare only supported for prompt-based generation.- Script mode does not use
web_search,episode_length, orepisode_length_minutes. - Scripts can include up to 50 chapters, 200 segments per chapter, and 600 segments total. Each segment can contain up to 5,000 characters.
- Long scripts require enough available credits before generation starts. The final charge is based on the narration audio generated, whether or not the episode is rendered.
- Script generation creates a new episode.