Letterboxd API & Scraper
The Letterboxd API returns film details, ratings and user data as clean JSON.
🤖 Using an AI assistant? Copy this link into ChatGPT / Claude / Cursor — it reads every endpoint and parameter instantly and tells you if this API fits your use case.
The primary film endpoint returns a film's title, year, directors, cast, genres, runtime, poster, IMDb/TMDb ids, average rating and rating count, and you can pull a user, their films, diary and lists, film reviews, browse, similar films and popular lists. It is built for film apps, recommendation engines and media research that need Letterboxd data without a scraper. One ReefAPI key, one shared credit pool, the standard envelope.
Slugs, the star scale and the page sizes Letterboxd fixes for you
Letterboxd has no numeric film id in its public pages, so everything here is addressed by slug. The numbers below are from live calls on Parasite, on a member diary, and on a one-minute 1920 documentary, which is what shows you where the nulls are.
| Field or parameter | Measured value | What to know |
|---|---|---|
| film slug | parasite-2019, the-housemaid | the id for every film action. Usually title-year, but a title with no collision has no year suffix. A full film URL is also accepted and parsed down to the slug. |
| avg_rating | 4.52 | the community mean on Letterboxd's 5-star scale, two decimals. Not out of 10. |
| user_rating, diary rating | 3.5, 4.0, 4.5 | half-star granularity from 0.5 to 5.0. null when the member logged the film without rating it: 6 of 72 rows in one member's film list, 16 of 50 in the diary. |
| watch_count, rating_count | 7,471,960 and 5,774,276 | two separate counters. More people log a film than rate it, so watch_count is always the larger of the pair. |
| like_count, list_count | 3,859,986 and 884,440 | likes are the heart, list_count is how many member lists include the film. |
| imdb_id, tmdb_id | tt6751668 and 496243 | both are strings. tmdb_id is bare digits with no prefix, imdb_id keeps the tt. |
| year on the film action | null | the film record does not carry a release year. Read it off the slug suffix, or off imdb_id / tmdb_id in another catalog. |
| per_page | films 72, diary 100, reviews 12, browse 72 | a hint only. Letterboxd fixes the page size per view and the value it actually used is echoed in meta.per_page. Walk pages with meta.has_more. |
Community counters are published only once a film has enough activity. A measured film action on kino-the-girl-of-colour, a one-minute 1920 documentary, returned a title, runtime_minutes 1, genres, a poster, imdb_id tt3307880 and tmdb_id 360370, with avg_rating, rating_count, watch_count, like_count and list_count all null.
Real request and response JSON
Captured from the indexed primary action, film, on .
{
"method": "POST",
"url": "https://api.reefapi.com/letterboxd/v1/film",
"headers": {
"x-api-key": "$REEF_KEY",
"content-type": "application/json"
},
"body": {
"film": "parasite-2019"
}
}{
"ok": true,
"meta": {
"api": "letterboxd",
"endpoint": "film",
"mode": "live",
"latency_ms": 1792.1,
"record_count": 1,
"bytes": 322776,
"cache_hit": false,
"method": "public_html_json_ld"
},
"data": {
"film": {
"slug": "parasite-2019",
"title": "Parasite",
"name": "Parasite",
"year": null,
"tagline": "Act like you own the place.",
"description": "All unemployed, Ki-taek's family takes peculiar interest in the wealthy and glamorous Parks for their livelihood until they get entangled in an unexpected incident.",
"poster_url": "https://a.ltrbxd.com/resized/film-poster/4/2/6/4/0/6/426406-parasite-[redacted-phone]-crop.jpg?v=8f5653f710",
"runtime_minutes": 133,
"directors": [
"Bong Joon Ho"
],
"director": "Bong Joon Ho",
"cast": [
{
"name": "[redacted-name]",
"role": "Kim Ki-taek"
},
{
"name": "[redacted-name]",
"role": "Park Dong-ik"
},
{
"name": "[redacted-name]",
"role": "Yeon-kyo"
}
],
"crew": {
"producer": [
"Jang Young-hwan",
"Kwak Sin-ae",
"Moon Yang-kwon"
],
"writer": [
"Kim Dae-hwan",
"Bong Joon Ho",
"Han Jin-won"
],
"story": [
"Bong Joon Ho"
],
"editor": [
"Yang Jin-mo"
],
"cinematography": [
"Hong Kyung-pyo"
],
"assistant_director": [
"Kim Seong-sik",
"Yoon Young-woo"
],
"executive_producer": [
"Miky Lee",
"Heo Min-heoi",
"Lim Myeong-gyun"
],
"production_design": [
"Lee Ha-jun"
],
"art_direction": [
"Mo So-ra"
],
"set_decoration": [
"Noh Seung-goog",
"Song Suk-ki",
"Cho Won-woo"
],
"visual_effects": [
"Hong Jeong-ho",
"Peter Ahn",
"Ha Jae-gu"
],
"stunts": [
"Yoo Mi-jin",
"Kwon Ji-hoon",
"Kang Gyeong-su"
],
"composer": [
"Jung Jae-il"
],
"sound": [
"Choi Tae-young",
"Eun Hee-soo",
"Kim Byung-in"
],
"costume_design": [
"Choi Se-yeon"
],
"makeup": [
"Kim Ho-sik",
"Kwak Tae-yong",
"Hwang Hyo-kyun"
]
},
"writers": [
"Kim Dae-hwan",
"Bong Joon Ho",
"Han Jin-won"
],
"cinematography": [
"Hong Kyung-pyo"
],
"editors": [
"Yang Jin-mo"
],
"composers": [
"Jung Jae-il"
],
"producers": [
"Jang Young-hwan",
"Kwak Sin-ae",
"Moon Yang-kwon"
],
"genres": [
"Thriller",
"Comedy",
"Drama"
],
"tags": [
"Thriller",
"Comedy",
"Drama"
],
"themes": [
"Intense violence and sexual transgression",
"Humanity and the world around us",
"Moving relationship stories"
],
"alternative_titles": [
"ชนชั้นปรสิต",
"パラサイト 半地下の家族:2019",
"Parazit"
],
"similar_films": [
{
"slug": "no-other-choice-2025",
"name": null,
"url": "https://letterboxd.com/film/no-other-choice-2025/",
"user_rating": null
},
{
"slug": "the-housemaid",
"name": null,
"url": "https://letterboxd.com/film/the-housemaid/",
"user_rating": null
},
{
"slug": "saltburn",
"name": null,
"url": "https://letterboxd.com/film/saltburn/",
"user_rating": null
}
],
"countries": [
"South Korea"
],
"languages": [
"English",
"German",
"Korean"
],
"production_companies": [
"Barunson E&A"
],
"imdb_id": "tt6751668",
"tmdb_id": "496243",
"imdb_url": "https://www.imdb.com/title/tt6751668/",
"tmdb_url": "https://www.themoviedb.org/movie/496243/",
"url": "https://letterboxd.com/film/parasite-2019/",
"avg_rating": 4.52,
"rating_count": 5578743,
"review_count": 744252,
"watch_count": 7231524,
"like_count": 3759340,
"list_count": 865763,
"watches": 7231524,
"lists": 865763,
"likes": 3759340,
"fans": null
}
}
}What the Letterboxd API does
| Action | Description | Concrete use case | Key params |
|---|---|---|---|
| film | Full Letterboxd film detail by film slug or URL: title, year, directors, cast, genres, runtime, poster, IMDb/TMDb IDs, average rating, total ratings, watch count, like count, and list appearances. | Content platforms call film to get full Letterboxd film detail by film slug or URL. | film, film_slug, url |
| user | Public Letterboxd member profile and stats by username: display name, avatar, bio, film/list/follower counts, and the member's favourite films. | Research tools call user to get public Letterboxd member profile and stats by username. | user |
| user_films | Paginated list of films a Letterboxd member has watched and rated, with their personal star rating for each. | Community analysts call user_films to get paginated list of films a Letterboxd member has watched and rated, with their personal star r…. | user, page, per_page |
| user_diary | Paginated diary entries for a Letterboxd member: film slug, name, watch date, and star rating for each viewing. | Media monitors call user_diary to get paginated diary entries for a Letterboxd member. | user, page, per_page |
| list | A public Letterboxd curated film list: title, description, and the paginated film entries with user ratings. | Content platforms call list to get a public Letterboxd curated film list. | user, list, page, per_page |
| film_reviews | Paginated public reviews for a Letterboxd film: reviewer username, display name, star rating, review text, and like count. | Research tools call film_reviews to get paginated public reviews for a Letterboxd film. | film, film_slug, url, page, per_page |
| browse | Browse Letterboxd films with optional genre / decade / year filters, ordered by popularity, rating, release date, or runtime (paginated): slug, name, URL, and average rating. | Community analysts call browse to get browse Letterboxd films with optional genre / decade / year filters, ordered by popularity, r…. | sort, genre, decade, year, page, ... |
| film_similar | Films Letterboxd shows as similar/related to a given film: slug, name, URL — useful for recommendation and discovery off a single seed film. | Media monitors call film_similar to get films Letterboxd shows as similar/related to a given film. | film, film_slug, url |
| lists_popular | Browse Letterboxd's most popular member-curated film lists for a time window (week/month/year/all): list title, owner username, list slug+URL, and a preview of the first films. | Content platforms call lists_popular to get browse Letterboxd's most popular member-curated film lists for a time window (week/month/year…. | period, page, per_page |
| user_lists | The public film lists a Letterboxd member has created (paginated): list title, slug+URL, and a preview of the first films in each list. | Research tools call user_lists to get the public film lists a Letterboxd member has created (paginated). | user, page, per_page |
| search | Search Letterboxd for films, members, or curated lists by keyword — returns matching results with ratings and links. | Community analysts call search to search Letterboxd for films, members, or curated lists by keyword. | query, type, page, per_page |
Call film from your stack
curl -X POST https://api.reefapi.com/letterboxd/v1/film \
-H "x-api-key: $REEF_KEY" \
-H "content-type: application/json" \
-d '{"film":"parasite-2019"}'import requests
r = requests.post(
"https://api.reefapi.com/letterboxd/v1/film",
headers={"x-api-key": REEF_KEY},
json={
"film": "parasite-2019"
},
)
print(r.json()["data"])const res = await fetch("https://api.reefapi.com/letterboxd/v1/film", {
method: "POST",
headers: {
"x-api-key": process.env.REEF_KEY,
"content-type": "application/json",
},
body: JSON.stringify({
"film": "parasite-2019"
}),
});
const { ok, data, meta, error } = await res.json();Ask your MCP-connected assistant: call reefapi.letterboxd.film with {"film":"parasite-2019"}.Who uses this API and why
- Film apps call film to enrich a title with rating, cast and cross-links to IMDb and TMDb.
- Recommendation engines use film_similar and lists_popular to surface related films.
- Media research uses user_films and film_reviews to study taste and sentiment.
Questions developers ask before integrating
Why is year null even though the slug ends in a year?
The film action does not return a release year. Measured on parasite-2019, the-sheep-detectives and kino-the-girl-of-colour, year came back null every time while runtime_minutes, genres and the external ids were populated. The year in a slug is a disambiguator Letterboxd adds when two films share a title, not a data field, and plenty of slugs have no year at all. If you need a reliable year, take imdb_id or tmdb_id from the same record and resolve it elsewhere.
Why does an obscure film come back with no rating at all?
Letterboxd only publishes a rating histogram once a film has accumulated enough ratings, and below that threshold the page carries no numbers to read. A live call on kino-the-girl-of-colour returned a complete identity block, with title, runtime, genre, poster, tt3307880 and tmdb 360370, and null for avg_rating, rating_count, watch_count, like_count and list_count together. All five going null at once is the signal that the film is below the threshold, not that the call failed.
How does the half-star rating come back in the JSON?
As a float, not as stars or a string. Letterboxd's scale runs from half a star to five in half-star steps, and the API mirrors that exactly: a measured diary page returned 4.0, 3.5, 4.5, 2.5 and 2.0, and never a value like 3.7. Multiply by two if you need integer buckets. A member who logged a film without rating it produces null, which is different from a zero and should not be averaged in.
Why is watch_count higher than rating_count on every film?
They count different actions. watch_count is how many members marked the film as watched, rating_count is how many attached a star rating, and marking watched does not require rating. On Parasite the gap was 7,471,960 watches against 5,774,276 ratings, with 3,859,986 likes and 884,440 list appearances on top. If you are ranking by popularity, watch_count is the broader signal and rating_count is the one avg_rating is actually computed over.
What does this give me that the imdb or movies-tv engines do not?
The community layer. IMDb gives you a 10-point rating and a vote count, TMDB gives you a 10-point rating and popularity, and neither of them knows how many people put a film in a list or logged it in a diary. This engine returns the Letterboxd 5-star average, the watch, like and list counters, per-member diary entries with watch dates, member film lists with each member's own rating, and curated lists ranked by period. It also returns Letterboxd's editorial themes, which are phrases such as Intense violence and sexual transgression rather than genre labels.
Can I control how many rows a page returns?
No. per_page is accepted but treated as a hint, because Letterboxd fixes the page size per view server side. Measured: user_films returned 72 rows, user_diary returned 50 rows on a page reported as per_page 100, film_reviews returned 12, and browse returned 72. The value actually used comes back in meta.per_page and meta.has_more tells you whether to ask for the next page. Budget your calls off those two, not off a number you sent.
Why do user_films rows have a null name?
That view is a poster grid, so the only identity on the page is the film slug and its link. A measured page for one member returned rows of slug, url, user_rating and name null. Treat slug as the join key, which is what every other action in this engine takes anyway, and call film only for the slugs you actually need to display. user_diary is the richer sibling: its rows carry both slug and a name that includes the year, such as Spider-Man: No Way Home (2021).
What is the difference between genres, tags and themes?
genres is the conventional list, Thriller, Comedy, Drama on Parasite. tags came back identical to genres on the films measured, so do not treat it as a second signal. themes is the one that is genuinely different: it is Letterboxd's own editorial clustering and returns phrases such as Humanity and the world around us or Moving relationship stories, eight of them on Parasite. There is no numeric id on any of the three, so they join on the string.
What is the Letterboxd API?
Letterboxd API is a ReefAPI endpoint group for letterboxd It returns live JSON through POST requests under /letterboxd/v1.
Is the Letterboxd API free to try?
Yes. ReefAPI starts with 1,000 free credits, no card required. Letterboxd calls use the same shared credit balance as every other ReefAPI engine.
Do I need a Letterboxd login or account?
No login to Letterboxd is needed for the API response. You call ReefAPI with your x-api-key header, and the playground can run live examples before you create a production key.
How fresh is the Letterboxd data?
The page example is captured from a live film call, and production requests fetch live data through ReefAPI rather than a static sample.
How many credits does the Letterboxd API use?
Letterboxd actions currently cost 1 credit per successful call. Failed or blocked calls are free, and all APIs draw from one credit pool.
Can I call Letterboxd from an AI assistant or MCP client?
Yes. Connect ReefAPI once through MCP and your assistant can call letterboxd actions with the same key, credit pool and JSON envelope used by normal REST requests.