# Podcast Transcription - RSS Episodes to Text (`tidytools/podcast-rss-transcriber`) Actor

Turn public podcast RSS episodes into text, TXT and optional SRT. Use publisher transcripts when available or transcribe audio by the minute. One newest episode per feed by default; failed inputs are free.

- **URL**: https://apify.com/tidytools/podcast-rss-transcriber.md
- **Developed by:** [Yukai Lin](https://apify.com/tidytools) (community)
- **Categories:** AI, Videos, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.80 / 1,000 audio minutes

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Podcast Transcription — RSS Episodes to Text

### What does Podcast Transcription do?

Turn public podcast RSS episodes into searchable text and optional SRT subtitles. Paste one or more public RSS feed URLs into **Podcast RSS feeds**. The first-run form contains one NASA feed and selects the newest episode from each feed (`maxEpisodesPerFeed: 1`). Remove the example before entering your own feed.

### How to use

1. Enter a public podcast RSS URL or Apple Podcasts show link in **Podcast RSS feeds**.
2. Keep **Use publisher transcripts** enabled to use a usable transcript linked by the feed when available.
3. Set **Episodes per podcast feed** to the number of recent episodes you need, then start the Actor.
4. Read the Dataset row and download `txtUrl` or `srtUrl` when present. A plain publisher transcript may have no timing, so an SRT file may be unavailable.

### Input example

Example JSON input:

```json
{"podcastFeeds":["https://www.nasa.gov/feeds/podcasts/small-steps-giant-leaps"],"urls":[],"maxEpisodesPerFeed":1,"usePublisherTranscripts":true}
```

### Output example

The overview shows `episodeTitle`, `transcriptSource`, `text`, `txtUrl`, `srtUrl`, `billedMinutes`, `charged` and `errorType`. The full Dataset retains the audio engine's other fields. A publisher transcript row has `transcriptSource: "publisher"` and `billedMinutes: 0`.

### Pricing

A successful publisher transcript has no audio-minute event. If a publisher transcript is absent or unusable, the engine transcribes the episode audio and charges one `audio-minute` event per started minute (Free/Bronze: $0.006 per minute). Failed, silent and invalid inputs are free rows. Optional speaker labels, summaries and translated subtitles use the engine's separate existing events and prices when enabled. Check Apify's maximum cost per run before a larger batch.

### Limitations

This is a focused form for the existing audio-transcriber engine. Other valid engine inputs, including direct media URLs, uploads and dataset rows, remain available through advanced JSON and follow the same behavior and prices. Feed reading and user-supplied media downloads follow the engine's current access behavior; this Actor does not promise a robots.txt gate for either. It does not log in, solve CAPTCHAs or use stealth tools. A blocked or inaccessible source is reported as a free error row.

Only use episodes and transcripts you are entitled to process; respect publisher rights and applicable terms. The NASA URL is a public first-run sample, not a guarantee that its latest episode has a publisher transcript or a particular duration.

### FAQ

**Will every episode have an SRT file?** A plain publisher transcript may have no timing, so an SRT file may be unavailable.

### Optional content pack

Set `"contentPack":true` for chapter, show notes, X and LinkedIn drafts in `contentPack`. Add `"contentPackSubtitleLanguages":["Spanish"]` for one translated SRT/VTT language in `contentPack.translatedSubtitles` (the existing $0.002/started-minute subtitle event applies on success). Drafts require review before publishing. Off by default; existing output and charges stay the same. A failed pack has `contentPackError` and no pack charge. **Charge starts 2026-11-06 or later:** one $0.10 `content-pack` event per successful pack, in addition to existing audio-minute charges. Until the operator sets `CONTENT_PACK_CHARGE_FROM` to an announced date on or after 2026-11-06 and that date arrives, the text pack is free.

# Actor input Schema

## `contentPack` (type: `boolean`):

Off by default. Draft chapters, show notes, X and LinkedIn posts. One content-pack event at $0.10 per successful episode from 2026-11-06; free before then. Translated subtitles use the existing minute event.

## `contentPackSubtitleLanguages` (type: `array`):

One optional translated SRT/VTT language billed by the existing subtitle-language-minute event.

## `podcastFeeds` (type: `array`):

Paste public podcast RSS feed URLs or Apple Podcasts show links, one per line. The newest episode per feed is selected by default. The NASA feed is a first-run example; remove it for your own feed.

## `maxEpisodesPerFeed` (type: `integer`):

How many of the newest episodes to transcribe from each feed.

## `usePublisherTranscripts` (type: `boolean`):

Many podcasts publish their own transcript in the feed (Podcasting 2.0 <podcast:transcript>: VTT, SRT, JSON, HTML or text). When an episode has one, it is downloaded and converted to the same output (text, segments, SRT/VTT for timed formats) with transcriptSource "publisher", and no audio minutes are charged. A transcript that is empty or does not match the episode length is ignored and the audio is transcribed as usual.

## `urls` (type: `array`):

Advanced: direct audio/video links or podcast feed URLs. Other valid audio-transcriber inputs retain their original behavior and pricing.

## `audioFiles` (type: `array`):

Upload recordings from your computer in Apify Console (they are stored in a key-value store of your account and read from there). Same formats and 2 GB limit as the URLs. In API calls, pass file URLs in "urls" instead.

## `audioUrls` (type: `array`):

Same as "Audio or video file URLs", in Apify's request list format (also accepts a link to a text file with URLs). Kept for existing integrations; new integrations should use "urls".

## `episodesSince` (type: `string`):

Only feed episodes published on or after this date (YYYY-MM-DD), or within e.g. "30 days". Applied before "Episodes per podcast feed". Filtered episodes get no row; SUMMARY.episodesFiltered counts them.

## `episodesUntil` (type: `string`):

Only feed episodes published on or before this date (YYYY-MM-DD, the whole day counts).

## `titleIncludes` (type: `array`):

Only feed episodes whose title contains one of these texts (case-insensitive). \* matches anything, e.g. "interview\*ceo".

## `titleExcludes` (type: `array`):

Skip feed episodes whose title contains one of these texts (case-insensitive), e.g. "trailer", "rerun", "best of".

## `language` (type: `string`):

ISO 639-1 code such as en, es, de, ja or zh (names such as English also work). Leave empty to detect automatically. YouTube links are always transcribed in the spoken language; when it differs from this, the row says so in languageNote.

## `vocabulary` (type: `string`):

Names, brands or terms that appear in the audio, e.g. "Apify, Cloudflare, Kubernetes". Helps spell them correctly.

## `transcriptionEngine` (type: `string`):

Choose "fast" (default, Cloudflare Workers AI Whisper) or "accurate" (self-hosted Whisper with more thorough decoding), e.g. {"transcriptionEngine":"accurate"}. Accurate is slower; busy pieces fall back to Fast and each result reports asrBackend. Both cost the same. Speaker labels use Deepgram; YouTube links use the YouTube service regardless of this setting.

## `searchTerms` (type: `array`):

JSON array of up to 50 brands, names or topics to find, e.g. {"searchTerms":\["Apify","customer churn"]}; in the form, enter one per line. Matches include timestamp, sentence, speaker and a link to that moment; the run also saves KEYWORD_MATCHES.csv. Matching ignores case and accents; a trailing \* matches word endings, e.g. "transcri\*". No extra charge.

## `saveSubtitles` (type: `boolean`):

Also save subtitle files for every transcript (no extra charge).

## `saveTranscriptFiles` (type: `boolean`):

Also save a plain-text (.txt) and a Markdown (.md) transcript for every file, linked as txtUrl and mdUrl, plus one combined file for the whole run when there are 2 or more transcripts (no extra charge).

## `summarize` (type: `boolean`):

Add a short summary and timestamped chapters to each transcript. Charged as a separate event per file (see Pricing). Recordings shorter than 2 minutes get a summary only.

## `meetingNotes` (type: `boolean`):

Add structured meeting notes to each transcript: a short summary, decisions, action items (task, owner, due date) and, with speaker labels on, talk time per speaker and each speaker's main points. Saved in the row (meetingNotes) and as a Markdown file (meetingNotesUrl). No extra charge.

## `speakerLabels` (type: `boolean`):

Label each segment and paragraph with Speaker 1, Speaker 2… and count the speakers. Uses Deepgram nova-3 instead of Whisper and is billed as "audio minute with speaker labels" ($0.015/min) instead of the regular audio minute. Some languages (e.g. Chinese) are not available; those files fail with errorType "unsupported" and are not charged. Not available for YouTube links (free "unsupported" row).

## `wordTimestamps` (type: `boolean`):

Add a words array \[{word, start, end}] to every segment (for video editing and precise subtitle alignment). Included in the regular $0.006/min price. With speaker labels on, the words come from nova-3 and the speaker-label price applies (not charged twice).

## `subtitleLanguages` (type: `array`):

Also make SRT + VTT subtitles in these languages (up to 5), e.g. Spanish, German, Traditional Chinese, ja. Timings and speaker labels are kept. $0.002 per audio minute per language, charged only for languages that succeed.

## `datasetId` (type: `string`):

A dataset from another Actor run, e.g. a podcast or video scraper. Its media URLs are added to the list above. Have the rows themselves instead? Paste them into "datasetItems" below.

## `datasetField` (type: `string`):

Field that holds the media URL, e.g. audioUrl or media.url (a dot path, for nested fields). Leave empty to detect it (audioUrl, mediaUrl, videoUrl, url...). Used for the dataset above and for pasted dataset rows.

## `datasetItems` (type: `array`):

JSON array of rows from another Actor dataset, e.g. {"datasetItems":\[{"audioUrl":"https://example.com/episode.mp3"}]}. Each row needs a media URL field (audioUrl, mediaUrl, videoUrl, url, etc.), or set datasetField to its field name; dot paths work. A row may itself be a URL. Rows without usable links get free error rows with their index; duplicate media is transcribed once. Other source columns are not copied to results.

## `maxMinutesPerFile` (type: `integer`):

Skip files longer than this. Skipped files get a result row with errorType "too_large" and are not charged. Empty = no limit (0 also means no limit). YouTube videos: empty = 240 minutes (the maximum).

## `maxTotalMinutes` (type: `integer`):

Stop charging for new files once this many audio minutes were transcribed. Files that would go over it get a row with errorType "limit_reached" and are not charged. Empty or 0 = no limit (your Apify spending limit still applies).

## `maxConcurrency` (type: `integer`):

Long files are split into 90-second pieces that are transcribed in parallel. More is faster.

## `fileConcurrency` (type: `integer`):

How many files (podcast episodes, URLs) are downloaded and transcribed at the same time. More is faster for batches; rows then arrive in the order files finish (inputIndex gives the input position). Your max charge per run and maxTotalMinutes are still respected: the cost of every file in progress is reserved before it is transcribed. With little run memory fewer files run at once (about 3 at 1024 MB).

## `fileTimeoutSecs` (type: `integer`):

A file whose download, conversion and transcription take longer than this is reported with errorType "timeout" and not charged; the run goes on with the next file. Raise it for recordings of several hours. YouTube videos get at least their length x 15 s + 20 min (our YouTube queue decides how long they take). 0 = no limit.

## `proxyConfiguration` (type: `object`):

Only used for requests sent from Apify's network (file downloads and podcast feeds). Apify proxy usage is billed to your Apify account.

## `downloadVia` (type: `string`):

Some file hosts refuse downloads from data centers or after many files in a row (HTTP 401/403/429 or a bot-check page). Auto then tries our backend (any size), our Oracle server (up to 15 MB) and our home line in Taiwan (up to 10 MB) before giving up. Each failed file is a free row that lists what was tried. No extra charge. We never use cookies or logins and never solve CAPTCHAs; when a host sends Retry-After we wait that long.

## `downloadUserAgent` (type: `string`):

Every file is first requested with our own User-Agent (TidyToolsTranscriber). Some hosts serve media files only to browsers: with Auto, a file that was refused is requested once more with a standard desktop browser User-Agent string (a plain header; no cookies, logins, CAPTCHA solving or browser automation). Choose TidyTools only to never switch. Only used for the audio/video file links you give.

## `newEpisodesOnly` (type: `boolean`):

Remember what was transcribed (podcast episode GUID or file URL) and skip it in later runs with the same monitor name. From podcast feeds, only episodes published after the newest one already transcribed are taken, so a run on a day without a new episode transcribes (and charges) nothing. Failed files are retried next time.

## `backfillOlderEpisodes` (type: `boolean`):

With "Only new episodes": also take older episodes that were never transcribed (up to Max episodes per feed per run), to work through a back catalogue over several runs. Off by default, so scheduled runs only pick up new releases.

## `monitorName` (type: `string`):

Runs with the same name share the list of transcribed files, e.g. one name per podcast schedule. Letters, digits and dashes.

## `sourceFileId` (type: `string`):

Your own id of the file (Drive file id, Dropbox file id `id:...`, storage object key...). Used for runs with one file; it is copied to the row so your workflow can match results, and it is the key of `resumeStore`. Without it, an id is derived from the link (Drive file id, Dropbox shared link id, the bucket and object of a presigned URL). Every row also has this run's `runId`.

## `resumeStore` (type: `string`):

Optional. A name such as `n8n-recordings`: finished transcripts are remembered by source file id in a named key-value store of your Apify account. When the same file is sent again with the same settings (a workflow retry), its saved transcript is returned again with `reused: true` and is not charged again; if another run is still transcribing that file, this run waits for it. Leave empty to turn off.

## Actor input object example

```json
{
  "contentPack": false,
  "contentPackSubtitleLanguages": [],
  "podcastFeeds": [
    "https://www.nasa.gov/feeds/podcasts/small-steps-giant-leaps"
  ],
  "maxEpisodesPerFeed": 1,
  "usePublisherTranscripts": true,
  "urls": [],
  "transcriptionEngine": "fast",
  "saveSubtitles": true,
  "saveTranscriptFiles": true,
  "summarize": false,
  "meetingNotes": false,
  "speakerLabels": false,
  "wordTimestamps": false,
  "subtitleLanguages": [],
  "maxConcurrency": 3,
  "fileConcurrency": 3,
  "fileTimeoutSecs": 1800,
  "downloadVia": "auto",
  "downloadUserAgent": "auto",
  "newEpisodesOnly": false,
  "backfillOlderEpisodes": false
}
```

# Actor output Schema

## `transcripts` (type: `string`):

No description

## `full` (type: `string`):

No description

## `subtitles` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "contentPackSubtitleLanguages": [],
    "podcastFeeds": [
        "https://www.nasa.gov/feeds/podcasts/small-steps-giant-leaps"
    ],
    "maxEpisodesPerFeed": 1,
    "urls": [],
    "subtitleLanguages": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("tidytools/podcast-rss-transcriber").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "contentPackSubtitleLanguages": [],
    "podcastFeeds": ["https://www.nasa.gov/feeds/podcasts/small-steps-giant-leaps"],
    "maxEpisodesPerFeed": 1,
    "urls": [],
    "subtitleLanguages": [],
}

# Run the Actor and wait for it to finish
run = client.actor("tidytools/podcast-rss-transcriber").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "contentPackSubtitleLanguages": [],
  "podcastFeeds": [
    "https://www.nasa.gov/feeds/podcasts/small-steps-giant-leaps"
  ],
  "maxEpisodesPerFeed": 1,
  "urls": [],
  "subtitleLanguages": []
}' |
apify call tidytools/podcast-rss-transcriber --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tidytools/podcast-rss-transcriber"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/0Roc7SavHR3S8766I/builds/4fOw6eDsstPdREjmj/openapi.json
