Profile
Back to NewsBack
GitHub Trending 8 min
Reader Mode
nmcassa/letterboxdpy: A letterboxd webscraper

nmcassa/letterboxdpy: A letterboxd webscraper

12 hours ago

letterboxdpy

A Python library for Letterboxd data
Simple, modern, and easy-to-use toolkit for movies, users, and more.

PyPI version Python Version License Downloads Daily DOM Health Check

---

Installation

From PyPI

You can easily install the stable version of letterboxdpy from PyPI using pip: ``bash pip install letterboxdpy `

From GitHub Repository

Alternatively, if you wish to access the latest (potentially unstable) version directly from the GitHub repository, you can execute the following command:
`bash pip install git+https://github.com/nmcassa/letterboxdpy.git `

Local Installation (for Development)

If you have cloned the repository locally and want to make changes to the code, it is recommended to install it in "editable" mode. This allows you to run the library files directly and see your changes reflected immediately:
`bash pip install -e .

Or with example dependencies

pip install -e ".[examples]"
`
[!WARNING]
Please be aware that installing directly from the GitHub repository or locally might give you access to the most recent features and bug fixes, but it could also include changes that haven't been thoroughly tested and may not be stable for production use.

Core Objects

User Object

Explore the file | Functions Documentation
`python from letterboxdpy.user import User user_instance = User("nmcassa") print(user_instance) `
Click to expand User object response `json { "username": "nmcassa", "url": "https://letterboxd.com/nmcassa", "id": 1500306, "badge": "member", "display_name": "nmcassa", "bio": null, "location": null, "website": null, "watchlist_length": 76, "stats": { "films": 702, "this_year": 7, "lists": 2, "following": 8, "followers": 8 }, "favorites": { "51794": { "slug": "the-king-of-comedy", "name": "The King of Comedy", "url": "https://letterboxd.com/film/the-king-of-comedy/", "year": 1982, "log_url": "https://letterboxd.com/nmcassa/film/the-king-of-comedy/activity/" }, "...": "..." }, "avatar": { "exists": true, "upscaled": true, "url": "https://a.ltrbxd.com/resized/avatar/upload/1/5/0/0/3/0/6/shard/avtr-0-1000-0-1000-crop.jpg" }, "backdrop": null, "recent": { "watchlist": { "703077": { "id": "703077", "slug": "magazine-dreams", "name": "Magazine Dreams", "year": 2023 }, "...": "..." }, "diary": { "months": { "1": { "31": [ { "name": "If I Had Legs I'd Kick You", "slug": "if-i-had-legs-id-kick-you" } ], "...": "..." } } } } } `

Movie Object

Explore the file | Functions Documentation
`python from letterboxdpy.movie import Movie

lookup by slug

movie_instance = Movie("v-for-vendetta")

lookup by external ids

movie_instance = Movie(tmdb=752) movie_instance = Movie(imdb="tt0434409")

or using factory methods

movie_instance = Movie.from_tmdb(752) movie_instance = Movie.from_imdb("tt0434409") print(movie_instance)
`
Click to expand Movie object response `json { "url": "https://letterboxd.com/film/v-for-vendetta", "slug": "v-for-vendetta", "id": "51400", "title": "V for Vendetta", "original_title": null, "runtime": 132, "rating": 3.84, "year": 2005, "tmdb_link": "https://www.themoviedb.org/movie/752/", "tmdb_id": "752", "imdb_link": "http://www.imdb.com/title/tt0434409/maindetails", "imdb_id": "tt0434409", "poster": "https://a.ltrbxd.com/resized/film-poster/5/1/4/0/0/51400-v-for-vendetta-0-230-0-345-crop.jpg", "banner": "https://a.ltrbxd.com/resized/sm/upload/mx/jg/tz/ni/v-for-vendetta-1920-1920-1080-1080-crop-000000.jpg", "tagline": "People should not be afraid of their governments. Governments should be afraid of their people.", "description": "In a world in which Great Britain has become a fascist state...", "trailer": { "id": "3ge0navn9E0", "link": "https://www.youtube.com/watch?v=3ge0navn9E0", "embed_url": "https://www.youtube.com/embed/3ge0navn9E0" }, "alternative_titles": [ "Vendetta \u00fc\u00e7\u00fcn V", "O za osvetu",... ], "details": [ { "type": "studio", "name": "Virtual Studios", "slug": "virtual-studios", "url": "https://letterboxd.com/studio/virtual-studios/" }, "..." ], "genres": [ { "type": "genre", "name": "Thriller", "slug": "thriller", "url": "https://letterboxd.com/films/genre/thriller/" }, "..." ], "cast": [ { "name": "Natalie Portman", "role_name": "Evey Hammond", "slug": "natalie-portman", "url": "https://letterboxd.com/actor/natalie-portman/" }, "..." ], "crew": { "director": [ { "name": "James McTeigue", "slug": "james-mcteigue", "url": "https://letterboxd.com/director/james-mcteigue/" } ], "...": "..." }, "popular_reviews": [ { "user": { "username": "zoeyluke", "display_name": "zoey luke" }, "link": "https://letterboxd.com/zoeyluke/film/v-for-vendetta/3/", "rating": 4.5, "review": "I love natalie Portman and I hate the government" }, "...": "..." ] } `
Explore the file | Functions Documentation `python from letterboxdpy.search import Search search_instance = Search("V for Vendetta", 'films') print(search_instance.get_results(5)) `
Click to expand Search object response `json { "available": true, "query": "V%20for%20Vendetta", "filter": "films", "end_page": 1, "count": 5, "results": [ { "no": 1, "page": 1, "type": "film", "slug": "v-for-vendetta", "name": "V for Vendetta", "year": 2005, "url": "https://letterboxd.com/film/v-for-vendetta/", "poster": "https://s.ltrbxd.com/static/img/empty-poster-70-BSf-Pjrh.png", "directors": [ { "name": "James McTeigue", "slug": "james-mcteigue", "url": "https://letterboxd.com/director/james-mcteigue/" } ] }, { "no": 2, "page": 1, "type": "film", "slug": "lady-vengeance", "name": "Lady Vengeance", "year": 2005, "url": "https://letterboxd.com/film/lady-vengeance/", "poster": null, "directors": [ { "name": "Park Chan-wook", "slug": "park-chan-wook", "url": "https://letterboxd.com/director/park-chan-wook/" } ] },... ] } `

List Object

Explore the file
`python from letterboxdpy.list import List list_instance = List("nmcassa", "movies-to-watch-with-priscilla-park") list_instance.movies # fetch movies (lazy loading) print(list_instance) `
Click to expand List object response `json { "username": "nmcassa", "slug": "movies-to-watch-with-priscilla-park", "url": "https://letterboxd.com/nmcassa/list/movies-to-watch-with-priscilla-park", "list_id": "31052453", "title": "Movies to Watch with Priscilla Park", "author": "nmcassa", "count": 2, "date_created": "2024-05-18T16:44:57.013000Z", "date_updated": "2024-05-20T14:58:06.486000Z", "description": null, "tags": [], "_movies": { "240344": { "slug": "la-la-land", "name": "La La Land", "year": 2016, "url": "https://letterboxd.com/film/la-la-land/" }, "...": "..." } } `

Members Object

Explore the file | Functions Documentation
`python from letterboxdpy.members import Members members_instance = Members(max=5) print(members_instance.members) `
Click to expand Members object response `json [ "schaffrillas", "kurstboy", "demiadejuyigbe", "zoerosebryant", "jaragon23" ] `

Films Object

Explore the file | Functions Documentation
`python from letterboxdpy.films import Films films_instance = Films("https://letterboxd.com/films/popular/", max=3) print(films_instance.movies) `
Click to expand Films object response `json { "1197499": { "slug": "marty-supreme", "name": "Marty Supreme", "rating": 4.21, "url": "https://letterboxd.com/film/marty-supreme/" }, "772232": { "slug": "hamnet", "name": "Hamnet", "rating": 4.22, "url": "https://letterboxd.com/film/hamnet/" }, "1116600": { "slug": "sinners-2025", "name": "Sinners", "rating": 4.11, "url": "https://letterboxd.com/film/sinners-2025/" } } `

Watchlist Object

Explore the file
`python from letterboxdpy.watchlist import Watchlist watchlist = Watchlist("nmcassa") watchlist.movies # fetch movies (lazy loading) print(watchlist) `
Click to expand Watchlist object response `json { "username": "nmcassa", "url": "https://letterboxd.com/nmcassa/watchlist", "count": 134, "movies": { "51315": { "slug": "videodrome", "name": "Videodrome", "year": 1983, "url": "https://letterboxd.com/film/videodrome/" }, "...": "..." } } `

Advanced Features

Authentication & Sessions

Explore the file The
UserSession module unlocks account-specific features like profile customization and settings management. It handles login securely and persists your session, so you don't have to sign in every time. `python from letterboxdpy.auth import UserSession

Logs in if no session exists, or loads the saved session automatically

session = UserSession.ensure()

2. Programmatic login

session = UserSession.login("username", "password")

3. Manual load from custom path

session = UserSession.load(Path(".cookie/session.json"))

print(f"Authenticated as: {session.username}")
`
Click to expand UserSession demo output `text Authenticated as: nmcassa Session CSRF token: 9a8b7c6d5e4f3a2b1c0d `

User Settings

Explore the file The
UserSettings module allows reading and updating user profile and notification settings. Requires an authenticated session. `python from letterboxdpy.account.settings import UserSettings settings = UserSettings(session)

Get current profile data

profile = settings.get_profile() print(f"Current Bio: {profile['bio']}")

Update specific profile fields

settings.update_profile({ "location": "New York, USA", "website": "https://example.com", "bio": "Just another movie lover." })

Manage notifications

notifs = settings.get_notifications() settings.update_notifications({"emailEditorial": True, "pushFollowers": False})
`
Click to expand UserSettings demo response `json { "__csrf": "9a8b7c6d5e4f3a2b1c0d", "completeSettings": "true", "givenName": "Jane", "familyName": "Doe", "location": "New York, USA", "website": "https://example.com", "bio": "Just another movie lover.", "favouriteFilms": [ { "id": "51400", "name": "V for Vendetta" }, { "id": "47447", "name": "Inception" }, { "id": "240344", "name": "La La Land" }, { "id": "20956", "name": "Black Swan" } ] } `

Development

Requirements

This project requires Python 3.10 or higher.
`bash

Core installation

pip install letterboxdpy

With example dependencies

pip install "letterboxdpy[examples]"
`

Examples

Example scripts demonstrating various features are available in the
examples/ directory. See examples/README.md for detailed usage instructions.

Linting

This project uses Ruff for linting and formatting, configured in
pyproject.toml. `bash

Check for issues

ruff check .

Auto-fix issues

ruff check --fix .

Format code

ruff format .
`

Testing

The test suite separates fast, deterministic offline unit tests from live external network tests (marked as
smoke).

Run Offline Unit Tests (Default)

Runs offline parser and model validation in ~0.5 seconds without network calls:
`bash python -m pytest `

Run Live Network Tests (Smoke Marker)

Executes tests against live Letterboxd pages:
`bash python -m pytest -m smoke `

Run All Tests

`bash python -m pytest -m "smoke or not smoke" `
[!NOTE]
Tests that require an authenticated Letterboxd session (e.g. test_auth.py) are automatically skipped if no valid .cookie file is present. This ensures the suite runs cleanly in CI environments without credentials.

Pre-commit Hooks

Pre-commit hooks automatically format code, check linting rules with Ruff, and validate commit title length before each commit:
  • Ruff & Ruff-Format: Runs on commit to check and auto-format staged files.
  • Commit Title Length: Validates that the first line of the commit message follows Conventional Commits and is at or under 62 characters (checked on commit-msg).
`bash

Install hooks for both pre-commit and commit-msg stages

pre-commit install --hook-type pre-commit --hook-type commit-msg

Run manually against all files

pre-commit run --all-files
`

CI Pipeline

GitHub Actions automatically runs linting and tests on every push and pull request against the
main branch, across all supported Python versions. See .github/workflows/ci.yml` for the full pipeline configuration. ---

Contributors

---

Star History

Star History Chart ---

License

MIT License — Free to use, modify, and share.
Chat with me