# YouTube Data API: video stats for videos, channels, playlists (`steadydata/youtube-data-api`) Actor

Official YouTube Data API v3 with your own free key: full video records for video links, whole channels, playlists and search terms. Views, likes, comments, duration, tags, category, captions, licence, channel subscribers. No proxies, no blocking, 10,000 free quota units a day. Pay per video.

- **URL**: https://apify.com/steadydata/youtube-data-api.md
- **Developed by:** [Steadydata Team](https://apify.com/steadydata) (community)
- **Categories:** Videos, Social media, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.65 / 1,000 video listeds

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

## YouTube Data API: video stats for videos, channels, playlists

Official YouTube Data API v3 with your own free key: full video records for video links, whole channels, playlists and search terms. Views, likes, comments, duration, tags, category, captions, licence, channel subscribers. No proxies, no blocking, 10,000 free quota units a day. Pay per video.

### Why this scraper

- **Only delivered results are charged.** Inputs that fail come back as clear error
  records at no cost.
- The official API instead of the site: Google's own numbers, no proxies, no consent screens, nothing that breaks when YouTube changes its pages. You bring a free key from your own Google project; the actor does the batching, paging and joining. Measured on the platform: one video, a channel's 25 latest uploads, 25 search results and a playlist, 53 rows, in 5 seconds for a sixth of a cent.
- One row per video with everything videos.list carries: title, channel, published time, duration in seconds, views, likes, comments, description, tags, category, captions, definition, licensed content, live status, language, privacy status, made-for-kids flag and the largest thumbnail, plus the channel's subscriber and video counts joined in from channels.list.
- Four kinds of input in one list: a video link or id, a channel (@handle, id or link) for its latest uploads, a playlist link, or a search term with its own ranking (relevance, date, views, rating). Each row says which input it came from.
- Quota-aware: videos are fetched 50 per call (1 unit), channels 50 per call, so a run of 500 videos costs about 30 units of your 10,000; a search costs 100 units, which the actor tells you before you run it.

### Who this is for

Put links, handles, playlists or search terms in `inputs` (up to 200 per run), paste your key in `apiKey` (it is stored as a secret and never appears in the log or the dataset), and set `maxVideosPerInput` (default 50, up to 500) and `searchOrder`. Built for creators and agencies tracking their own and competing channels, marketing teams measuring campaign videos, researchers collecting video metadata at scale, and anyone who needs YouTube numbers that come straight from Google and hold up in a report.

### Who this is not for

You need a Google Cloud project with the YouTube Data API v3 enabled and an API key; that takes five minutes and costs nothing, but without it the actor returns a free `INVALID_KEY` row. The default quota of 10,000 units a day allows about 100 searches or several thousand videos; when it is used up, rows come back as free `QUOTA_EXCEEDED` errors until midnight Pacific time. Comments, transcripts and chapters are not in this actor (see the related actors), and private videos are not returned by the API. Subscriber counts are rounded as YouTube publishes them.

### Input fields

| Field | Type | Required or default | What it does |
|---|---|---|---|
| `inputs` | list of text | required | One per row, up to 200: a video link or id, a channel link or @handle (its latest uploads), a playlist link, or a search term. |
| `apiKey` | text | required | A key from console.cloud.google.com with YouTube Data API v3 enabled. Free: 10,000 quota units a day (a video costs 1 unit per 50, a search 100). |
| `maxVideosPerInput` | number | 50 | Cost ceiling per input, newest first for channels and playlists. A single video link is always one row. |
| `searchOrder` | text (relevance, date, viewCount, rating) | relevance | How search terms are ranked: relevance, date, viewCount or rating. |

### Input example

```json
{
    "inputs": [
        "https://www.youtube.com/watch?v=ifI_fwg55k8",
        "@mkbhd",
        "retro tech flying cars"
    ],
    "apiKey": "YOUR_GOOGLE_API_KEY",
    "maxVideosPerInput": 50,
    "searchOrder": "relevance"
}
```

### Output example

| Field | Type | What it holds |
|---|---|---|
| `videoId` | text | The eleven-character YouTube id, the key to the video. |
| `title` | text | The video title as the uploader set it. |
| `channelTitle` | text | The channel name that published the video. |
| `channelId` | text | The channel id (UC...), stable where the name can change. |
| `publishedAt` | text | When the video was published, in UTC. |
| `durationSeconds` | number | The length in seconds, converted from the API's ISO 8601 duration. |
| `viewCount` | number | Views as YouTube reports them at the time of the run. |
| `likeCount` | number | Likes; empty when the uploader hides them. |
| `commentCount` | number | Comments; empty when comments are off or hidden. |
| `description` | text | The full description text. |
| `tags` | list | The uploader's tags; most videos have none or a few. |
| `categoryId` | text | YouTube's category number (10 is Music, 28 is Science & Technology). |
| `hasCaptions` | true/false | Whether the video has captions (uploaded or automatic). |
| `definition` | text | hd or sd, the best resolution class the video was uploaded in. |
| `isLicensedContent` | true/false | Whether YouTube marks the content as licensed to a partner. |
| `liveBroadcast` | text | Empty for a normal video; live or upcoming for a stream, as YouTube labels it. |
| `defaultLanguage` | text | The audio or metadata language the uploader set, when any. |
| `privacyStatus` | text | public or unlisted; private videos are not returned by the API. |
| `madeForKids` | true/false | Whether the uploader marked the video as made for kids. |
| `thumbnailUrl` | text | The largest thumbnail YouTube offers for the video. |
| `channelSubscriberCount` | number | The channel's subscribers, rounded as YouTube publishes them; empty when hidden. |
| `channelVideoCount` | number | How many public videos the channel has. |
| `url` | text | The watch page of the video. |
| `source` | text | How the row was found: video, channel, playlist or search. |
| `sourceInput` | text | The input row this video came from, so rows from several inputs can be told apart. |

Error codes: `INVALID_INPUT`, `INVALID_KEY`, `QUOTA_EXCEEDED`, `NOT_FOUND`, `BLOCKED`.

One delivered row looks like this (description shortened):

```json
{
  "videoId": "ifI_fwg55k8",
  "title": "Retro Tech: Flying Cars",
  "channelTitle": "Marques Brownlee",
  "channelId": "UCBJycsmduvYEL83R_U4JriQ",
  "publishedAt": "2021-04-13T13:00:30Z",
  "durationSeconds": 1163,
  "viewCount": 2603371,
  "likeCount": 95511,
  "commentCount": 5152,
  "description": "No matter what year, the idea of flying vehicles helps cement our sense of when the future begins. I'm taking a look at everything flying, from cars to skateboards, and seeing where the tech currently stands.",
  "tags": [],
  "categoryId": "28",
  "hasCaptions": true,
  "definition": "hd",
  "isLicensedContent": true,
  "liveBroadcast": null,
  "defaultLanguage": "en",
  "privacyStatus": "public",
  "madeForKids": false,
  "thumbnailUrl": "https://i.ytimg.com/vi/ifI_fwg55k8/maxresdefault.jpg",
  "channelSubscriberCount": 21300000,
  "channelVideoCount": 1853,
  "url": "https://www.youtube.com/watch?v=ifI_fwg55k8",
  "source": "video",
  "sourceInput": "https://www.youtube.com/watch?v=ifI_fwg55k8",
  "status": "ok"
}
```

### Related actors from steadydata

- [youtube-video-details](https://apify.com/steadydata/youtube-video-details): the same video facts without a Google key, over the site itself
- [youtube-channel-videos](https://apify.com/steadydata/youtube-channel-videos): a channel's uploads without a key
- [youtube-video-chapters](https://apify.com/steadydata/youtube-video-chapters): the chapter list of a video

### Pricing

Pay per event: one `video-listed` event per delivered result. No charge for inputs
that fail, no separate platform-usage surcharge.

**Free Apify plan:** this actor delivers up to 25 rows per run for accounts on the Apify free
plan, and then stops with a message. That limit is set by us, not by Apify. It exists so the
actor keeps paying for itself for the people who do pay. Any paid Apify plan runs it at full
size, billed per delivered row, with failed rows never charged.

**Reviews:** if this actor saves you time, a short review on this page is the one thing that
helps most. Ratings are what other buyers look at first, and we have no other way to ask.

### FAQ

**Is personal data collected?**
No. Rows carry public videos and the channel that published them; comments and the people who wrote them are not read, and your API key is stored as a secret.

**How do I get a key?**
In console.cloud.google.com create a project (or use one you have), open APIs & Services, Library, enable "YouTube Data API v3", then Credentials, Create credentials, API key. Restricting the key to the YouTube Data API is a good idea. Google charges nothing for this API; it only has a daily quota.

**How much quota does a run use?**
About 1 unit per 50 videos for the video lookup, 1 per 50 channels for subscriber counts, 1 per 50 playlist items, and 100 per page of 50 search results. A channel of 500 videos costs around 25 units; 50 searches cost 5,000. The default quota is 10,000 a day.

**Does a channel input give every video ever?**
It gives the latest uploads, newest first, up to `maxVideosPerInput` (500 at most per run). For a complete archive of a large channel, run again with a higher ceiling or use the channel's uploads playlist over several runs.

**Why is likeCount empty on some rows?**
Because the uploader hid the like count; YouTube then omits it from the API. Comments are empty the same way when they are off.

**What does a run cost when an input fails?**
Nothing. `NOT_FOUND`, `INVALID_INPUT`, `INVALID_KEY`, `QUOTA_EXCEEDED` and `BLOCKED` rows are free; only delivered videos are charged.

**What happens when the source changes?**
Sources change from time to time; that is the nature of this work. The actor is
monitored daily and fixed fast, and while it is broken you are not charged, because
only delivered results cost anything.

# Changelog

This Actor's version history is a separate document: https://apify.com/steadydata/youtube-data-api/changelog.md

# Actor input Schema

## `inputs` (type: `array`):

One per row, up to 200: a video link or id, a channel link or @handle (its latest uploads), a playlist link, or a search term.

## `apiKey` (type: `string`):

A key from console.cloud.google.com with YouTube Data API v3 enabled. Free: 10,000 quota units a day (a video costs 1 unit per 50, a search 100).

## `maxVideosPerInput` (type: `integer`):

Cost ceiling per input, newest first for channels and playlists. A single video link is always one row.

## `searchOrder` (type: `string`):

How search terms are ranked: relevance, date, viewCount or rating.

## Actor input object example

```json
{
  "inputs": [
    "https://www.youtube.com/watch?v=ifI_fwg55k8",
    "@mkbhd",
    "retro tech flying cars"
  ],
  "maxVideosPerInput": 50,
  "searchOrder": "relevance"
}
```

# Actor output Schema

## `results` (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 = {
    "inputs": [
        "https://www.youtube.com/watch?v=ifI_fwg55k8",
        "@mkbhd",
        "retro tech flying cars"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("steadydata/youtube-data-api").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 = { "inputs": [
        "https://www.youtube.com/watch?v=ifI_fwg55k8",
        "@mkbhd",
        "retro tech flying cars",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("steadydata/youtube-data-api").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 '{
  "inputs": [
    "https://www.youtube.com/watch?v=ifI_fwg55k8",
    "@mkbhd",
    "retro tech flying cars"
  ]
}' |
apify call steadydata/youtube-data-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,steadydata/youtube-data-api"
        }
    }
}
```

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/hEEDdS2wlh4x2HV7r/builds/zM3BhPuwhFsEbn0vg/openapi.json
