# Letterboxd Films Scraper (`automation-lab/letterboxd-film-details-scraper`) Actor

Extract public Letterboxd film metadata, current ratings, posters, genres, and activity links from supplied film URLs.

- **URL**: https://apify.com/automation-lab/letterboxd-film-details-scraper.md
- **Developed by:** [Stas Persiianenko](https://apify.com/automation-lab) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.44 / 1,000 item extracteds

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

## Letterboxd Films Scraper

Extract current public film metadata and rating signals from supplied Letterboxd film URLs. Letterboxd Films Scraper turns each accepted URL into one typed dataset record with film identity, year, directors, synopsis, poster, genres, aggregate ratings, and links back to the public film activity and reviews indexes.

The Actor is designed for repeatable catalog enrichment and rating snapshots. It does **not** collect review text, member diaries, profiles, or list membership.

### What can you extract?

Each successful URL produces one record containing:

| Field | Meaning |
| --- | --- |
| `filmId` | Letterboxd response identifier, with the slug as fallback |
| `slug`, `title`, `year` | Film identity and release year |
| `directors` | Credited director names |
| `description` | Public synopsis when available |
| `posterUrl` | Public poster image URL when available |
| `genres` | Genres shown by Letterboxd |
| `averageRating` | Current aggregate rating when available |
| `ratingCount`, `reviewCount` | Current public aggregate counts |
| `canonicalUrl` | Canonical Letterboxd film URL |
| `activityUrl`, `reviewsUrl` | Public index links; their contents are not scraped |
| `scrapedAt` | UTC extraction timestamp |

Optional source fields can be `null` or empty when Letterboxd does not expose them.

### Who is this Actor for?

- **Media catalog teams** enriching film records with Letterboxd identity and metadata.
- **Data analysts** taking comparable rating snapshots on a schedule.
- **Recommendation teams** joining titles, genres, directors, and aggregate audience signals to internal datasets.
- **Developers** who need typed JSON instead of maintaining a page parser.

### Why use URL-based film extraction?

A supplied-URL workflow is predictable: you choose the exact films, preserve input order, and receive at most one record per unique canonical film URL. Duplicate URLs are removed before processing. Unlike broad review or member scrapers, this Actor remains limited to public film-level details and aggregate rating signals.

### Getting started

1. Open the Actor input page.
2. Add one or more public URLs such as `https://letterboxd.com/film/parasite-2019/`.
3. Set **Maximum films** if you want to process only the first part of the input.
4. Click **Start**.
5. Open the default dataset and export JSON, CSV, Excel, XML, or another Apify-supported format.

### Input

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `startUrls` | array | Yes | Public `https://letterboxd.com/film/<slug>/` URLs. Review, list, diary, member, and search URLs are rejected. |
| `maxItems` | integer | No | Maximum unique films to export, from 1 to 1,000. Default: 100. |

Example input:

```json
{
  "startUrls": [
    { "url": "https://letterboxd.com/film/parasite-2019/" },
    { "url": "https://letterboxd.com/film/oppenheimer-2023/" }
  ],
  "maxItems": 2
}
```

### Output example

A current run returns records shaped like this:

```json
{
  "filmId": "hTha",
  "slug": "parasite-2019",
  "title": "Parasite",
  "year": 2019,
  "directors": ["Bong Joon Ho"],
  "description": "All unemployed, Ki-taek's family takes peculiar interest in the wealthy and glamorous Parks...",
  "posterUrl": "https://a.ltrbxd.com/resized/film-poster/...jpg",
  "genres": ["Thriller", "Comedy", "Drama"],
  "averageRating": 4.52,
  "ratingCount": 5783478,
  "reviewCount": 775408,
  "canonicalUrl": "https://letterboxd.com/film/parasite-2019/",
  "activityUrl": "https://letterboxd.com/film/parasite-2019/activity/",
  "reviewsUrl": "https://letterboxd.com/film/parasite-2019/reviews/",
  "scrapedAt": "2026-08-29T14:36:11.256Z"
}
```

Ratings and counts change over time. The example demonstrates the schema, not a permanently fixed value.

### How much does it cost to extract Letterboxd films?

The Actor uses pay-per-event pricing: a **$0.00005 Start** event per run plus one **Item extracted** event for each film record saved. At the BRONZE tier an item costs **$0.0024**; the six plan tiers decrease from $0.00276 at FREE to $0.000672 at DIAMOND. Failed, rejected, and duplicate URLs do not create an item charge.

At BRONZE, 1 saved film costs $0.00245, 10 films cost $0.02405, and 100 films cost $0.24005 including the start event. For efficiency, submit several film URLs in one run rather than starting one run per film.

### Refresh film ratings on a schedule

Create an Apify Schedule with the same URL set to take recurring snapshots. Each run stores current values and `scrapedAt`; compare datasets in your own database, spreadsheet, or automation flow to identify changes. The Actor does not provide built-in history, alerts, or change detection.

### Export and integrations

The default dataset works with Apify exports and integrations. Common workflows include:

- export CSV or Excel for a one-time catalog review;
- send dataset items to Google Sheets;
- connect a webhook that loads completed records into a warehouse;
- call the API from an enrichment pipeline;
- schedule a stable URL batch and compare snapshots downstream.

### Use the Apify API

Replace `APIFY_TOKEN` with your token.

#### cURL

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/automation-lab~letterboxd-film-details-scraper/runs?token=APIFY_TOKEN&waitForFinish=120" \
  -H "Content-Type: application/json" \
  -d '{"startUrls":[{"url":"https://letterboxd.com/film/parasite-2019/"}],"maxItems":1}'
```

#### JavaScript

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/letterboxd-film-details-scraper').call({
  startUrls: [{ url: 'https://letterboxd.com/film/parasite-2019/' }],
  maxItems: 1,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

#### Python

```python
from apify_client import ApifyClient

client = ApifyClient(token="APIFY_TOKEN")
run = client.actor("automation-lab/letterboxd-film-details-scraper").call(run_input={
    "startUrls": [{"url": "https://letterboxd.com/film/parasite-2019/"}],
    "maxItems": 1,
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)
```

### Use with MCP

Add the Apify MCP server to Claude Code:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/letterboxd-film-details-scraper"
```

For **Claude Desktop**, **Cursor**, or **VS Code**, add this MCP JSON block to the client's MCP configuration:

```json
{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com?tools=automation-lab/letterboxd-film-details-scraper"
    }
  }
}
```

Example prompts:

- “Run Letterboxd Films Scraper for Parasite and Oppenheimer, then summarize their current ratings.”
- “Extract these five Letterboxd film URLs and return a table with title, year, director, genres, and canonical URL.”
- “Refresh my supplied film URLs and save the resulting dataset for downstream comparison.”

### Reliability and failure behavior

The Actor uses the public server-rendered film page and parses structured movie data. It retries transient network errors, HTTP 429, and temporary 5xx responses up to two times with backoff. Invalid URLs, missing films, challenge pages, or pages without recognizable movie data fail clearly instead of silently returning an empty record.

No proxy, login, browser, or automatic residential fallback is used. A source-side layout or access-policy change can temporarily require an Actor update.

### Limits and responsible usage

Only public film-detail URLs are supported. Keep request volume reasonable and do not use the Actor to collect private information or circumvent access controls.

### Legality

Scraping public web data can be lawful, but your purpose, jurisdiction, and downstream use matter. Follow Letterboxd's terms, applicable privacy and database laws, and source rate limits. Poster URLs and film metadata may be subject to third-party rights; verify that your storage, redistribution, and commercial use are authorized. This Actor does not provide legal advice.

### Troubleshooting

**Why was my URL rejected?** Ensure it is an HTTPS Letterboxd film URL ending in `/film/<slug>/`. Review, member, diary, list, and search URLs are outside scope.

**Why is a field null or an array empty?** Letterboxd does not expose every metadata field for every title. Optional fields accurately reflect what was present during the run.

**Why did the run fail after retries?** Check the Actor log for an HTTP status or challenge message. Retry later if Letterboxd returned a temporary block or server error.

### Data freshness

Every run fetches the current public film page. `scrapedAt` identifies when the snapshot was taken. The Actor does not cache records across runs and does not claim real-time updates between scheduled executions.

### Related Actors

This Actor is intentionally standalone for Letterboxd film-level catalog records. Review-text, member diary, watchlist, and list-membership products are outside its scope; no unrelated third-party Actor is presented as an Automation Lab companion.

### FAQ

**Does it scrape Letterboxd reviews?** No. It returns the public reviews index URL and aggregate review count when available, but never review text.

**Does it support lists, profiles, diaries, or watchlists?** No. Only supplied public film-detail URLs are accepted.

**Can I process duplicates?** Duplicate canonical URLs are processed once per run.

**Can I monitor rating changes?** Schedule repeated runs and compare the resulting datasets downstream. Change detection and alerts are not built in.

**What happens when one URL is unavailable?** The run fails with a clear error rather than charging for or emitting an invalid film record.

# Actor input Schema

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

Public film URLs in the form https://letterboxd.com/film/film-slug/. Review, list, diary, member, and search URLs are rejected.

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

Maximum number of unique film records to export. Processing follows the supplied URL order.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://letterboxd.com/film/parasite-2019/"
    },
    {
      "url": "https://letterboxd.com/film/oppenheimer-2023/"
    }
  ],
  "maxItems": 10
}
```

# Actor output Schema

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

Open the default dataset view containing typed Letterboxd film metadata and current aggregate rating signals.

# 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 = {
    "startUrls": [
        {
            "url": "https://letterboxd.com/film/parasite-2019/"
        },
        {
            "url": "https://letterboxd.com/film/oppenheimer-2023/"
        }
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/letterboxd-film-details-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 = {
    "startUrls": [
        { "url": "https://letterboxd.com/film/parasite-2019/" },
        { "url": "https://letterboxd.com/film/oppenheimer-2023/" },
    ],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/letterboxd-film-details-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 '{
  "startUrls": [
    {
      "url": "https://letterboxd.com/film/parasite-2019/"
    },
    {
      "url": "https://letterboxd.com/film/oppenheimer-2023/"
    }
  ],
  "maxItems": 10
}' |
apify call automation-lab/letterboxd-film-details-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/letterboxd-film-details-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/tfDoBZaMheTjUcQZa/builds/REZHCm1BOyEd5CEl7/openapi.json
