LichessNET: My first real open source project


The moment the COVID-19 pandemic started causing lockdowns in March 2020, I—an chess enthusiast—found myself needing to play online. Although I had already used lichess.org for a long time by then, this was the moment I really had to dive deep into the platform. Lichess saw record-breaking numbers of players, and the legendary Lichess Bundesliga was born amongst many other features.

Fast-forward a few years: in 2024, I started working on a small piece of software for Lichess. I wanted to generate custom puzzles from my own games. That’s what led me down the rabbit hole of handling the Lichess API—and from there, the idea emerged: why not build a full Lichess API wrapper in C#?

Just for the sake of enhancing my coding skills, I wanted to make accessing the API a lot easier. And honestly? This journey is driving me slightly insane. But more on that later.


Why Lichess?

Lichess is free, open-source, and impressively transparent. That’s always drawn me in. Sure, my account doesn’t show many games—maybe 30 or so—but that’s only because I used Lichess for ages without logging in. Trust me, I’ve known and loved this platform for a long time.

Lichess feels like two worlds in one:

  • The first world is what everyone sees—lichess.org itself. A stunning, smooth, and intuitive interface. It’s elegant and inviting.

  • The second world lies hidden deep within the vaults of GitHub. Here, a crew of brilliant developers works tirelessly to keep the whole machine running. These are the unsung heroes—the programmers, the coders. Among them, a mythical figure seems to echo like a ghost in the community: @thibault.

These people built the colossal Lichess API—a kind of bridge between developers and the platform. If you want to make apps, tools, or integrations with Lichess, this is where you go. And yet, this magical bridge is… a bit chaotic.


About Wrappers

Wrappers are libraries (basically little bundles of code that can be reused easily) that manage API requests for you. Instead of manually crafting URLs and constructing HTTP request blocks, you can just call something like GetProfile("thibault") and boom—you get the result. But don’t be fooled by the simplicity: someone has to take the long, steep road of building the wrapper first, so others can enjoy the elevator ride.

I decided I wanted to be one of those builders. I chose C# as the language, because:

  • It’s versatile.
  • It’s beginner-friendly but also supports complex, large-scale applications.
  • And honestly, it’s so much better than all the Java we’re stuck learning at university.
  • And it’s my favorite language. I know I have some haters for that, but I really don’t care. I just like the syntax :P

Decoding the API

Let’s walk through an example—checking whether a user is online. Who better to use than the legend himself: thibault (the creator of Lichess)

[{"name":"thibault","flair":"symbols.rainbow-flag","patron":true,"id":"thibault","online":true}]

Not too bad, right? This is JSON, a format designed to be human-readable and used to transmit data on the web. But here’s the thing: computers aren’t human. They don’t understand JSON unless you teach them how.

So, in C#, we define a class to match this structure:

class Status
{
    public string Name   { get; set; }
    public string ID     { get; set; }
    public string Flair  { get; set; }
    public bool Patron   { get; set; }
    public bool Online   { get; set; }
}

Each property matches a key in the JSON object. Once this mapping is done, your program can easily parse and work with the data. Most programming languages offer built-in tools for this, and C# is no exception.


But Here’s the Catch…

Lichess has a huge amount of endpoints, and each one returns data in slightly different formats. This means you’ll need a structure for each response—a fun but exhausting exercise in patience and precision.

Let me give you a prime example of why this drives me crazy: Lichess sends data about users differently based on context.

  • A Lichess User might include full profile information.
  • An Opponent includes just the username, user ID, and maybe a rating.
  • A Game Player might also have a title or other game-specific fields.

These are all different representations of the same person. They vary slightly, and converting between them often leads to information loss. I’ve ended up with so many different user-related classes that I’m losing track of them. What should I do? Help me…


Okay, I’m Complaining a Lot…

I know. And it probably sounds worse than it is. There are good solutions for this mess—using parent-child class structures, implicit or explicit conversions, handling null values, and more advanced programming patterns.

Or, you could just use something I didn’t even realize existed at first: an OpenAPI specification of all the endpoints. Instead of manually crafting every single model and structure, I could’ve simply run a code generator with that spec file. It would’ve saved me a lot of time and headaches.

Lesson learned.


Spreading the Word

Then came the ultimate step: trying to get Lichess to recognize my wrapper as a proper open-source contribution.

So, I opened an issue on the official Lichess API GitHub repository:

Added LichessNET to client list
I made this Lichess API wrapper, and even though it doesn’t yet support all endpoints, it already supports many important ones.
This is just a proposal. If you don’t think this should be included, please tell me what to change—I’d love to keep improving it until it’s actually useful.
Thanks.

The reply came from none other than @thibault himself:

I’ll add it but first, please choose a license

Totally fair. So, I licensed the project under GNU AGPL v3.0, meaning it’s free to use, whether privately or commercially, but any changes to the code must also be shared under the same terms.


People Joined In

And then, something awesome happened: people actually started to notice.

The repository got its first stars, and even a couple of issues and pull requests. A few developers chimed in with suggestions, bug reports, or just kind words. Knowing others are using—or at least noticing—what you’ve made? That’s an incredible feeling.

It’s still far from complete. There are dozens of endpoints left to support, and countless edge cases to handle. But it’s out there. It’s alive.

And that’s just the beginning.


Current state

I’m currently not that active on the project anymore. But it happens about once a month that someone is tuning in and is sharing their code when it comes to a new feature they implemented and wanted it to be in a place where people could find it.

I plan to pick up on it at some point though.

But it’s out there for everyone to use already. Have fun.


This text has been spell checked and grammar checked by AI.