Josh

Building in the open

Displaying Letterboxd Like Counts on My Movie Reviews

I’ve been crossposting my Letterboxd reviews to this site for a while now. The posts link back to the original review, but I wanted to show engagement metrics the same way I do for Mastodon, Bluesky, and HackerNews posts. I run this static site on Jekyll so I want to load these in the visitor’s browser for efficiency and accuracy.

The Approach

Letterboxd doesn’t have a public API (though they have a private beta API), but the like count is embedded in the review page’s HTML. When you view a review, there’s an element with a data-count attribute holding the number:

data-likes-page="/joshbeckman/film/the-french-connection/likes/"
data-format="count"
data-count="1"
data-owner="joshbeckman"

So the plan was simple: fetch the review page, parse out that data-count value, and update the link text.

The Implementation

I added a loadLetterboxd() function alongside my existing social platform loaders. These all fire when the comments section scrolls into view, using an IntersectionObserver to avoid unnecessary requests on page load.

function loadLetterboxd() {
    var letterboxd = document.querySelector('.social-letterboxd');
    if (!letterboxd) {
        return;
    }
    var url = letterboxd.getAttribute('href');
    fetch(url)
        .then(response => response.text())
        .then(html => {
            var match = html.match(/data-count="(\d+)"/);
            if (match) {
                var likes = parseInt(match[1], 10);
                if (likes > 0) {
                    letterboxd.textContent = `${likes} ${pluralize(likes, 'like')} on Letterboxd`;
                }
            }
        })
        .catch(error => console.error('Error fetching Letterboxd review:', error));
}

The CORS Problem

This didn’t work. The browser blocked the request:

Fetch API cannot load https://letterboxd.com/... due to access control checks.

Letterboxd doesn’t set CORS headers allowing cross-origin requests from browsers. This is the same problem you hit with any site that hasn’t explicitly opted into being fetched by client-side JavaScript from other domains.

The Fix: A CORS Proxy

The solution is to route the request through a proxy that adds the appropriate headers. I tried a few options:

  1. corsproxy.io - Worked locally but blocks HTML content in production due to phishing concerns. Only JSON, XML, and CSV are allowed.

  2. allorigins.win - Supports HTML, but was painfully slow in my testing.

  3. cors.lol - Fast, supports HTML, and has a simple API. This is what I ended up using.

The CORS Proxies gist is a useful reference for finding working proxies, though the landscape changes frequently as services get abused and shut down.

The final implementation:

var url = letterboxd.getAttribute('href');
fetch(`https://api.cors.lol/?url=${encodeURIComponent(url)}`)
    .then(response => response.text())
    // ... rest unchanged

This adds a dependency on a third-party service, which isn’t ideal. If cors.lol goes away or starts blocking HTML, I could set up a Cloudflare Worker to proxy these requests, or fetch the data at Jekyll build time instead of client-side. For now, the simplicity of a hosted proxy wins.

The Result

Display of Letterboxd likes on a review
Display of Letterboxd likes on a review

Movie review posts now show like counts from Letterboxd right alongside the other social metrics. It’s a small addition, but it makes the social section feel more complete.

Keyboard Shortcuts

Key Action
o Source
e Edit
i Insight
r Random
h Home
s or / Search
Josh Beckman's Organization: https://www.joshbeckman.org/blog/practicing/displaying-letterboxd-like-counts-on-my-movie-reviews