Public
Likeletterboxd
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v25View latest version
A simple API to fetch the most recent diary entry from a Letterboxd user's diary page.
This API scrapes the Letterboxd diary page for user "pinjasaur" and returns the most recent film entry in a clean JSON format. Perfect for displaying your latest watched movie on a personal website or blog.
GET /
Returns the most recent diary entry as JSON.
{ "title": "The Naked Gun: From the Files of Police Squad!", "year": 1988, "rating": 4, "watchDate": "2026-01-13", "posterUrl": "https://your-val-town-url.web.val.run/poster?url=https%3A//letterboxd.com/film/the-naked-gun-from-the-files-of-police-squad/image-300/", "filmUrl": "https://letterboxd.com/pinjasaur/film/the-naked-gun-from-the-files-of-police-squad/", "scrapedAt": "2026-01-15T21:35:08.392Z" }
- title: Film title (string | null)
- year: Release year (number | null)
- rating: Star rating from 0.5 to 5 stars (number | null)
- watchDate: Date watched in YYYY-MM-DD format (string | null)
- posterUrl: High-resolution poster image URL (string | null)
- filmUrl: Link to the film's Letterboxd page (string | null)
- scrapedAt: ISO timestamp of when the data was scraped (string)
fetch('https://your-val-town-url.web.val.run/')
.then(response => response.json())
.then(data => {
console.log(`Latest watched: ${data.title} (${data.year})`);
console.log(`Rating: ${data.rating}/5 stars`);
});
<div id="latest-movie"></div> <script> async function loadLatestMovie() { try { const response = await fetch('https://your-val-town-url.web.val.run/'); const movie = await response.json(); document.getElementById('latest-movie').innerHTML = ` <div class="movie-card"> <img src="${movie.posterUrl}" alt="${movie.title}" width="150"> <h3>${movie.title} (${movie.year})</h3> <p>Rating: ${'★'.repeat(movie.rating)}${'☆'.repeat(5-movie.rating)}</p> <p>Watched: ${movie.watchDate}</p> <a href="${movie.filmUrl}" target="_blank">View on Letterboxd</a> </div> `; } catch (error) { console.error('Failed to load latest movie:', error); } } loadLatestMovie(); </script>
The API includes caching headers:
- Cache-Control:
public, max-age=300(5 minutes) - CORS: Enabled for all origins
The API returns appropriate HTTP status codes:
- 200: Success
- 404: No diary entries found
- 500: Server error (network issues, parsing errors, etc.)
Error responses include a descriptive message:
{ "error": "No diary entries found", "message": "The diary appears to be empty or the page structure has changed." }
- Built with TypeScript on Val Town
- Scrapes HTML from
https://letterboxd.com/pinjasaur/diary/ - Uses regex patterns to extract structured data
- Handles various edge cases and fallbacks
- Returns high-resolution poster images (300px width)
- Only works for public Letterboxd profiles
- Currently hardcoded to user "pinjasaur"
- Dependent on Letterboxd's HTML structure (may break if they change their layout)
- Rate limited by Letterboxd's servers
- No authentication or private diary support
Potential improvements could include:
- Support for multiple users via query parameters
- Pagination to get multiple recent entries
- Additional metadata extraction (genres, director, etc.)
- Webhook support for real-time updates
- Better error handling and retry logic