A simple FastAPI-based wrapper around the letterboxdpy library, exposing a handful of endpoints to fetch Letterboxd user data as JSON. Packaged with Docker for easy multi-architecture deployment.
Example: http://127.0.0.1:6996/user/saisamarth
Click to view JSON Responce
{
"username": "saisamarth",
"watchlist_length": "125",
"favorites": [
[
"Mulholland Drive",
"mulholland-drive"
],
[
"Heat",
"heat-1995"
],
[
"Nightcrawler",
"nightcrawler"
],
[
"A Bronx Tale",
"a-bronx-tale"
]
],
"stats": {
"Films": "380",
"This year": "21",
"Lists": "8",
"Following": "7",
"Followers": "8"
}
}
Returns basic profile data (watchlist length, favorites, stats).
Example: http://127.0.0.1:6996/user/saisamarth/following
Click to view JSON Responce
{
"following": [
"korosuke",
"BhimaMalgi",
"Guruprasad Pattar",
"jagadeesh11",
"capncook08",
"OmBevin",
"Vinay07"
]
}
Returns a list of usernames that the user is following.
Example: http://127.0.0.1:8000/user/saisamarth/followers
Click to view JSON Responce
{
"followers": [
"DeepakBaligar",
"BhimaMalgi",
"bott89",
"Guruprasad Pattar",
"jagadeesh11",
"capncook08",
"OmBevin",
"Vinay07"
]
}
Returns a list of usernames who follow the user.
Example: http://127.0.0.1:8000/user/saisamarth/films
Click to view JSON Responce
{
"films": [
[
"Thunderbolts*",
"thunderbolts"
],
[
"Mickey 17",
"mickey-17"
],
[
"The Godfather",
"the-godfather"
]
]
}
Returns an array of (title, slug) pairs for all watched films.
All endpoints run on port 6996 by default.
To drastically reduce scraping latency and load:
An in-memory key–value store with built-in TTL eviction—perfect for caching JSON responses without a schema or disk I/O overhead.
-
Connect to Redis via
redis.from_url(REDIS_URL)inapp.py. -
Wrap each endpoint in a
cached(key, ttl, fetch_fn)helper:
-
cache.get(key)returns existing JSON, avoiding a scrape. -
On miss, call
fetch_fn()(the slow Letterboxd scrape), thencache.set(key, json, ex=ttl).
- Default TTL is 300s (5 minutes), configurable per-endpoint.
| Change | Before | After |
|---|---|---|
| Response time reduced from 10.07 s to 73.47 ms -> 99.27% increase | ![]() |
![]() |
| Response time reduced from 3.92 s to 33.55 ms -> 99.14% increase | ![]() |
![]() |
git clone https://github.com/saisamarth21/letterboxd-api.git
cd letterboxd-api
fastapi
uvicorn[standard]
letterboxdpy
requests
beautifulsoup4
lxml
redis>=4.5.0
python -m uvicorn app:app --host 0.0.0.0 --port 6996 --reload
-
Swagger UI: http://localhost:6996/docs
-
ReDoc: http://localhost:6996/redoc
Click to view
# syntax=docker/dockerfile:1
FROM --platform=$BUILDPLATFORM python:3.11-slim
# Create app directory
WORKDIR /app
# Install runtime dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy source
COPY . .
# Expose the port your FastAPI app uses
EXPOSE 6996
# Default command
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "6996"]
# enable buildx once
docker buildx create --name multiarch --use
# if already enabled buildx
docker buildx use multiarch
# build & push for amd64, arm64, arm/v7
docker buildx build \
--platform linux/amd64,linux/arm64,linux/arm/v7 \
-t <username>/letterboxd-api:latest \
--push \
.version: "3.8"
services:
redis:
container_name: redis
image: redis:7-alpine
command: ["redis-server", "--save", "", "--appendonly", "no"]
ports:
- "6379:6379"
restart: unless-stopped
letterboxd-api:
container_name: letterboxd-api
image: saisamarth21/letterboxd-api:redis1.3
ports:
- "6996:6996"
environment:
REDIS_URL: "redis://redis:6379/0"
depends_on:
- redis
restart: unless-stoppeddocker-compose up -dYour API will now be accessible on port 6996 of the host machine.
Feel free to open issues or PRs in the GitHub repo. Enjoy!



