Turn text into podcasts, programmatically.
A small JSON API for creating shows and generating episodes. Bring your own source text, pick a podcast style, get back an MP3 and an RSS feed.
Introduction
The API is organized around shows (the podcast itself — title, cover art, RSS feed) and episodes (individual generated audio tracks based on source text you supply).
All requests are JSON over HTTPS. Responses are JSON. The base URL is:
https://www.text-to-podcast.com/api/v1
Episode generation is asynchronous — when you create an episode the API returns immediately with a pending status, then you poll the episode endpoint until status flips to ready and an audioUrl appears. Generation typically takes a couple of minutes depending on episode length.
Authentication
Every request must include an Authorization header with a bearer API key. Generate one from Settings → API keys. Keys are shown once at creation time — store them somewhere safe.
Authorization: Bearer echo_a1b2c3d4e5f6...
Keys are scoped to your user account. A request with a missing or invalid key returns 401 Unauthorized.
Quickstart
A minimal end-to-end example: create a show, kick off an episode, and poll until the audio is ready. Set API_KEY in your environment first.
Node.js (18+, uses built-in fetch)
const API = "https://www.text-to-podcast.com/api/v1";
const headers = {
Authorization: `Bearer ${process.env.API_KEY}`,
"Content-Type": "application/json",
};
// 1. Create a show
const showRes = await fetch(`${API}/shows`, {
method: "POST",
headers,
body: JSON.stringify({
title: "Weekend Reads",
description: "Long-form essays, narrated.",
category: "Arts",
}),
});
const show = await showRes.json();
// 2. Create an episode
const epRes = await fetch(`${API}/shows/${show.id}/episodes`, {
method: "POST",
headers,
body: JSON.stringify({
title: "Why teams ship slower than they think",
sourceText: "Every engineering org I've worked with has...",
targetMinutes: 10,
style: "narrative",
}),
});
let episode = await epRes.json();
// 3. Poll until ready
while (episode.status !== "ready" && episode.status !== "failed") {
await new Promise((r) => setTimeout(r, 5000));
const r = await fetch(`${API}/episodes/${episode.id}`, { headers });
episode = await r.json();
}
if (episode.status === "ready") {
console.log("Audio:", episode.audioUrl);
} else {
console.error("Failed:", episode.errorMessage);
}Python (3.8+, uses requests)
import os
import time
import requests
API = "https://www.text-to-podcast.com/api/v1"
headers = {
"Authorization": f"Bearer {os.environ['API_KEY']}",
"Content-Type": "application/json",
}
# 1. Create a show
show = requests.post(
f"{API}/shows",
headers=headers,
json={
"title": "Weekend Reads",
"description": "Long-form essays, narrated.",
"category": "Arts",
},
).json()
# 2. Create an episode
episode = requests.post(
f"{API}/shows/{show['id']}/episodes",
headers=headers,
json={
"title": "Why teams ship slower than they think",
"sourceText": "Every engineering org I've worked with has...",
"targetMinutes": 10,
"style": "narrative",
},
).json()
# 3. Poll until ready
while episode["status"] not in ("ready", "failed"):
time.sleep(5)
episode = requests.get(
f"{API}/episodes/{episode['id']}",
headers=headers,
).json()
if episode["status"] == "ready":
print("Audio:", episode["audioUrl"])
else:
print("Failed:", episode["errorMessage"])Errors
The API uses standard HTTP status codes. Error responses always include a JSON body with an error field describing what went wrong.
{
"error": "sourceText must be at least 50 characters"
}400Bad requestMissing or invalid parameters (e.g. blank title, source text too short).
401UnauthorizedMissing or invalid API key.
403ForbiddenPlan limit reached (max shows, monthly minutes, episode length).
404Not foundThe show or episode doesn't exist, or doesn't belong to your account.
List shows
/api/v1/showsReturns every show owned by the authenticated user, newest first.
curl https://www.text-to-podcast.com/api/v1/shows \ -H "Authorization: Bearer $API_KEY"
Response
{
"shows": [
{
"id": 42,
"title": "Weekend Reads",
"description": "Long-form essays, narrated.",
"author": "Jane Doe",
"category": "Technology",
"language": "en-us",
"coverImageUrl": "https://...",
"createdAt": "2026-05-12T10:30:00.000Z"
}
]
}Create a show
/api/v1/showsCreates a new show. Only titleis required; everything else has a sensible default, including an auto-generated cover image if you don't supply one.
titlestringrequiredThe show's display name.
descriptionstringA short summary shown in podcast directories.
authorstringAuthor/host name. Defaults to text-to-podcast.com.
categorystringApple Podcasts category. Defaults to Technology.
languagestringBCP-47 language tag. Defaults to en-us.
coverImageUrlstring (URL)Public URL to a square cover image. If omitted, one is generated for you.
curl -X POST https://www.text-to-podcast.com/api/v1/shows \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Weekend Reads",
"description": "Long-form essays, narrated.",
"category": "Arts"
}'Response · 201 Created
{
"id": 42,
"title": "Weekend Reads",
"description": "Long-form essays, narrated.",
"author": "text-to-podcast.com",
"category": "Arts",
"language": "en-us",
"coverImageUrl": "https://...",
"rssUrl": "https://www.text-to-podcast.com/api/shows/42/rss",
"createdAt": "2026-05-19T12:00:00.000Z"
}The rssUrl is publicly accessible and can be submitted directly to Apple Podcasts, Spotify, etc.
List episodes
/api/v1/shows/:id/episodesReturns every episode for a show, newest first. The :id path parameter is the numeric show id returned by Create a show.
curl https://www.text-to-podcast.com/api/v1/shows/42/episodes \ -H "Authorization: Bearer $API_KEY"
Response
{
"episodes": [
{
"id": 1001,
"title": "Why teams ship slower than they think",
"description": "...",
"status": "ready",
"audioUrl": "https://.../episode-1001.mp3",
"durationSeconds": 612,
"errorMessage": null,
"createdAt": "2026-05-18T09:15:00.000Z"
}
]
}status is one of pending, generating, ready, or failed. Only ready episodes have an audioUrl.
Create an episode
/api/v1/shows/:id/episodesKicks off generation of a new episode. Returns immediately with 202 Accepted — poll Get an episode to watch the status.
titlestringrequiredEpisode title shown in podcast players.
sourceTextstringrequiredThe text the episode will be based on. Must be at least 50 characters. Paste in an article, essay, transcript, memo — whatever you want turned into audio.
targetMinutesintegerTarget episode length in minutes. Defaults to 8. Clamped between 2and your plan's maximum.
stylestringPodcast format. One of interview, conversational, debate, all_in, narrative. Defaults to interview. See Podcast styles.
publishedbooleanWhether the episode appears in the public RSS feed once ready. Defaults to true. Pass false to save it as a draft.
curl -X POST https://www.text-to-podcast.com/api/v1/shows/42/episodes \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Why teams ship slower than they think",
"sourceText": "Every engineering org I've worked with has...",
"targetMinutes": 10,
"style": "narrative",
"published": false
}'Response · 202 Accepted
{
"id": 1001,
"showId": 42,
"title": "Why teams ship slower than they think",
"description": "An auto-generated summary of the source text.",
"status": "pending",
"published": false,
"targetMinutes": 10,
"createdAt": "2026-05-19T12:01:00.000Z"
}Get an episode
/api/v1/episodes/:idReturns the current state of an episode. Poll this endpoint every few seconds after creating an episode until status is ready.
curl https://www.text-to-podcast.com/api/v1/episodes/1001 \ -H "Authorization: Bearer $API_KEY"
Response
{
"id": 1001,
"showId": 42,
"title": "Why teams ship slower than they think",
"description": "...",
"status": "ready",
"audioUrl": "https://.../episode-1001.mp3",
"durationSeconds": 612,
"errorMessage": null,
"createdAt": "2026-05-19T12:01:00.000Z"
}If status is failed, errorMessage will explain why.
Podcast styles
The style parameter on episode creation controls how the script is written and voiced. Five formats are supported:
interviewHost + GuestA host interviews a guest who is the expert/source on the topic. Host drives with questions; guest carries the substance.
conversationalTwo co-hostsTwo co-hosts of roughly equal authority riffing on the topic. Symmetric back and forth, friendly disagreement, shared discovery.
debateOpposing positionsTwo hosts argue different sides of the topic. Each defends their position, challenges the other, and concedes points where appropriate.
all_inFour-host roundtableA four-person roundtable in the All-In style — overlapping voices, banter, sharp takes, occasional interruptions.
narrativeStorytellingA produced narrative in the spirit of Serial or This American Life. A narrator carries the arc with vivid scene-setting; a second voice appears sparingly as a quoted source, witness, or character. Designed for stories with emotional arcs rather than discussion.