text-to-podcast.com API · v1

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 request

Missing or invalid parameters (e.g. blank title, source text too short).

401Unauthorized

Missing or invalid API key.

403Forbidden

Plan limit reached (max shows, monthly minutes, episode length).

404Not found

The show or episode doesn't exist, or doesn't belong to your account.

List shows

GET/api/v1/shows

Returns 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

POST/api/v1/shows

Creates a new show. Only titleis required; everything else has a sensible default, including an auto-generated cover image if you don't supply one.

titlestringrequired

The show's display name.

descriptionstring

A short summary shown in podcast directories.

authorstring

Author/host name. Defaults to text-to-podcast.com.

categorystring

Apple Podcasts category. Defaults to Technology.

languagestring

BCP-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

GET/api/v1/shows/:id/episodes

Returns 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

POST/api/v1/shows/:id/episodes

Kicks off generation of a new episode. Returns immediately with 202 Accepted — poll Get an episode to watch the status.

titlestringrequired

Episode title shown in podcast players.

sourceTextstringrequired

The 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.

targetMinutesinteger

Target episode length in minutes. Defaults to 8. Clamped between 2and your plan's maximum.

stylestring

Podcast format. One of interview, conversational, debate, all_in, narrative. Defaults to interview. See Podcast styles.

publishedboolean

Whether 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

GET/api/v1/episodes/:id

Returns 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 + Guest

A host interviews a guest who is the expert/source on the topic. Host drives with questions; guest carries the substance.

conversationalTwo co-hosts

Two co-hosts of roughly equal authority riffing on the topic. Symmetric back and forth, friendly disagreement, shared discovery.

debateOpposing positions

Two hosts argue different sides of the topic. Each defends their position, challenges the other, and concedes points where appropriate.

all_inFour-host roundtable

A four-person roundtable in the All-In style — overlapping voices, banter, sharp takes, occasional interruptions.

narrativeStorytelling

A 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.

Need something the API doesn't cover yet? Let us know — we'd love to hear what you're building.