2024 is about to close… just in time for the next cryptocurrency bull run.
If you look at previous cycles, we’re right on schedule (cha-ching). That means all things crypto-related are about to trend upward for the next few months. There’s no better time to write a Python API wrapper for the next-gen crypto data aggregator: CoinGecko.com’s v3 API.
Why?
The most important question a software developer can be asked is: “Why did you build this?” Here’s why:
-
No official Python SDK
-
To play with new (and old) Python tooling
-
For… fun?
Let’s talk about it.
No official SDK
When a service offers a public API, they typically provide an SDK or wrapper in popular programming languages. It simplifies how your application interacts with the API. CoinGecko, however, doesn’t offer an official SDK, and that’s understandable. Their API just returns aggregated data responses—nothing too fancy.
If you search GitHub for “coingecko” and filter by “Python,” the most reputable wrapper you’ll find has 1k+ stars. Unfortunately, the last PR was merged over two years ago, which was my first signal to potentially build something new and improved.
While two years may not seem like much, CoinGecko has since released new API routes and features that developers would likely want in an SDK.
Edit: As I’m writing this, I just noticed the maintainer merged a PR from June that includes the latest missing routes. LMAO. Oh well… I’m continuing with this post regardless—it was a great learning experience and scratched my coding itch (especially since I’ve been focused on building our data platform at my day job).
Pivoting to BirdEye.so SDK
I stumbled across another crypto data aggregator: BirdEye.so. Just like CoinGecko, but with slightly different features—and again, no Python SDK. So I reused a lot of the same wrapper logic.
Here’s how I like to structure my API wrappers:
- Each API resource gets its own class
- Each class encapsulates all actions related to that resource
- Have a single entry point for all HTTP requests

I also like creating a thin abstraction around HTTP libraries to avoid try/catch fatigue when using raise_for_status()
(e.g., with the requests
library). For small codebases, that’s probably fine. But for anything larger,
having a single HTTP entry point helps keep things DRY and maintainable.
One feature this project currently lacks is domain objects. Returning raw HTTP responses kind of defeats the purpose of a wrapper. A solid SDK should provide typed and structured domain models to simplify downstream use.
You can view the project here.
New and old Python tools
I’ve seen Python tooling come and go. A few years back, Poetry shook things up with .toml
files for managing dependencies,
project settings, and build config in one place. I liked it, as it was cleaner than having .rc files littered everywhere.
Dependency installs were fast enough and worked out of the box. I never once thought, “Dang, that install took forever.”
Of course, install speed in CI environments still depends on your cache setup.
But as always, the ecosystem craved something faster.
And of course—it had to be written in Rust. Enter Astral.sh, who built uv
: a blazing-fast Python
package manager backed by Rust.
Since Python tooling is something I enjoy exploring, I gave uv
a try. For a small project like python-coingecko, the experience was solid.
One thing I appreciated was the Python version management. Just drop a .python-version file into your project and you’re good to go. No need for complex shell commands or verbose version switching tools. Simple and clean.
I’ll close this out by mentioning uv
has reached “Adopt” level on Thoughtworks technology radar…so…yeah, go fast!