# Rotten Tomatoes Scraper - Movie & TV Ratings API (`dami_studio/rotten-tomatoes-scraper`) Actor

Look up movies and TV on Rotten Tomatoes by title or URL. Returns the Tomatometer and Popcornmeter with their review counts and average ratings, the critics consensus, cast, crew, genres, runtime, release dates, box office and poster.

- **URL**: https://apify.com/dami\_studio/rotten-tomatoes-scraper.md
- **Developed by:** [Dami's Studio](https://apify.com/dami_studio) (community)
- **Categories:** Videos, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.60 / 1,000 title returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Rotten Tomatoes Scraper

Looks up movies and TV on [Rotten Tomatoes](https://www.rottentomatoes.com) and hands back one row per title: the Tomatometer and the Popcornmeter, both with their review counts, average ratings and fresh/rotten splits, plus the critics consensus paragraph, cast and crew, genres, age rating, runtime, theatrical and streaming release dates, US box office, the poster and the Rotten Tomatoes ids.

Give it titles — `["dune part two", "severance"]` — and it searches the site the way its own search box does, then reads the page it lands on. Or paste URLs and it reads exactly those. No account, no API key, no cookies to supply.

### What a row looks like

A real row, read on 19 September 2026:

```json
{
  "url": "https://www.rottentomatoes.com/m/dune_part_two",
  "slug": "dune_part_two",
  "path": "/m/dune_part_two",
  "mediaType": "Movie",
  "isMovie": true,
  "title": "Dune: Part Two",
  "seriesTitle": null,
  "year": 2024,

  "tomatometer": 92,
  "tomatometerSentiment": "POSITIVE",
  "tomatometerCertifiedFresh": true,
  "criticsAverageRating": 8.4,
  "criticReviewCount": 466,
  "criticsFreshCount": 429,
  "criticsRottenCount": 37,
  "topCriticsScore": 87,
  "topCriticsReviewCount": 78,

  "audienceScore": 95,
  "audienceSentiment": "POSITIVE",
  "audienceAverageRating": 4.7,
  "audienceReviewCount": 2244,
  "audienceRatingCountText": "5,000+ Verified Ratings",
  "audienceLikedCount": 7468,
  "audienceNotLikedCount": 412,

  "criticsConsensus": "Visually thrilling and narratively epic, Dune: Part Two continues Denis Villeneuve's adaptation of the beloved sci-fi series in spectacular form.",
  "synopsis": "\"Dune: Part Two\" will explore the mythic journey of Paul Atreides as he unites with Chani and the Fremen while on a warpath of revenge against the conspirators who destroyed his family. ...",

  "genres": ["Sci-Fi", "Adventure", "Action", "Fantasy", "Drama"],
  "contentRating": "PG-13",
  "originalLanguage": "English",
  "runtime": "2h 46m",
  "runtimeMinutes": 166,

  "releaseDateTheaters": "Mar 1, 2024, Wide",
  "rereleaseDateTheaters": null,
  "releaseDateStreaming": "Apr 16, 2024",
  "airDate": null,
  "boxOfficeGrossUsa": "$282.0M",

  "directors": ["Denis Villeneuve"],
  "creators": [],
  "writers": ["Denis Villeneuve", "Jon Spaihts"],
  "producers": ["Mary Parent", "Cale Boyter", "Denis Villeneuve", "Tanya Lapointe", "Patrick McCormick"],
  "distributor": "Warner Bros. Pictures",
  "productionCompanies": ["Legendary Pictures"],
  "network": null,
  "soundMix": ["Dolby Atmos", "Dolby Digital"],
  "aspectRatio": null,

  "castAndCrew": [
    { "name": "Denis Villeneuve", "role": "Director", "url": "https://www.rottentomatoes.com/celebrity/denis-villeneuve" },
    { "name": "Timothée Chalamet", "role": "Paul Atreides", "url": "https://www.rottentomatoes.com/celebrity/timothee_chalamet" },
    { "name": "Zendaya", "role": "Chani", "url": "https://www.rottentomatoes.com/celebrity/zendaya" },
    { "name": "Rebecca Ferguson", "role": "Jessica", "url": "https://www.rottentomatoes.com/celebrity/rebecca_ferguson" },
    { "name": "Javier Bardem", "role": "Stilgar", "url": "https://www.rottentomatoes.com/celebrity/javier_bardem" },
    { "name": "Josh Brolin", "role": "Gurney Halleck", "url": "https://www.rottentomatoes.com/celebrity/josh_brolin" }
  ],

  "numberOfSeasons": null,
  "seasons": [],
  "posterUrl": "https://resizing.flixster.com/Wk685cwvPp6E6fAnFc1noRRCtX0=/164x246/v2/...",
  "criticReviewsUrl": "https://www.rottentomatoes.com/m/dune_part_two/reviews",
  "audienceReviewsUrl": "https://www.rottentomatoes.com/m/dune_part_two/reviews/verified-audience",
  "emsId": "cecd223d-a20d-3268-bbef-9a4c847c673d",
  "rtId": "771566273",
  "searchQuery": "dune part two",
  "position": 1,
  "scrapedAt": "2026-09-20T00:00:50.890Z"
}
```

TV rows look the same, with `network`, `numberOfSeasons` and a `seasons` list of links instead of the theatrical fields. A season row (`/tv/severance/s02`) carries `seriesTitle`, its own Tomatometer, and the consensus for that season.

### Input

```json
{
  "searchQueries": ["dune part two", "anora", "severance"],
  "mediaType": "any",
  "resultsPerQuery": 1,
  "maxItems": 50
}
```

or by URL:

```json
{
  "startUrls": [
    "https://www.rottentomatoes.com/m/anora",
    "https://www.rottentomatoes.com/tv/severance/s02",
    "https://www.rottentomatoes.com/m/the_matrix/reviews"
  ]
}
```

| Field | What it does |
|---|---|
| `searchQueries` | One title per line. Searched, best match read. |
| `startUrls` | Movie, TV series or single-season URLs. A `/reviews` or `/cast-and-crew` link is trimmed back to the title page. A bare `/m/anora` or `rottentomatoes.com/m/anora` works too. |
| `mediaType` | `any`, `movies` or `tv`. Applies to the search only; pasted URLs are read as given. |
| `resultsPerQuery` | Matches to read per search line. 1 for a plain lookup, higher when a term is genuinely ambiguous. |
| `maxItems` | Hard cap on rows for the whole run. |

Both input fields can be used in the same run. Duplicates across them are read once.

### A note on slugs

Rotten Tomatoes puts the year in the slug when a title has been remade, and it does not always put it where you would guess. `/m/cats` is not the 2019 musical — that lives at `/m/cats_2019`. `/m/oppenheimer` is not the Nolan film; that is `/m/oppenheimer_2023`. Casablanca sits at `/m/1003707-casablanca` with a numeric prefix.

That is why searching by title is the safer input. Searching `oppenheimer` lands on the 2023 film; typing the slug you would expect lands on a stub page with no scores. If you do paste slugs and a row comes back as `NOT_SCORED_YET`, this is usually why.

Search has its own trap in the other direction. `breaking bad` on the default `any` setting comes back as *El Camino: A Breaking Bad Movie*, because the film outranks the series in the site's own relevance. Set `mediaType` to `tv` when you mean the show, or paste `/tv/breaking_bad`.

### Pricing and what counts as a charge

You are charged once per title actually returned, plus the run-start fee. The current figures are in the Pricing box on this listing.

Not charged:

- the sample row an empty run writes
- every diagnostic row — a title the site has no match for, a URL that does not resolve, a page with no scores yet, a blocked request
- a run that returns nothing at all

So a run that fails to find any of your titles costs you the start fee and nothing else, and you can see exactly why in the rows it did write.

### What this does not do

**No individual critic review texts.** The Tomatometer, the number of reviews behind it, the critics average rating out of 10, the fresh/rotten split, the top-critics score and the consensus paragraph all come back. The quotes from each named critic do not — that part of the site is served by an endpoint that answers browsers only, and reaching it would mean running a browser for every title, which would cost more per row than the row is worth. `criticReviewsUrl` and `audienceReviewsUrl` point at the pages if you want to read them by hand.

**`castAndCrew` is the top of the credits, not all of them.** You get the six or so people the title page puts in its Cast & Crew strip — directors first, then top-billed actors with their character names — each with their Rotten Tomatoes profile link. A 60-name ensemble list is not in there. The separate `directors`, `writers`, `producers`, `creators`, `distributor` and `productionCompanies` fields are complete, because those come from the page's own credits table.

**TV series pages carry an averaged score and no consensus.** That is how the site presents them: a series shows an average Tomatometer across its seasons and no consensus paragraph. Ask for a specific season (`/tv/severance/s02`) and you get a real Tomatometer, a real review count and that season's consensus.

**Some titles have no scores.** Unreleased films, minor catalogue entries and duplicate stub pages exist on the site with no meter on them. Those come back as an uncharged `NOT_SCORED_YET` row rather than a row full of nulls.

**Release dates come through as the site writes them** — `"Mar 1, 2024, Wide"`, `"Jan 1, 2009"` — not as ISO dates. Box office is the same: `"$282.1M"`, US gross only, and absent on most TV and on films that never had a wide US release.

**Search is a search, not an exact-match lookup.** One line in, the best match out. For a guaranteed title use its URL.

**No episode-level data, no showtimes, no streaming prices.** `seasons` gives you the links to go a level deeper; the actor does not follow them on its own.

### Rate and reliability

Measured over 27 varied lookups on 19 September 2026 — blockbusters, 1940s catalogue titles, foreign-language films, documentaries, badly reviewed films, current releases, TV series and TV seasons — 27 of 27 returned a complete row. Mean coverage was 11.7 of the 12 headline fields; the gaps are TV series with no runtime and no consensus, which is the site's own shape rather than a miss.

When an address does get refused, the actor moves to another one and tries again. If every address fails for a title, that title gets an uncharged diagnostic row naming the status code, and the run carries on with the rest.

### FAQ

**Is there a Rotten Tomatoes API?**
Not a public one. The official developer programme was retired and there is no self-serve key any more; licensing is a business request now. This actor reads the public site instead, and it needs nothing from you but the titles.

**Do I need a Rotten Tomatoes login?**
No. Nothing here uses an account, a cookie or a key.

**Can I get both the Tomatometer and the audience score?**
Yes, both, on every row, with their review counts and average ratings. `tomatometer` and `audienceScore` are the two percentages; `criticsAverageRating` is out of 10 and `audienceAverageRating` is out of 5, which is how the site scores them.

**How do I look up a TV season rather than the whole show?**
Paste the season URL — `https://www.rottentomatoes.com/tv/the_last_of_us/s01`. Searching the show name gives you the series page, which has an averaged score.

**What happens if a title does not exist?**
You get an uncharged row with `errorCode: "NO_MATCH"` (search found nothing) or `"NOT_FOUND"` (the URL is a 404), and a hint about what usually causes it. The run still exits successfully.

**Can I run this on a schedule?**
Yes. Scores and review counts move, so a weekly or daily run over a watchlist is the common use. Every row carries `scrapedAt`, so you can diff two runs.

**Does it work outside the US?**
Yes. The site serves the same catalogue everywhere; nothing here is geo-specific. Dates and box office are US-centric because that is what Rotten Tomatoes publishes.

**How is this different from an IMDb scraper?**
Different scores and different fields. Rotten Tomatoes has the Tomatometer — an aggregate of published critic verdicts, with the fresh/rotten split and the consensus paragraph — and the Popcornmeter for the audience. IMDb has a single 1–10 user rating and no critic aggregate. If you want critic sentiment, you want this one; if you want user votes, you want IMDb. A lot of people pull both and compare them.

# Actor input Schema

## `searchQueries` (type: `array`):

One movie or TV title per line. The actor searches Rotten Tomatoes the way its own search box does and reads the best match. When a title has been remade, add the year: "cats 2019" lands on the right page, "cats" may not.

## `startUrls` (type: `array`):

Optional. Paste movie pages (https://www.rottentomatoes.com/m/anora), TV series pages (/tv/severance) or a single season (/tv/severance/s02). A /reviews or /cast-and-crew link is fine too, it is trimmed back to the title. Works alongside the field above.

## `mediaType` (type: `string`):

Applies to the title search only. Pasted URLs are read as given.

## `resultsPerQuery` (type: `integer`):

How many search matches to read for each line above. Leave at 1 for a straight lookup. Raise it when a term is genuinely ambiguous and you want the alternatives too. You are charged per title returned.

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

Hard cap on rows for the whole run, across every search term and URL. You are charged per title returned.

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

Optional. The actor already rotates its own addresses; leave this alone unless you need to exit from a specific network. Your own proxy URLs are used as given.

## Actor input object example

```json
{
  "searchQueries": [
    "dune part two",
    "severance"
  ],
  "startUrls": [],
  "mediaType": "any",
  "resultsPerQuery": 1,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

One row per title: Tomatometer and Popcornmeter with their review counts, average ratings and fresh/rotten splits, the top-critic score, the critics consensus paragraph, synopsis, genres, age rating, runtime, theatrical and streaming release dates, box office, cast and crew, distributor, network, poster and the Rotten Tomatoes ids. Empty input, an unrecognised URL or a title the site has not scored writes an uncharged sample or diagnostic row instead.

# 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 = {
    "searchQueries": [
        "dune part two",
        "severance"
    ],
    "startUrls": [],
    "mediaType": "any",
    "resultsPerQuery": 1,
    "maxItems": 50,
    "proxyConfiguration": {
        "useApifyProxy": false
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dami_studio/rotten-tomatoes-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 = {
    "searchQueries": [
        "dune part two",
        "severance",
    ],
    "startUrls": [],
    "mediaType": "any",
    "resultsPerQuery": 1,
    "maxItems": 50,
    "proxyConfiguration": { "useApifyProxy": False },
}

# Run the Actor and wait for it to finish
run = client.actor("dami_studio/rotten-tomatoes-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 '{
  "searchQueries": [
    "dune part two",
    "severance"
  ],
  "startUrls": [],
  "mediaType": "any",
  "resultsPerQuery": 1,
  "maxItems": 50,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}' |
apify call dami_studio/rotten-tomatoes-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dami_studio/rotten-tomatoes-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/4f92GJ8z3RaYuQJlO/builds/labkHZ4Vuoiut8R0B/openapi.json
