# Universal Transcriber - Video, Audio & Podcast to Text (`maged120/universal-transcriber`) Actor

Turn any video, audio file or podcast feed into clean, timestamped text with automatic language detection.

- **URL**: https://apify.com/maged120/universal-transcriber.md
- **Developed by:** [Maged](https://apify.com/maged120) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $7.00 / 1,000 results

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

**Paste any link — a video, an audio file, a podcast, a news article, a documentation page or a PDF — and get back clean, AI-ready text.** One input, one consistent output, whatever the link turns out to be. No API key, no account with any transcription service, no uploads.

### What does Universal Transcriber & Content Extractor do?

Give it a list of links and it returns the content as structured data: the **full text** of each item plus **every paragraph or spoken line as its own row**, ready for embedding, search, summarisation or feeding straight into an AI pipeline.

It works out what each link is and handles it accordingly:

- **Video pages** — returns the spoken words, with timestamps.
- **Direct audio and video files** — MP3, M4A, MP4, WAV, OGG, OPUS, FLAC and more, from any public URL: webinars, interviews, lecture captures, call archives.
- **Podcast feeds** — paste an RSS feed and the newest episodes are pulled and transcribed automatically.
- **Articles and web pages** — news, blogs, documentation and wikis come back as clean body text with the navigation, ads, cookie banners and footers stripped out.
- **PDFs** — papers, reports, manuals and whitepapers are read page by page.

**You do not have to sort your links first, or know which kind each one is.** Mix them freely in a single run. Every row comes back in the same shape, tagged with what it turned out to be, so one downstream pipeline handles all of it.

**Every link returns a row — always.** Many publishers refuse automated readers. When the first attempt is refused, the Actor escalates through harder retrieval routes, and if the content still cannot be reached it returns what the page and its address do reveal: publisher, title, date, summary and topic words. Each row states exactly how much was obtained in a `contentLevel` field — `full`, `partial`, `metadata` or `none` — so an AI can trust the good rows and know not to treat a refusal page as an article.

Results land in an Apify dataset you can export as JSON, CSV, Excel or HTML, pull through the API, schedule to re-run, or wire into Make, Zapier, Google Sheets and n8n.

### Why use Universal Transcriber & Content Extractor?

- **One tool instead of four.** Most pipelines bolt a transcription service to an article extractor to a PDF parser. This replaces all of them with a single step and a single output format.
- **Feed AI, RAG and agents.** Text arrives pre-chunked into paragraphs and spoken lines — the unit embeddings actually want — so there is no splitting step to write.
- **Research and monitoring.** Track what competitors, analysts and public figures publish and say, across formats, on a schedule.
- **Content repurposing.** Turn talks, episodes, posts and reports into newsletters, summaries, show notes and social clips.
- **Subtitles and search inside media.** Timestamped lines become SRT/VTT, or let you jump to the exact moment a phrase was said.
- **Accessibility and compliance.** Produce text records of published media, meetings and briefings.

### How to use Universal Transcriber & Content Extractor

1. Click **Try for free** and open the Input tab.
2. In **URLs**, paste one link per line — videos, media files, podcast feeds, articles and PDFs can all go in together.
3. Leave **Link handling** on *Auto* unless you want to force a particular reading (see Tips).
4. Pick a **quality** level for anything that has to be transcribed from audio. *Balanced* suits almost everything.
5. Click **Start**, then open the **Output** tab and export as JSON, CSV, Excel or HTML.

To keep a site, channel or podcast permanently captured, use **Schedules** to re-run it and send new rows straight to your database, spreadsheet or AI workflow.

### Input

| Field | Type | Description |
|---|---|---|
| `urls` | array | **Required.** Any mix of video links, media files, podcast feeds, articles and PDFs. |
| `linkHandling` | string | `auto` (default), `article` to always read page text, or `media` to always transcribe. |
| `quality` | string | `fast`, `balanced` (default) or `accurate`. Applies to audio transcription only. |
| `language` | string | Two-letter code (`en`, `es`, `de`, `ar`…). Empty = detect automatically. |
| `usePublishedCaptions` | boolean | Use the publisher's own transcript when one exists. Default `true`. |
| `maxMinutesPerItem` | integer | Stop after N minutes of each recording. `0` = no limit. |
| `maxCharactersPerItem` | integer | Cap the text taken from each article or PDF. `0` = no limit. |
| `maxEpisodesPerFeed` | integer | Newest episodes to take from each feed. Default `5`. |
| `outputTranscripts` | boolean | Push one full-text row per item. Default `true`. |
| `outputSegments` | boolean | Push one row per paragraph or spoken line. Default `true`. |
| `proxyConfiguration` | object | Proxy used for requests. |

```json
{
    "urls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://en.wikipedia.org/wiki/Large_language_model",
        "https://arxiv.org/pdf/1706.03762",
        "https://example.com/recordings/all-hands-q3.mp3",
        "https://feeds.example.com/my-favourite-podcast"
    ],
    "linkHandling": "auto",
    "quality": "balanced",
    "maxEpisodesPerFeed": 5
}
```

### Output

Two row types share one dataset, separated by `entityType` and by the ready-made views on the Output tab. Every row carries a `sourceType` (`video`, `audio`, `article` or `pdf`) so you can tell at a glance what each link turned out to be. You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

A `transcript` row — one per item, holding the complete text:

```json
{
    "entityType": "transcript",
    "sourceType": "article",
    "sourceUrl": "https://en.wikipedia.org/wiki/Large_language_model",
    "title": "Large language model - Wikipedia",
    "author": null,
    "publishedAt": "2023-03-09",
    "durationSeconds": null,
    "pageCount": null,
    "language": null,
    "text": "A large language model (LLM) is an AI model trained on a vast amount of text...",
    "wordCount": 13447,
    "segmentCount": 478,
    "error": null,
    "transcribedAt": "2026-09-15T11:02:18.441Z"
}
```

A `segment` row — one per paragraph or spoken line. Spoken lines carry timestamps; paragraphs leave them empty:

```json
{
    "entityType": "segment",
    "sourceType": "video",
    "sourceUrl": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "title": "Me at the zoo",
    "index": 0,
    "startSeconds": 1.2,
    "endSeconds": 3.36,
    "text": "All right, so here we are, in front of the elephants",
    "language": "en",
    "transcribedAt": "2026-09-15T11:02:18.441Z"
}
```

#### Data fields

| Field | Description |
|---|---|
| `contentLevel` | How much was obtained: `full` (body text), `partial` (structured body), `metadata` (title, summary, date) or `none` (address only) |
| `retrievedVia` | `direct` for a live fetch, `archive` when a public archive copy supplied it |
| `sourceType` | What the link turned out to be: `video`, `audio`, `article` or `pdf` |
| `siteName` | Publisher the link belongs to |
| `description` | The page's own summary, where it publishes one |
| `topics` | Topic words taken from the address — present even when a site returns nothing |
| `title` | Title of the video, episode, article or document |
| `author` | Channel, byline or document author, where published |
| `publishedAt` | Publication date of an article, where published |
| `durationSeconds` | Length of the audio transcribed (media only) |
| `pageCount` | Number of pages (PDFs only) |
| `language` | Detected or requested language code |
| `text` | Full content (`transcript` rows) or the single line/paragraph (`segment` rows) |
| `startSeconds` / `endSeconds` | Position of a spoken line, for subtitles and jump-to-moment links |
| `wordCount` / `segmentCount` | Size of the result, handy for cost and quality checks |
| `error` | Plain-language reason when an item could not be read |

### How much content does each link produce?

Billing is per result, so what you use follows how many rows you actually extract. Articles and PDFs are the lightest — there is no audio to process — and media with a published transcript is nearly as light.

| Run | Roughly |
|---|---|
| A long article or a 15-page PDF | 100–500 rows |
| A 10-minute video with a published transcript | ~120 rows |
| A 10-minute recording transcribed from audio | ~120 rows |
| 5 podcast episodes, 45 minutes each | ~2,700 rows |

Every Apify account includes free monthly usage, enough to try it on several links at no cost. Turn `outputSegments` off to store one row per item instead of one per paragraph.

### What happens when a site blocks automated readers

Some publishers serve a "verify you are human" page, a paywall stub or an error page instead of their content — and those pages extract as perfectly clean prose, so a naive reader stores them as if they were the article. This Actor never does that.

1. It fetches the page the way a browser does, which is enough for most sites.
2. If refused, it retries over a route that negotiates the connection the way a real browser does. This alone recovers the majority of sites that reject ordinary automated traffic.
3. If still refused, it looks for a public archived copy of the page.
4. If the body genuinely cannot be reached, it falls back to the page's own structured data, then its published summary.
5. Failing all of that, it returns the publisher, the topic words and the date carried by the address itself.

Refusal, paywall and error pages are recognised and graded `none` rather than being passed off as content. A run never fails because of one stubborn link.

### Tips

- **Let Auto do the work.** It reads a news article as text and a video page as speech without being told. Use `article` only when a page holds both a video and an article you want, and `media` when a player page is being read as text.
- **Cap long items.** `maxMinutesPerItem` and `maxCharactersPerItem` stop the work at the limit rather than trimming afterwards, so they genuinely cut run time and cost.
- **Start at `fast`.** On clear speech the fast tier is already accurate and noticeably quicker. Move to `accurate` only for difficult audio.
- **Keep published transcripts on.** It is the single biggest speed and cost saver for media that has them.
- **Batch generously.** One run with fifty links is cheaper and simpler than fifty runs.
- **Switch to Residential proxy for media without a published transcript.** When an item has to be transcribed from its audio, some sources refuse datacenter traffic and the item comes back with an error. Selecting Residential resolves it.

### FAQ

**What links work?** Video pages, direct audio/video files, podcast RSS feeds, articles, blog posts, documentation pages and PDFs. Anything reachable at a public URL is worth trying; links that cannot be read come back with a clear message instead of failing the run.

**Do I need an API key or an account anywhere?** No. There is nothing to sign up for and no key to paste.

**How does it decide whether a link is an article or a video?** Automatically, from what the link actually serves. Pages that are mostly a player are transcribed; pages that are mostly prose are read as text. If a particular link is read the wrong way, the **Link handling** setting forces either behaviour.

**Is the article text clean?** Yes — navigation, ads, cookie banners, sidebars and footers are removed, leaving the body content. Pages built entirely in JavaScript may return only their summary.

**How do I know whether a row is real content?** Check `contentLevel`. Only `full` and `partial` carry the body; `metadata` carries the page's own summary; `none` means the site served nothing and the row holds only what the address reveals. Filter on it before embedding.

**A link came back with `contentLevel: none` — what now?** A handful of publishers (major paywalled newspapers especially) serve nothing at all to automated readers, and no setting changes that. The row still tells you the publisher, the date and the topic of the link. Trying the Residential proxy is worth one attempt; beyond that, the content is not publicly reachable.

**Why does an item come back empty?** It held no recognisable speech (music-only tracks are the usual case), or the page had no readable body text. The row tells you which.

**An item failed but the link works in my browser — why?** Some sources only serve their media to residential traffic. Switch the proxy setting to Residential and re-run.

**Can it transcribe a live stream?** No — a live feed has no end. Point it at the recording once the stream has finished.

**What about PDFs without a title?** Many PDFs carry no title metadata, so those rows are named after the file. The text is unaffected.

**Is this legal?** The Actor reads publicly available content and returns it as text. You are responsible for how you use the output and for complying with applicable law, copyright and the terms of the sites you point it at.

### Support

Hit a link that comes back wrong, or want another format supported? Open an issue on the **Issues** tab with the URL — coverage is extended based on what users actually paste. Custom versions and higher-volume setups are available on request.

# Actor input Schema

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

One link per line. Video pages, direct audio/video files, podcast feeds, news and blog articles, documentation pages and PDFs all work — mix them freely. Each link is detected and handled automatically.

## `quality` (type: `string`):

Higher quality is more accurate on difficult audio, accents and background noise, but takes longer per minute of media. Only applies when the transcript has to be generated from the audio.

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

Two-letter language code (en, es, de, ar…). Leave empty to detect the language automatically and to take whichever published transcript the publisher offers.

## `usePublishedCaptions` (type: `boolean`):

When the publisher already provides a transcript, use it: it is near-instant and much cheaper than generating one. Turn this off to always generate a fresh transcript from the audio.

## `maxMinutesPerItem` (type: `integer`):

Stop after this many minutes of each item. 0 = transcribe the whole thing. Use this to keep long recordings predictable — work stops at the limit, it is not just trimmed afterwards.

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

How many of the latest episodes to take from each podcast feed.

## `outputTranscripts` (type: `boolean`):

Push one row per item holding the complete transcript text.

## `outputSegments` (type: `boolean`):

Push one row per timestamped line, ready for subtitles, search and clip-finding. Turn off if you only need the full text.

## `linkHandling` (type: `string`):

How to read each link. Auto detects the right mode per link and suits almost every run. Force article text when a page holds both a video and an article you want, and force transcription for a player page that is misread as an article.

## `maxCharactersPerItem` (type: `integer`):

Cap the text taken from each article or PDF. 0 = no limit.

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

Proxy used for the requests. The default suits most runs. Switch to Residential when an item has no published transcript and has to be transcribed from its audio — some sources refuse datacenter traffic for the media itself.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://en.wikipedia.org/wiki/Large_language_model"
  ],
  "quality": "balanced",
  "language": "",
  "usePublishedCaptions": true,
  "maxMinutesPerItem": 0,
  "maxEpisodesPerFeed": 5,
  "outputTranscripts": true,
  "outputSegments": true,
  "linkHandling": "auto",
  "maxCharactersPerItem": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `dataset` (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 = {
    "urls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://en.wikipedia.org/wiki/Large_language_model"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("maged120/universal-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 = {
    "urls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://en.wikipedia.org/wiki/Large_language_model",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("maged120/universal-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 '{
  "urls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://en.wikipedia.org/wiki/Large_language_model"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call maged120/universal-transcriber --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,maged120/universal-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/LZEqlqfhDM19ibmeR/builds/jdo4Q2geQm4FcjmHl/openapi.json
