Everything you need to integrate with FernPod programmatically. Built for AI agents.
https://fernpod.com
LLM / Agent-friendly version
Plain text, optimized for AI consumption. Feed this to your agent.
FernPod supports two authentication methods. All authenticated endpoints require one of these headers.
Obtained from /auth/login or /auth/register. Expires after 7 days.
Authorization: Bearer <jwt-token>
Created via /api-keys. Requires active subscription. Keys start with fpod_.
Authorization: ApiKey fpod_<key>
/auth/register
Public
Create a new account. Returns a JWT token.
Notes
email (string, required) — valid email addresspassword (string, required) — minimum 8 charactersname (string, optional) — display nameRequest Body
Response
/auth/login
Public
Authenticate and receive a JWT token.
Notes
email (string, required)password (string, required)Request Body
Response
/auth/me
Auth Required
Get the current authenticated user's info.
Response
/podcasts
Auth Required
List all podcasts owned by the authenticated user.
Response
/podcasts
Auth Required
Create a new podcast. Requires active subscription.
Notes
title (string, required) — podcast titledescription (string, optional)author (string, optional) — defaults to user nameemail (string, optional) — defaults to user emaillanguage (string, optional) — ISO code, defaults to "en"category (string, optional) — iTunes categorysubcategory (string, optional) — iTunes subcategoryexplicit (boolean, optional) — defaults to falseis_private (boolean, optional) — defaults to falseRequest Body
Response
/podcasts/:id
Auth Required
Get a single podcast by ID.
Response
/podcasts/:id
Auth Required
Update a podcast. Send only the fields you want to change.
Notes
title, description, author, email, language, category, subcategory, image_url, website_url, explicit, is_private — all optionalRequest Body
Response
/podcasts/:id/cover
Auth Required
Upload cover art. Send raw image bytes (not multipart form data). Max 10 MB. Supports PNG, JPG.
Notes
Content-Type to the image MIME typeResponse
/podcasts/:id
Auth Required
Delete a podcast and all its episodes, audio files, and analytics. This is permanent.
Response
All episode routes are nested under /podcasts/:podcastId/episodes.
/podcasts/:podcastId/episodes
Auth Required
List all episodes for a podcast. Optionally filter by status.
Notes
?status=draft|published|scheduled|unpublished|planned (optional)Response
/podcasts/:podcastId/episodes
Auth Required
Create a new episode (metadata only — upload audio or generate via TTS separately). Requires active subscription.
Notes
title (string, required)description (string, optional)season (number, optional)episode_number (number, optional)episode_type (string, optional) — "full", "trailer", or "bonus"explicit (boolean, optional)status (string, optional) — "draft" (default), "published", or "planned"scheduled_at (string, optional) — ISO timestamp for scheduled publishplanned_for (string, optional) — date for content calendarchapters (array, optional) — array of { start: number, title: string } for chapter markersRequest Body
Response
/podcasts/:podcastId/episodes/:episodeId/upload
Auth Required
Upload audio to an existing episode. Send raw audio bytes (not multipart). Supports MP3, M4A, OGG.
Notes
Content-Type to the audio MIME typeResponse
/podcasts/:podcastId/episodes/:episodeId
Auth Required
Get a single episode by ID.
Response
/podcasts/:podcastId/episodes/:episodeId
Auth Required
Update an episode. Send only the fields you want to change. Setting status to "published" auto-sets published_at.
Notes
title, description, season, episode_number, episode_type, explicit, status, scheduled_at, planned_for, chapters — all optionalRequest Body
Response
/podcasts/:podcastId/episodes/:episodeId
Auth Required
Delete an episode and its audio file from storage. This is permanent.
Response
Generate audio from text. The TTS endpoint accepts text and produces audio that is attached to an existing episode. This is the core of the agent workflow: create an episode, then generate audio from a script.
/podcasts/:podcastId/episodes/:episodeId/tts
Auth Required
Generate audio from text for an existing episode. Audio generation is asynchronous — the endpoint returns immediately with a processing status.
Notes
text (string, required) — the text content to convert to speech202 Accepted — audio generation happens asynchronouslyGET /podcasts/:podcastId/episodes/:episodeId to check when audio is ready (audio_url will be populated)Request Body
Response
Trigger AI transcription on uploaded audio. Generates a full transcript with timestamps and auto-generates chapter markers.
/podcasts/:podcastId/episodes/:episodeId/transcribe
Auth Required
Trigger AI transcription on an episode's audio. Requires audio to be uploaded first. Asynchronous — poll the episode to check transcript_status.
Notes
202 Accepted — transcription runs asynchronouslyGET /podcasts/:podcastId/episodes/:episodeId and check transcript_status fieldai_chaptersResponse
/podcasts/:podcastId/episodes/:episodeId/transcript
Auth Required
Fetch the full transcript for an episode.
Notes
404 if no transcript exists202 with { status: "processing" } if transcription is still runningResponse
Full analytics with geographic, app, and referrer breakdowns included with all subscriptions.
/podcasts/:podcastId/analytics
Auth Required
Get podcast-level analytics.
Notes
?days=30 (optional, default: 30)Response
/podcasts/:podcastId/analytics/episodes/:episodeId
Auth Required
Get analytics for a single episode.
Notes
?days=30 (optional, default: 30)Response
/billing/checkout
Auth Required
Create a Stripe Checkout session to start your subscription.
Notes
plan (string, required) — "starter"Request Body
Response
/billing/portal
Auth Required
Create a Stripe Customer Portal session to manage subscription.
Notes
Response
/billing/status
Auth Required
Get current billing status.
Response
Requires active subscription. API keys provide an alternative to JWT tokens for programmatic access.
/api-keys
Auth Required
List all API keys for the authenticated user.
Response
/api-keys
Auth Required
Create a new API key. The full key is only returned once — save it immediately.
Notes
name (string, required) — descriptive namescopes (string, optional) — defaults to "read,write"Request Body
Response
/api-keys/:id
Auth Required
Revoke an API key. This is immediate and permanent.
Response
/feedback
Auth Required
Submit feedback (bug report, feature request, or general).
Notes
type (string, optional) — "bug", "feature", or "general" (default: "general")subject (string, required) — max 200 charactersmessage (string, required) — max 5000 charactersRequest Body
Response
/feedback
Auth Required
List all your feedback submissions.
Response
/feedback/:id
Auth Required
Get a single feedback item.
Response
These routes require no authentication and serve public-facing content.
/feed/:slug
RSS feed for a podcast. Apple Podcasts and Spotify compatible. Submit this URL to podcast directories.
/p/:slug
Public podcast website page with episode listings and embedded player.
/p/:slug/episodes/:episodeSlug
Public episode page with player, description, and metadata.
/embed/:episodeId
Embeddable episode player widget. Use in an iframe.
/embed/podcast/:podcastId
Embeddable podcast player showing the 5 most recent episodes.
/audio/:userId/:podcastId/:filename
Audio file streaming with range request support. Downloads are tracked and deduplicated (same IP + episode within 24h = 1 download).
/covers/:userId/:podcastId/:filename
Cover art image serving. Cached for 24 hours.
| Feature | Starter ($10/mo) |
|---|---|
| Podcasts | Unlimited |
| Episodes | Unlimited |
| Storage | Unlimited |
| Downloads/month | Unlimited |
| Analytics | Full |
| API access | Yes |
| Text-to-Speech | Yes |
| Custom domain | Yes |
| Private podcasts | Yes |
7-day free trial included. Content goes offline when subscription is canceled. Fair use policy: 200,000 downloads/month soft cap.
All errors return a JSON object with an error field and an appropriate HTTP status code.
| Status | Meaning |
|---|---|
400 | Bad request — missing or invalid parameters |
401 | Unauthorized — missing or invalid auth token/key |
403 | Forbidden — subscription required or insufficient permissions |
404 | Not found — resource doesn't exist or not owned by you |
429 | Rate limited — 60 requests per minute |
500 | Server error — something went wrong on our end |
Create an account, make a podcast, generate an episode with TTS, and publish — the full agent workflow:
audio_url is set.transcript_status = "completed".is_private: true on create. No public page, no RSS feed, no embed. Audio served via signed URLs. Learn more.chapters array on episode create/update.