DEV Community

Alex Spinov
Alex Spinov

Posted on

I Built 8 Python API Clients in One Day (Here's the Pattern)

Yesterday I published 8 Python API wrappers to GitHub. Each one took about 20 minutes. They all follow the same pattern.

Here's the template.

The Pattern

Every API client I build has:

  1. A single class
  2. A base URL
  3. A private _get() method
  4. Public methods that return clean dictionaries
  5. Under 100 lines total
import requests

class MyAPIClient:
    BASE = "https://api.example.com/v1"

    def __init__(self, api_key=None):
        self.api_key = api_key

    def _get(self, endpoint, **params):
        if self.api_key:
            params["apikey"] = self.api_key
        resp = requests.get(f"{self.BASE}/{endpoint}", params=params)
        resp.raise_for_status()
        return resp.json()

    def get_something(self, query):
        data = self._get("search", q=query)
        return [{"title": r["title"], "url": r["url"]} for r in data["results"]]
Enter fullscreen mode Exit fullscreen mode

That's it. No complex abstractions. No decorator magic. No metaclasses.

The 8 Clients

Client API Auth Lines
CoinGecko Crypto prices None 45
Open-Meteo Weather None 55
Hacker News HN stories None 40
Wikipedia Articles None 50
Alpha Vantage Stocks Free key 65
SEC EDGAR SEC filings None 60
FRED Economic data Free key 50
NewsAPI News search Free key 45

All of them follow the exact same pattern. All are under 100 lines.

Why This Approach Works

1. No dependencies beyond requests

Most API clients on PyPI pull in 5-10 dependencies. Mine need exactly one: requests. Less to install, less to break.

2. Read the entire codebase in 2 minutes

When a library is 45 lines, you can read every line of code before using it. No hidden surprises.

3. Easy to modify

Need to add a method? Copy the pattern, change the endpoint. Need to add caching? Wrap _get(). Need rate limiting? Add a sleep to _get().

4. No abstraction leaks

Returning plain dictionaries means no custom objects to learn, no serialization issues, no repr surprises. Just data.

The Anti-Pattern: Over-Engineered Clients

I've seen API clients with:

  • Custom exception hierarchies (10+ exception classes)
  • Async + sync versions of every method
  • Built-in caching with 5 configuration options
  • Response objects with 20+ methods
  • Connection pooling with retry logic

For what? Making GET requests to a REST API.

When You DO Need More

The simple pattern breaks down when:

  • You need to handle authentication flows (OAuth, refresh tokens)
  • You're making hundreds of requests per second
  • The API has complex pagination
  • You need webhook handling

For everything else: 50 lines is enough.

The Unified Client

I also built a single client that combines all 8 APIs:

from data_fetcher import DataFetcher
df = DataFetcher()

df.crypto("bitcoin")       # CoinGecko
df.weather(40.71, -74.01)  # Open-Meteo
df.hn_top(5)               # Hacker News
df.stock("AAPL")           # Alpha Vantage
df.news("AI")              # NewsAPI
Enter fullscreen mode Exit fullscreen mode

One import, all the data.


What APIs would you want a simple Python client for?

All clients: github.com/Spinov001

Top comments (0)