# Video & Audio Transcriber (`lightmoon/video-audio-transcriber`) Actor

Transcribe YouTube videos, audio and video files and podcast episodes to text with timestamps, SRT and VTT. One row per file: transcript, language, duration, what it cost. Charged per audio minute; YouTube captions cheaper; refused files and silent audio free. No API key, no account.

- **URL**: https://apify.com/lightmoon/video-audio-transcriber.md
- **Developed by:** [Stable](https://apify.com/lightmoon) (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 $45.00 / 1,000 audio minute transcribeds

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

## Video & Audio Transcriber

Paste YouTube links, audio or video files, or a podcast feed. Get one row per
file with the full transcript, timestamped segments, detected language and
duration — and SRT/VTT if you want subtitles. Charged **per audio minute**;
a file that could not be fetched costs nothing.

> **Unofficial.** This Actor is not affiliated with, endorsed by or sponsored by YouTube or Google. It reads only publicly available media, does not log in and does not bypass any access control. All product names and trademarks belong to their respective owners.

### One row, from a real run

The first video ever uploaded to YouTube, whose channel published captions —
so this row took the caption path: exact words, two seconds, the cheaper rate.

```json
{
  "status": "ok",
  "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "sourceType": "youtube",
  "title": "Me at the zoo",
  "author": "jawed",
  "published": "2005-04-24",
  "durationSeconds": 19,
  "minutesCharged": 1,
  "truncated": false,
  "language": "en",
  "method": "youtube-captions-manual",
  "model": null,
  "text": "All right, so here we are, in front of the elephants the cool thing about these guys is that they have really... really really long trunks and that's cool (baaaaaaaaaaahhh!!) and that's pretty much all there is to say",
  "wordCount": 39,
  "segmentCount": 6,
  "segments": [
    { "start": 1.2, "end": 3.36, "text": "All right, so here we are, in front of the elephants" },
    { "start": 5.32, "end": 7.97, "text": "the cool thing about these guys is that they have really..." }
  ],
  "fetchSeconds": 1.96,
  "decodeSeconds": 0,
  "transcribeSeconds": 0,
  "videoId": "jNQXAC9IVRw"
}
```

A podcast episode from the same run, through the speech model: 120 seconds
of audio (cut at a 2-minute test ceiling), 351 words, `method: whisper-base`,
`decodeSeconds: 6.8`, `transcribeSeconds: 38.2`, `minutesCharged: 2`,
`truncated: true`.

### What you can give it

| input | what happens |
|---|---|
| YouTube video link (`watch?v=`, `youtu.be`, Shorts) | audio is fetched and transcribed. If the channel published captions, they are used instead — exact wording, instant, cheaper (see *YouTube captions*) |
| direct link to a media file — mp3, m4a, mp4, wav, webm, mov, ogg, flac, on any host | downloaded and transcribed |
| podcast feed (RSS or Atom) | the newest episodes are transcribed, up to *Episodes per feed* |
| a web page, a channel page, a playlist | refused, free, with the reason in the row |

Files up to 1 GB. Per-file ceiling in minutes is yours to set (default 180);
anything past it is cut, the row says `truncated: true`, and only the minutes
transcribed are charged.

### Three things worth knowing

- **Captions when they exist.** A YouTube video whose channel uploaded captions
  returns those — the exact words, in about a second, at the lower *caption
  minute* rate. The `method` column says which path a row took:
  `whisper-base` or `youtube-captions-manual`. Set *YouTube captions* to *Never*
  if you always want the model's transcript.
- **Every row explains its cost.** `minutesCharged` is exactly what was billed,
  rounded up to the started minute. `fetchSeconds`, `decodeSeconds` and
  `transcribeSeconds` show where the time went.
- **No speech, no charge.** A file that turns out to hold music or silence
  comes back with an empty transcript, `minutesCharged: 0`, and a note saying
  so. You pay for words, not for minutes of nothing.
- **Refused is a row, not a mystery.** A dead link, a page instead of a file, a
  feed with no episodes — each comes back as `status: refused` with the reason,
  never charged, so a batch of fifty tells you which three to fix.

### Models

| model | when | speed | accuracy |
|---|---|---|---|
| `base` (default) | clear speech, most languages | ~5× realtime | good |
| `small` | accents, noise, technical vocabulary | ~2× realtime | best |
| `tiny` | drafts, clear English, long batches | ~9× realtime | rough |

All three are built into the Actor; nothing downloads at run time. Languages:
detection is automatic; set one when you know it. *Translate to English*
returns English text from any language at the same price.

### Output

`text` is the whole transcript; `segments` is a list of `{start, end, text}`
in seconds. With *Include SRT and VTT* on, each row carries the subtitle text
in `srt` and `vtt` **and** links to the same as downloadable files
(`srtFileUrl`, `vttFileUrl`) in the run's storage. Export the table as JSON,
CSV or Excel from the run's dataset; for CSV, switch *Include timestamped
segments* off to keep one cell per row.

### Price

Charged per **audio minute transcribed**, rounded up to the started minute,
minimum one minute per file. Caption minutes (YouTube captions used instead of
the model) are charged at a lower rate. Nothing else: no per-file fee, no
charge for refused items, no charge for minutes past your ceiling.

### Limits, stated plainly

- Up to 200 items per run and 600 minutes per item; defaults are 50 and 180.
- One file at a time. A 60-minute file on `base` takes about 12 minutes of run
  time; on `small` about 30. Long batches are fine, just slow — schedule them.
- Live streams are refused until they have ended.
- YouTube channel pages and playlists are not expanded — paste the videos, or
  use a channel-videos scraper to list them first.
- Age-restricted and private videos cannot be fetched.
- The free plan runs this Actor like any other; a run stops cleanly when your
  usage limit is reached, and the rows already stored stay.

### Typical uses

- Turn a podcast feed into searchable text every week — schedule it with the
  feed URL and *Episodes per feed* = 3.
- Subtitle a batch of product videos: switch *Include SRT and VTT* on.
- Build a corpus from a list of talks for analysis or an LLM: plain `text`
  column, segments off.
- Translate foreign-language interviews to English in one pass.

# Actor input Schema

## `mediaUrls` (type: `array`):

One per line. A YouTube video link (`youtube.com/watch?v=…`, `youtu.be/…`, Shorts), or a direct link to an audio or video file (mp3, m4a, mp4, wav, webm, mov, ogg, flac…) on any host — a podcast episode's file, a Dropbox or S3 link, a recording on your own site. A link that opens a web page instead of a file is refused for free. Channel and playlist links are not accepted: paste the videos.

## `feedUrls` (type: `array`):

RSS or Atom feed addresses, one per line. The newest episodes of each feed are transcribed, as many as *Episodes per feed* allows. A feed we cannot read is refused for free.

## `episodesPerFeed` (type: `integer`):

Newest first. Each episode counts against *Maximum items* and is charged like any other file.

## `model` (type: `string`):

`base` is the default: accurate on clear speech in most languages. `small` is the one for accents, noise and technical vocabulary — about 3× slower per minute. `tiny` is fastest and draft quality. All three are built into the Actor; nothing downloads at run time.

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

Leave on *Detect* to let the model decide from the first 30 seconds. Set it when you know the language — detection costs a moment and can be wrong on music intros.

## `translateToEnglish` (type: `boolean`):

Transcribe any language and return English text instead of the original. Same price.

## `youtubeCaptions` (type: `string`):

When a YouTube video already carries captions, use them instead of running the model: exact wording, instant, and charged at the lower *caption minute* rate. *Published by the channel* uses only captions a human uploaded. *Any* also accepts YouTube's automatic captions. *Never* always runs the model.

## `includeSegments` (type: `boolean`):

Adds a `segments` list to each row: start, end and text for every phrase. Switch off for a lighter table with the plain text only.

## `includeSubtitles` (type: `boolean`):

Adds ready-made `srt` and `vtt` subtitle text to each row.

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

A file longer than this is transcribed up to the limit, marked `truncated`, and charged only for the minutes transcribed. A guard against a three-hour stream you did not mean to pay for.

## `maxItems` (type: `integer`):

Files and episodes in total, in the order given. The run stops here even if the list is longer.

## `includeFailedItems` (type: `boolean`):

A link that could not be fetched or read comes back as a row with `status: refused` and the reason, so you see what did not work. Such rows are never charged.

## Actor input object example

```json
{
  "mediaUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://archive.org/download/art_of_war_librivox/art_of_war_01-02_sun_tzu_64kb.mp3"
  ],
  "feedUrls": [],
  "episodesPerFeed": 5,
  "model": "base",
  "language": "auto",
  "translateToEnglish": false,
  "youtubeCaptions": "manual",
  "includeSegments": true,
  "includeSubtitles": false,
  "maxMinutesPerItem": 180,
  "maxItems": 50,
  "includeFailedItems": true
}
```

# Actor output Schema

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

No description

## `timing` (type: `string`):

No description

## `all` (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 = {
    "mediaUrls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://archive.org/download/art_of_war_librivox/art_of_war_01-02_sun_tzu_64kb.mp3"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("lightmoon/video-audio-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 = { "mediaUrls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://archive.org/download/art_of_war_librivox/art_of_war_01-02_sun_tzu_64kb.mp3",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("lightmoon/video-audio-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 '{
  "mediaUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://archive.org/download/art_of_war_librivox/art_of_war_01-02_sun_tzu_64kb.mp3"
  ]
}' |
apify call lightmoon/video-audio-transcriber --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,lightmoon/video-audio-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/54N88vaqWjzwh9Gl0/builds/03Heewmyt1QvXiqPs/openapi.json
