Skip to main content

A letterboxd webscraper

Project description

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 Weekly DOM Health Check


Installation

From PyPI

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

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:

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:

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

from letterboxdpy.user import User
user_instance = User("nmcassa")
print(user_instance)
Click to expand User object response
{
  "username": "nmcassa",
  "url": "https://letterboxd.com/nmcassa",
  "id": 1500306,
  "is_hq": false,
  "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"
  },
  "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

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
{
  "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

from letterboxdpy.search import Search
search_instance = Search("V for Vendetta", 'films')
print(search_instance.get_results(5))
Click to expand Search object response
{
  "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

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
{
  "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

from letterboxdpy.members import Members
members_instance = Members(max=5)
print(members_instance.members)
Click to expand Members object response
[
  "schaffrillas",
  "kurstboy",
  "demiadejuyigbe",
  "zoerosebryant",
  "jaragon23"
]

Films Object

Explore the file | Functions Documentation

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
{
  "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

from letterboxdpy.watchlist import Watchlist
watchlist = Watchlist("nmcassa")
watchlist.movies # fetch movies (lazy loading)
print(watchlist)
Click to expand Watchlist object response
{
  "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.

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}")

User Settings

Explore the file

The UserSettings module allows reading and updating user profile and notification settings. Requires an authenticated session.

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})

Development

Requirements

This project requires Python 3.10 or higher.

# 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.

# Check for issues
ruff check .

# Auto-fix issues
ruff check --fix .

# Format code
ruff format .

Testing

Run the full test suite using pytest:

python -m pytest tests

Or run a specific test file:

python -m pytest tests/test_movie.py

[!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 run Ruff and the test suite before every commit, ensuring no broken or non-compliant code enters the repository.

# Install hooks (one-time setup)
pre-commit install

# 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 Python 3.10, 3.11, and 3.12.

See .github/workflows/ci.yml for the full pipeline configuration.


Contributors


License

MIT License — Free to use, modify, and share.


Stargazers over time:
Stargazers over time
Built by the Letterboxdpy community

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

letterboxdpy-6.5.6.tar.gz (170.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

letterboxdpy-6.5.6-py3-none-any.whl (91.7 kB view details)

Uploaded Python 3

File details

Details for the file letterboxdpy-6.5.6.tar.gz.

File metadata

  • Download URL: letterboxdpy-6.5.6.tar.gz
  • Upload date:
  • Size: 170.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for letterboxdpy-6.5.6.tar.gz
Algorithm Hash digest
SHA256 823fb997f8383cc0069d8ffefd4803f6d5729ab22bfd58e575a8d49afc7848e9
MD5 ada0575a1f47c0decf31ae004aad810c
BLAKE2b-256 a45a039e6070d556aad9c2a29ed67f32b4f67d0349677532aee18d9c3f1f01c6

See more details on using hashes here.

File details

Details for the file letterboxdpy-6.5.6-py3-none-any.whl.

File metadata

  • Download URL: letterboxdpy-6.5.6-py3-none-any.whl
  • Upload date:
  • Size: 91.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for letterboxdpy-6.5.6-py3-none-any.whl
Algorithm Hash digest
SHA256 c21425d1e4c371d7c5e8df8a6a7720997335b3868a16a45dc96ce76107fd3bce
MD5 f6e34575da70809781f496263f809485
BLAKE2b-256 3c0e47810802297b79a2f570c26f748059b5c02af02ad8b5b4feb7c093eb6fcc

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page