# Boxofficemojo Scraper (`normdata/boxofficemojo-scraper`) Actor

Get real box office data from Box Office Mojo: yearly and all-time domestic/worldwide charts, full per-movie financials (budget, distributor, MPAA, ROI, and a real country-by-country gross breakdown), or both at once with a whole year's top movies fully priced out - no titles to type in.

- **URL**: https://apify.com/normdata/boxofficemojo-scraper.md
- **Developed by:** [Norm Data](https://apify.com/normdata) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.80 / 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?

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

![Norm Data](https://i.ibb.co/rGbhM5Y8/Chat-GPT-Image-Sep-8-2026-02-20-50-PM.png)

## Box Office Mojo Scraper

Get real box office data from **Box Office Mojo** with no login: a whole domestic/worldwide chart
for one year or all-time, optionally with full per-movie financials added to every row - no titles
to type in - or full financials for specific titles you do list.

Here is one real row (chart mode with `includeMovieDetails`, no title typed by the caller):

```json
{
  "rank": 1,
  "title": "Inside Out 2",
  "imdb_id": "tt22022452",
  "domestic_distributor": "Walt Disney Studios Motion Pictures",
  "budget": 200000000,
  "mpaa": "PG",
  "running_time_minutes": 96,
  "genres": ["Adventure", "Animation", "Comedy", "Drama", "Family", "Fantasy", "Sport"],
  "domestic_gross": 652980194,
  "international_total": 1045883622,
  "worldwide_total": 1698863816,
  "roi": 8.494,
  "profit": 1498863816,
  "international_grosses": [
    { "country": "United Kingdom", "release_date": "Jun 12, 2024", "opening": 12400000, "gross": 85500000 },
    { "country": "Japan", "release_date": "Aug 1, 2024", "opening": 4200000, "gross": 36685528 }
  ],
  "chart_area": "domestic",
  "chart_period": "year",
  "scraped_at": "2026-09-14T00:00:00.000Z"
}
```

### What it does

Reads Box Office Mojo's own public pages directly and normalizes them into clean, flat rows.

- **Chart mode** returns a full ranking: one year's domestic chart, one year's worldwide chart, or
  the all-time top 200 for either area. Domestic and worldwide are genuinely different rankings, not
  the same list with a label swapped. Turn on `includeMovieDetails` to add full financials to every
  row automatically - each row's own title is looked up for you, nothing to type in.
- **Movie mode** takes a title or an IMDb ID and returns budget, distributor, MPAA rating, genres,
  running time, domestic/international/worldwide gross, computed ROI and profit, and a real
  country-by-country international gross breakdown - for specific titles you already know.

Missing source values are returned as `null`, never invented.

### Why this scraper

- **The only one of the three real Box Office Mojo Actors on Apify that does both movie financials
  and chart rankings - and the only one that can combine them in one request.** One competitor only
  does charts (worldwide, no domestic chart at all). The other only does movie financials, one title
  at a time (no chart mode at all, so no way to get a whole year's movies without already knowing
  every title). `includeMovieDetails` gets a year's top movies with full financials from one request.
- **A real country-by-country international breakdown**, not a single number worked out by
  subtracting domestic from worldwide.
- **Reliable domestic/international/worldwide totals on every title**, including classic movies with
  multiple theatrical re-releases, where an approach built only around the single-release country
  table returns nothing for the totals.
- **No API key, no login, no browser.** Reads only what a normal visitor's page load reads.

### How it compares

Checked directly against both real Box Office Mojo scrapers on Apify (their own declared
input/output, not a guess):

| Capability | This actor | parseforge/boxofficemojo-world-scraper | trovevault/movie-box-office-tracker |
|---|:--:|:--:|:--:|
| Movie financials (budget, distributor, MPAA, opening, ROI/profit) | yes | **no** | yes |
| **Real per-country international breakdown** | **yes** | no | no (derived as a subtraction) |
| **Domestic yearly chart** | **yes** | no | no |
| Worldwide yearly chart | yes | yes | no |
| **All-time chart, both domestic and worldwide** | **yes** | worldwide only | no |
| **Full financials for a whole chart, no titles typed in** | **yes** | no | no (one title at a time only) |
| Theater count on year charts | yes | yes | n/a |
| `maxItems` cap | yes | yes | n/a |
| Declared Apify dataset schema | yes | not documented | not documented |

### Use cases

- **Film industry analysis.** Budget, ROI, and profit across a slate of titles.
- **International distribution research.** Real country-level opening and gross figures.
- **Box office journalism and content.** A year's chart, or a title's full financial profile, ready
  to drop into a story or chart.
- **Studio and distributor benchmarking.** Compare domestic vs. international performance by title,
  genre, or distributor.
- **Historical analysis.** All-time domestic and worldwide top 200, compared side by side.

### Quickstart

A year's top movies with full financials - no titles to type in:

```json
{ "mode": "chart", "period": "year", "year": 2024, "area": "domestic", "includeMovieDetails": true }
```

A year's domestic chart, bare (just the ranking):

```json
{ "mode": "chart", "period": "year", "year": 2024, "area": "domestic" }
```

A movie's full financials, by title:

```json
{ "mode": "movie", "titles": ["Inside Out 2", "Dune: Part Two"] }
```

A movie by direct IMDb ID:

```json
{ "mode": "movie", "imdbIds": ["tt22022452"] }
```

A year's worldwide chart:

```json
{ "mode": "chart", "period": "year", "year": 2024, "area": "worldwide" }
```

The all-time worldwide top 200:

```json
{ "mode": "chart", "period": "alltime", "area": "worldwide" }
```

### Input reference

| Field | Applies to | Description |
|---|---|---|
| `maxItems` | all | Ceiling on rows written. |
| `mode` | all | `movie` or `chart`. |
| `titles` / `imdbIds` | movie | Free-text titles (resolved automatically) or direct IMDb IDs. |
| `period` | chart | `year` or `alltime`. |
| `year` | chart, period = year | The year's chart to pull (1977 onward). |
| `area` | chart | `domestic` or `worldwide`. |
| `includeMovieDetails` | chart | Adds full movie-mode fields to every chart row by looking up each row's own title - no titles typed in. Billed as its own event per row (real extra requests a bare chart pull doesn't need). |

### Output reference (selected)

| Field | Description |
|---|---|
| `imdb_id` / `title` / `url` | Identity. |
| `domestic_distributor` / `budget` / `mpaa` / `running_time_minutes` / `genres` | Movie profile. |
| `domestic_opening` / `domestic_gross` / `international_total` / `worldwide_total` | Movie mode financials. |
| `roi` / `profit` | Computed: `worldwide_total / budget` and `worldwide_total - budget`. |
| `international_grosses` | Movie mode: one entry per country with release date, opening, and gross. |
| `rank` / `total_gross` / `worldwide_gross` / `lifetime_gross` | Chart mode ranking fields (which one is populated depends on `period`/`area`). |
| `theaters` / `distributor` / `release_date` / `is_estimated` | Chart mode, year charts. |
| `chart_area` / `chart_period` | Which chart a row came from. |
| `query` / `error` | Set on an unresolved movie lookup. |
| `scraped_at` | ISO 8601 collection timestamp. |

### Run via API and CLI

```bash
curl -X POST "https://api.apify.com/v2/acts/USERNAME~boxofficemojo-scraper/run-sync-get-dataset-items?token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"mode":"movie","titles":["Inside Out 2"]}'
```

```bash
apify call USERNAME/boxofficemojo-scraper --input '{"mode":"chart","period":"year","year":2024,"area":"domestic"}'
```

### Fetch results

```bash
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?token=<TOKEN>&clean=true&format=json"
curl "https://api.apify.com/v2/datasets/<DATASET_ID>/items?token=<TOKEN>&clean=true&format=csv"
```

### Billing and limits

- **Pay per result.** Billed per row written (one row per movie in movie mode; one row per chart
  entry in chart mode).
- **`includeMovieDetails` is billed as a separate event**, once per chart row it successfully
  enriches - it costs real extra requests a bare chart pull doesn't need, so it isn't folded into the
  base row price. A row it fails to enrich is left as chart-only and not charged for the add-on.
- **No charge on failure.** A run that produces zero rows fails with a message and is not billed.
- An unresolved title/ID still writes one `error: "not found"` row and is billed as a base row.
- **Free Apify plans** run the built-in 10-row sample only.

### FAQ and troubleshooting

**Do I need an API key?** No. Box Office Mojo's own public pages are read directly, with no key and
no login.

**Why did my title search return "not found"?** The title didn't resolve to a real movie. Try adding
the release year to the title, or use `imdbIds` with the exact ID.

**What if two different movies share the exact same title?** This does happen (remakes, franchise
reboots) - add the release year to the title (e.g. `"Moana 2016"`) to disambiguate, or use `imdbIds`
directly for guaranteed precision. This is a real limit of title-based lookup on any service, not
something specific to this Actor.

**Why does `domestic` mean something different in movie mode vs. chart mode?** In movie mode,
`domestic_gross` is that title's own real domestic total across every theatrical release it's ever
had. In chart mode, `domestic_gross` only appears on a worldwide chart row, showing that title's
domestic share of the year being charted.

**Why are some `international_grosses` countries missing for older titles?** Box Office Mojo itself
only started tracking per-country detail for more recent releases - the domestic/international/
worldwide totals are still real and complete either way, just without a full per-country list on
older titles.

**Are domestic and worldwide charts the same list?** No. They are genuinely different real rankings

- a title's domestic hit and its worldwide hit are not always the same movie.

**How current is the data?** Read live from Box Office Mojo on every run. It is not cached.

**Is this an official Box Office Mojo/IMDb tool?** No. Independent, not affiliated with either. It
reads only publicly available pages.

### Local development

```bash
bun install
bun test                       # offline: chart/movie parsing, normalization (real fixtures)
bun run src/main.ts             # reads storage/key_value_stores/default/INPUT.json
```

# Actor input Schema

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

Caps how many rows this run writes. Starts at 10 for a quick sample; raise it for a full run. Applies even when this field is left out of the input entirely (e.g. a bare API call), not just in the Console form.

## `mode` (type: `string`):

Chart returns a full domestic or worldwide gross ranking for one year, or the all-time list - no titles to type in. Movie returns budget, distributor, MPAA rating, genres, and a country-by-country gross breakdown for specific titles you list.

## `titles` (type: `array`):

Free-text titles, resolved to the right movie via IMDb's own title search, e.g. "Inside Out 2", "Dune: Part Two".

## `imdbIds` (type: `array`):

Direct IMDb IDs (e.g. "tt22022452") to skip the title search. The "tt" prefix is optional.

## `period` (type: `string`):

One year returns that year's full domestic or worldwide chart. All-time returns the all-time domestic or worldwide top 200.

## `year` (type: `integer`):

Which year's chart to pull. Ignored when period is "All-time".

## `area` (type: `string`):

Domestic and worldwide are genuinely different rankings with different columns - a title's domestic hit and its worldwide hit are not always the same movie.

## `includeMovieDetails` (type: `boolean`):

Looks up each chart row's own title and adds real budget, distributor, MPAA rating, genres, ROI, and a country-by-country gross breakdown - no need to type any titles yourself. Billed as its own event per row, since it costs real extra requests a bare chart pull doesn't need.

## Actor input object example

```json
{
  "maxItems": 10,
  "mode": "chart",
  "titles": [
    "Inside Out 2"
  ],
  "period": "year",
  "year": 2024,
  "area": "domestic",
  "includeMovieDetails": true
}
```

# Actor output Schema

## `listings` (type: `string`):

Complete dataset with all scraped rows.

## `overview` (type: `string`):

Overview view with key fields displayed in a table.

# 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 = {
    "maxItems": 10,
    "mode": "chart",
    "titles": [
        "Inside Out 2"
    ],
    "period": "year",
    "year": 2024,
    "area": "domestic",
    "includeMovieDetails": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("normdata/boxofficemojo-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 = {
    "maxItems": 10,
    "mode": "chart",
    "titles": ["Inside Out 2"],
    "period": "year",
    "year": 2024,
    "area": "domestic",
    "includeMovieDetails": True,
}

# Run the Actor and wait for it to finish
run = client.actor("normdata/boxofficemojo-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 '{
  "maxItems": 10,
  "mode": "chart",
  "titles": [
    "Inside Out 2"
  ],
  "period": "year",
  "year": 2024,
  "area": "domestic",
  "includeMovieDetails": true
}' |
apify call normdata/boxofficemojo-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,normdata/boxofficemojo-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/1YTFRhi5hOFTrbbyl/builds/KTiE3rtNb1gi7a1UD/openapi.json
