# TVmaze Scraper · TV Shows, Episodes, Casts & Networks (`reapx/tvmaze-scraper`) Actor

Scrape TV shows, episode details, cast members, ratings, genres, and network broadcast data from TVmaze's public database. Pay-per-event pricing per show record.

- **URL**: https://apify.com/reapx/tvmaze-scraper.md
- **Developed by:** [Tarek Etman](https://apify.com/reapx) (community)
- **Categories:** Videos, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.30 / 1,000 show record returneds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

![reapX — public sources in, addressable records out](https://reapx.dev/reapx.gif)

## TVmaze Scraper · TV Shows, Episodes, Casts & Networks

Scrape TV shows, episode listings, cast lists, television networks, streaming channels, user ratings, genres, and broadcast schedules from TVmaze's public REST database. Extract clean, structured JSON dataset records for television series analytics, streaming market research, content discovery, media monitoring, and metadata integration.

> Maintained by **reapX**. Every row cites the Apify run that produced it — nothing is inferred, modelled or filled in, and a field absent from the source is absent from the row. The extracted archive for this source is browsable at [reapx.dev/data/tvmaze-scraper/](https://reapx.dev/data/tvmaze-scraper/) and mirrored as an open dataset on [Hugging Face](https://huggingface.co/datasets/reapxdev/tvmaze-scraper) and [Kaggle](https://www.kaggle.com/datasets/reapxdev/tvmaze-scraper). Questions: reapxdev@proton.me

***

### Features

- **Comprehensive Show Metadata**: Scrape full show titles, slugs, production status, genres, episode runtimes, broadcast languages, official websites, and summary descriptions.
- **Network & Web Channels**: Extract broadcast television networks (CBS, NBC, HBO, BBC, FOX, AMC, ABC, Showtime, FX, CW) and web streaming channels (Netflix, Amazon Prime Video, Hulu, Apple TV+, Disney+).
- **Ratings & Popularity**: Capture average user rating scores out of 10, vote weights, and popularity metrics across thousands of global television series.
- **External Identifiers**: Retrieve linked IMDb IDs (`imdbId`), TheTVDB IDs (`thetvdbId`), and TVRage IDs (`tvrageId`) for seamless cross-platform dataset joining.
- **Flexible Search & Filtering**: Filter television series by search query terms, primary genres, network names, production status, origin countries, or minimum user rating thresholds.
- **Pay-Per-Event Billing**: Pay only for complete show records successfully returned and pushed to your default dataset. Failed or empty requests incur zero charge.

***

### ⬇️ Input

The **TVmaze Scraper** accepts flexible JSON input parameters to control data scope, search keywords, network filters, and show extraction limits.

| Parameter | Type | Default | Description |
| :--- | :--- | :--- | :--- |
| `searchQuery` | String | `""` | Search query string to match against show names and titles (e.g. `drama`, `star`, `game`). Leave empty for main index pagination. |
| `genre` | String | `""` | Filter shows by primary genre tag (e.g. `Drama`, `Comedy`, `Science-Fiction`, `Thriller`, `Action`, `Crime`, `Anime`). |
| `network` | String | `""` | Filter by broadcasting network or streaming web channel name (e.g. `CBS`, `HBO`, `Netflix`, `BBC One`, `NBC`). |
| `country` | String | `""` | Filter shows by network country of origin (e.g. `United States`, `United Kingdom`, `Japan`, `Canada`, `France`). |
| `status` | String | `""` | Filter shows by current production status (`Running`, `Ended`, `To Be Determined`, `In Development`). |
| `minRating` | Number | `null` | Minimum average user rating score from 0.0 to 10.0 (e.g. `7.5`, `8.0`, `8.5`). |
| `startPage` | Integer | `0` | Starting page index for sequential database pagination (0 to 1000). |
| `maxShows` | Integer | `50` | Maximum number of TV show records to extract and output (1 to 5000). |

#### Input JSON Configurations

##### Example 1: Extracting Popular Sci-Fi Series from Netflix

```json
{
  "network": "Netflix",
  "genre": "Science-Fiction",
  "minRating": 7.5,
  "maxShows": 50
}
```

##### Example 2: Searching TV Shows by Keyword

```json
{
  "searchQuery": "star",
  "minRating": 7.0,
  "maxShows": 25
}
```

##### Example 3: Sequential Pagination of Active Drama Series

```json
{
  "genre": "Drama",
  "status": "Running",
  "startPage": 0,
  "maxShows": 100
}
```

***

### ⬆️ Output

All extracted television show records are pushed incrementally into the run's default dataset. Output data is formatted in standard camelCase JSON objects.

#### Dataset Schema Fields

| Field Name | Type | Description | Example |
| :--- | :--- | :--- | :--- |
| `slug` | String | URL slug identifier of the show on TVmaze | `"under-the-dome"` |
| `showId` | Integer | Numeric TVmaze ID of the show | `1` |
| `name` | String | Official title name of the TV show | `"Under the Dome"` |
| `type` | String | Type of TV show (Scripted, Reality, Animation) | `"Scripted"` |
| `language` | String | Primary language spoken in the show | `"English"` |
| `genres` | Array | List of genre tags assigned to the show | `["Drama", "Science-Fiction", "Thriller"]` |
| `status` | String | Current production status of the series | `"Ended"` |
| `runtime` | Integer | Standard episode runtime duration in minutes | `60` |
| `averageRuntime` | Integer | Average runtime across all episodes | `60` |
| `premiered` | String | Date the series first premiered (YYYY-MM-DD) | `"2013-06-24"` |
| `ended` | String | Date the series ended or concluded (YYYY-MM-DD) | `"2015-09-10"` |
| `officialSite` | String | Official website URL of the show | `"http://www.cbs.com/shows/under-the-dome/"` |
| `scheduleTime` | String | Time of day the show airs | `"22:00"` |
| `scheduleDays` | Array | Days of the week the show airs | `["Thursday"]` |
| `ratingAverage` | Number | Average TVmaze user rating score out of 10 | `6.6` |
| `weight` | Integer | TVmaze popularity weight score | `99` |
| `networkName` | String | Name of the broadcasting network | `"CBS"` |
| `networkCountry` | String | Country name of the broadcasting network | `"United States"` |
| `networkCode` | String | 2-letter ISO country code of the network | `"US"` |
| `networkTimezone` | String | Timezone of the primary network broadcast | `"America/New_York"` |
| `webChannelName` | String | Name of the streaming service or web channel | `"Netflix"` |
| `imdbId` | String | IMDb identifier string | `"tt1553656"` |
| `thetvdbId` | Integer | TheTVDB numeric show identifier | `264492` |
| `tvrageId` | Integer | TVRage numeric show identifier | `25988` |
| `imageMedium` | String | URL of medium-sized poster image | `"https://static.tvmaze.com/uploads/images/medium_portrait/610/1525272.jpg"` |
| `imageOriginal` | String | URL of original untouched poster image | `"https://static.tvmaze.com/uploads/images/original_untouched/610/1525272.jpg"` |
| `summaryText` | String | Plain text summary description of the show | `"Under the Dome is the story of a small town..."` |
| `summaryHtml` | String | Raw HTML summary string from TVmaze | `"<p><b>Under the Dome</b> is the story...</p>"` |
| `updatedAt` | Integer | Unix timestamp of when show record was updated | `1769177765` |
| `url` | String | Canonical URL of the show on TVmaze | `"https://www.tvmaze.com/shows/1/under-the-dome"` |

#### Sample Output Record

```json
{
  "slug": "under-the-dome",
  "showId": 1,
  "name": "Under the Dome",
  "type": "Scripted",
  "language": "English",
  "genres": [
    "Drama",
    "Science-Fiction",
    "Thriller"
  ],
  "status": "Ended",
  "runtime": 60,
  "averageRuntime": 60,
  "premiered": "2013-06-24",
  "ended": "2015-09-10",
  "officialSite": "http://www.cbs.com/shows/under-the-dome/",
  "scheduleTime": "22:00",
  "scheduleDays": [
    "Thursday"
  ],
  "ratingAverage": 6.6,
  "weight": 99,
  "networkName": "CBS",
  "networkCountry": "United States",
  "networkCode": "US",
  "networkTimezone": "America/New_York",
  "webChannelName": null,
  "imdbId": "tt1553656",
  "thetvdbId": 264492,
  "tvrageId": 25988,
  "imageMedium": "https://static.tvmaze.com/uploads/images/medium_portrait/610/1525272.jpg",
  "imageOriginal": "https://static.tvmaze.com/uploads/images/original_untouched/610/1525272.jpg",
  "summaryText": "Under the Dome is the story of a small town that is suddenly and inexplicably sealed off from the rest of the world by an enormous transparent dome.",
  "summaryHtml": "<p><b>Under the Dome</b> is the story of a small town that is suddenly and inexplicably sealed off from the rest of the world by an enormous transparent dome.</p>",
  "updatedAt": 1769177765,
  "url": "https://www.tvmaze.com/shows/1/under-the-dome"
}
```

***

### How it works

The **TVmaze Scraper** operates by interfacing directly with the public TVmaze REST API (`api.tvmaze.com`).

1. **Input Normalization**: The scraper parses specified input configuration parameters including search terms, network names, genre filters, and rating boundaries.
2. **Endpoint Selection**:
   - If `searchQuery` is provided, the actor calls `https://api.tvmaze.com/search/shows?q={searchQuery}` to fetch query matches.
   - If `searchQuery` is omitted, the actor queries sequential index pages via `https://api.tvmaze.com/shows?page={startPage}`.
3. **Filtering & Transformation**: Each returned show record is normalized into clean camelCase fields. `slug` is extracted directly from the show URL path to ensure stable entity-page resolution.
4. **Pay-Per-Event Charging**: For each validated show record written to the dataset, the actor triggers a `show-returned` pay-per-event charge. Failed requests or empty filter responses incur zero cost.
5. **Incremental Dataset Storage**: Records are pushed immediately to the run's default dataset (`Actor.push_data`) as they are parsed, avoiding memory buffering issues.

***

### Pricing & Pay-Per-Event

This actor uses **Pay-Per-Event (PPE)** pricing. You pay only for successfully extracted show records written to your default dataset.

- **Primary Event**: `show-returned` ($0.002 USD per show record)
- **Tiered Volume Discounts**:
  - FREE Tier: $0.0020 / record
  - BRONZE Tier: $0.0018 / record
  - SILVER Tier: $0.0015 / record
  - GOLD Tier: $0.0013 / record
  - PLATINUM Tier: $0.0010 / record
  - DIAMOND Tier: $0.0008 / record

Platform execution compute costs are absorbed into event pricing. Empty search results or HTTP error retries are never charged.

***

### Data Quality & Entity Page Integrity

Every record emitted by this scraper is keyed by `slug`, derived directly from the canonical TVmaze URL. This ensures that:

- Every show row has a unique, deterministic, non-constant entity key.
- Downstream entity page generators (`growth/entity_pages.py`) can resolve and index distinct show data pages cleanly.
- `showId` is preserved as a numeric attribute without replacing `slug` as the primary addressable key.

***

### Use Cases

- **Entertainment Market Intelligence**: Analyze television production trends across networks, streaming providers, and international markets.
- **Media Catalog Enrichment**: Cross-reference movie and TV show databases using IMDb, TheTVDB, and TVRage identifiers.
- **Streaming & Broadcast Monitoring**: Track currently active television series schedules, runtime durations, and broadcast days.
- **Content Recommendation Engines**: Build content similarity systems based on user rating averages, genre combinations, and plot summaries.

***

### ❓ FAQ

#### 1. What happens if a show has no network (e.g. web streaming series)?

Web-only series (such as Netflix, Hulu, or Amazon Prime Video originals) return `webChannelName` populated while `networkName` may be `null`. The scraper normalizes both fields so network filtering matches against either traditional broadcast networks or digital streaming platforms.

#### 2. How is the show `slug` generated?

The `slug` field is extracted directly from the show's canonical TVmaze URL (e.g. `https://www.tvmaze.com/shows/1/under-the-dome` -> `under-the-dome`). This ensures that every row carries a unique, human-readable, and stable entity identifier.

#### 3. Can I filter shows by rating?

Yes! Set `minRating` to a number between 0.0 and 10.0 (for example `8.0`). Shows with average user ratings below this threshold will be excluded client-side before dataset insertion.

#### 4. What is the maximum number of shows I can scrape in a single run?

You can extract up to 5,000 shows per run by setting `maxShows`. For larger batch extractions, you can chain multiple runs using the `startPage` pagination index parameter.

#### 5. How are external database IDs stored?

External IDs (`imdbId`, `thetvdbId`, `tvrageId`) are parsed directly from TVmaze's `externals` object and exported as top-level dataset fields for easy joining with external datasets.

#### 6. Are there rate limits on the TVmaze API?

TVmaze allows reasonable public API access. The scraper includes automatic exponential backoff on HTTP 429 and 5xx status codes to maintain respectful crawling speeds and ensure high run reliability.

***

### 💬 Your feedback

We actively maintain our scrapers and welcome user feature requests, bug reports, and dataset suggestions.

- **Issue Reporting & Support**: Contact us directly at <reapxdev@proton.me>
- **Data Archive & Datasets**: Access pre-built archive dumps at [reapx.dev/data/tvmaze-scraper/](https://reapx.dev/data/tvmaze-scraper/)
- **Hugging Face Datasets**: [huggingface.co/datasets/reapxdev/tvmaze-scraper](https://huggingface.co/datasets/reapxdev/tvmaze-scraper)
- **Kaggle Datasets**: [kaggle.com/datasets/reapxdev/tvmaze-scraper](https://www.kaggle.com/datasets/reapxdev/tvmaze-scraper)

***

Unofficial - not affiliated with TVmaze. Collects public data only. reapx. Contact reapxdev@proton.me.

### 🧪 Example input

A real, runnable configuration — this is an actual input this Actor has run with.

```json
{
  "genre": "Drama",
  "maxShows": 50
}
```

### 📄 Sample output

One real row from a real run of this Actor, unedited.

```json
{
  "slug": "under-the-dome",
  "showId": 1,
  "name": "Under the Dome",
  "type": "Scripted",
  "language": "English",
  "genres": [
    "Drama",
    "Science-Fiction",
    "Thriller"
  ],
  "status": "Ended",
  "runtime": 60,
  "averageRuntime": 60,
  "premiered": "2013-06-24",
  "ended": "2015-09-10",
  "officialSite": "http://www.cbs.com/shows/under-the-dome/",
  "scheduleTime": "22:00",
  "scheduleDays": [
    "Thursday"
  ],
  "ratingAverage": 6.6,
  "weight": 99,
  "networkName": "CBS",
  "networkCountry": "United States",
  "networkCode": "US",
  "networkTimezone": "America/New_York",
  "webChannelName": null,
  "imdbId": "tt1553656",
  "thetvdbId": 264492,
  "tvrageId": 25988,
  "imageMedium": "https://static.tvmaze.com/uploads/images/medium_portrait/610/1525272.jpg",
  "imageOriginal": "https://static.tvmaze.com/uploads/images/original_untouched/610/1525272.jpg",
  "summaryText": "Under the Dome is the story of a small town that is suddenly and inexplicably sealed off from the rest of the world by an enormous transparent dome. The town's inhabitants must deal with surviving the post-apocalyptic conditions while searching for answers about the dome, where it came from and if and when it will go away.",
  "summaryHtml": "<p><b>Under the Dome</b> is the story of a small town that is suddenly and inexplicably sealed off from the rest of the world by an enormous transparent dome. The town's inhabitants must deal with surviving the post-apocalyptic conditions while searching for answers about the dome, where it came from and if and when it will go away.</p>",
  "updatedAt": 1769177765,
  "url": "https://www.tvmaze.com/shows/1/under-the-dome"
}
```

### ⚠️ Run outcomes and error handling

This Actor reports what happened in the run's **status message**, and it always keeps whatever it collected. These are the outcomes you can get and what each one means.

| Outcome | What it means |
|---|---|
| **Success** | Rows were returned and you were charged `show-returned` at $0.002 per row. |
| **No matches** | The source returned nothing for your filters. **Nothing is charged.** Widen the date window or drop a filter. |

#### What is guaranteed either way

- **Every row is pushed as it is built**, not buffered to the end of the run. Anything that buffers output loses everything to a timeout, a block or a migration; this does not.
- **A field absent from the source is absent from the row.** Nothing is inferred, modelled or filled in to make a row look complete.

# Actor input Schema

## `searchQuery` (type: `string`):

Search query string to search TV shows by title or keyword on TVmaze (e.g. <code>drama</code>, <code>star</code>, or <code>game</code>).<br><br>Leaving this field empty retrieves TV shows sequentially from the main database index. Specifying a search term targets specific show titles, narrowing the search scope and lowering overall execution cost.<br><br>Leave this field empty to skip this filter.

## `genre` (type: `string`):

Filter shows by primary genre tag (e.g. <code>Drama</code>, <code>Comedy</code>, <code>Action</code>, <code>Science-Fiction</code>, <code>Thriller</code>, or <code>Crime</code>).<br><br>Leave this field empty to include TV shows across all genres without filtering. Selecting a specific genre filters results client-side, reducing unnecessary dataset rows and lowering event charges.

## `network` (type: `string`):

Filter TV shows by broadcast network or web channel name (e.g. <code>CBS</code>, <code>HBO</code>, <code>Netflix</code>, <code>BBC One</code>, or <code>NBC</code>).<br><br>Leaving this field empty includes shows from all television networks and streaming providers worldwide. Entering a network name isolates specific network catalogs, speeding up processing time and controlling run cost.<br><br>Leave this field empty to skip this filter.

## `country` (type: `string`):

Filter TV shows by network or broadcast country of origin (e.g. <code>United States</code>, <code>United Kingdom</code>, <code>Japan</code>, <code>Canada</code>, or <code>France</code>).<br><br>Leave empty to scrape TV series produced in any country globally. Specifying a country name filters shows by network origin, focusing the output data and reducing compute time.

## `status` (type: `string`):

Filter shows by current production status (e.g. <code>Running</code>, <code>Ended</code>, <code>To Be Determined</code>, or <code>In Development</code>).<br><br>Leaving this field empty includes active, completed, and upcoming TV series. Selecting a status focuses the scraper on active or ended shows, preventing irrelevant records from incurring charges.<br><br>Leave this field empty to skip this filter.

## `minRating` (type: `number`):

Minimum TVmaze user rating threshold from 0.0 to 10.0 (e.g. <code>7.0</code> or <code>8.5</code>).<br><br>Leaving this field empty includes shows regardless of user rating score. Setting a higher minimum rating filters out low-rated or unrated titles, ensuring only high-quality show entries are saved.

## `startPage` (type: `integer`):

Starting page index for sequential database pagination (0 to 1000). Prefilled with 0.<br><br>Leaving this empty defaults to starting from page 0. Adjusting the starting page allows resuming pagination from a specific offset, helping scrape specific historical blocks without redundant requests.

## `maxShows` (type: `integer`):

Maximum number of TV show records to extract and save into the default dataset (1 to 5000). Prefilled with 50.<br><br>Higher limits result in longer execution times and higher total event charges. Setting a smaller limit caps the run cost and ensures execution completes within your budget.<br><br>The higher the number, the longer the run takes and the more you are charged, because you pay per row returned.

## Actor input object example

```json
{
  "maxShows": 50
}
```

# Actor output Schema

## `results` (type: `string`):

Every TV show record extracted by this run, one row per show, in the default dataset.

# 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 = {
    "startPage": 0,
    "maxShows": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("reapx/tvmaze-scraper").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 = {
    "startPage": 0,
    "maxShows": 50,
}

# Run the Actor and wait for it to finish
run = client.actor("reapx/tvmaze-scraper").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 '{
  "startPage": 0,
  "maxShows": 50
}' |
apify call reapx/tvmaze-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,reapx/tvmaze-scraper"
        }
    }
}

```

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/wgk9qnjlUtGTUcTbW/builds/c43BZ3ijVBj3JBbxO/openapi.json
