πŸ”₯ Free Python Library That Caches, Retries & Deduplicates API Calls for You

:high_voltage: Stop Writing the Same Data-Fetching Code Over and Over in Python

One library handles caching, retries, and deduplication β€” so you can stop reinventing the wheel every project.

One install. Zero dependencies. Works with any async Python app.

If you’ve ever built something in Python that talks to an API β€” weather data, user profiles, stock prices, anything β€” you’ve written the same boring code a dozen times: fetch data, store it somewhere, check if it’s still fresh, fetch again if it’s not. PyStackQuery does all of that automatically, so you write one line instead of twenty.


🧠 What Even Is This? β€” The 30-Second Explanation

Think of it like a smart middleman between your Python app and any data source (API, database, whatever).

Without it, every time your app needs data, it goes straight to the source β€” even if it already grabbed the exact same data 2 seconds ago. That’s slow, wasteful, and annoying to code around.

With PyStackQuery, the flow works like this:

  1. First request β†’ fetches from the real source, stores a copy
  2. Second request (same data) β†’ instantly returns the stored copy. Zero wait.
  3. Background β†’ quietly checks if the stored copy is outdated, refreshes it behind the scenes

That’s called Stale-While-Revalidate (SWR) β€” show what you have immediately, update silently in the background. The user never waits. The data stays fresh. Everybody wins.

:light_bulb: The β€œwhy should I care” version: Your app feels faster because it shows cached data instantly, then updates without the user noticing. No loading spinners. No repeated API calls. No wasted bandwidth.

πŸ”₯ Why This Exists β€” The Problem It Solves

Every Python dev who works with APIs ends up writing something like this:

cache = {}
pending = {}
lock = asyncio.Lock()

async def get_user(user_id):
    key = f"user_{user_id}"
    async with lock:
        if key in cache:
            return cache[key]
        if key in pending:
            return await pending[key]
        task = asyncio.create_task(fetch_user(user_id))
        pending[key] = task
    try:
        result = await task
        cache[key] = result
        return result
    finally:
        del pending[key]

That’s 15 lines of boilerplate β€” manual cache dictionary, manual lock, manual deduplication, manual cleanup. And you rewrite some version of this for every project.

With PyStackQuery, the same thing becomes:

user = await client.fetch_query(
    QueryOptions(("user", user_id), lambda: fetch_user(user_id))
)

Two lines. Caching, deduplication, retries, background refresh β€” all handled automatically.

That’s the entire pitch. Less code, fewer bugs, same result.

βš™οΈ What It Actually Does β€” Feature Breakdown
Feature What It Means (Plain English)
SWR Caching Shows stored data instantly, refreshes in the background. Your app feels fast even when the network is slow
Request Deduplication If 10 parts of your app ask for the same data at the same time, only ONE actual request goes out. The rest share the result
Automatic Retries If a request fails (network blip, server error), it retries with increasing wait times instead of just crashing
Dual-Tier Cache (L1 + L2) L1 = fast memory cache (lives in RAM, dies when app restarts). L2 = plug in Redis or SQLite so your cache survives restarts and works across multiple app instances
Sync-Safe Observer API The subscription/notification system works synchronously β€” safe for desktop apps (Tkinter, etc.) without blocking the UI loop
Query Keys Every piece of data gets a unique label (like ("user", "123")). The library uses these labels to organize, cache, and deduplicate everything
Cache Invalidation Tell the library β€œthis data is outdated, go get a fresh copy” β€” and it handles the rest
Zero Dependencies The core library needs nothing else installed. No dependency chain, no version conflicts, no bloat
πŸ› οΈ How to Get Started β€” 3 Minutes, No Experience Needed

Step 1 β€” Install it:

pip install pystackquery

Requires Python 3.11+ (uses modern Python generics for clean type hints).

Step 2 β€” Basic usage:

import asyncio
from pystackquery import QueryClient, QueryOptions

client = QueryClient()

async def fetch_user(user_id: int) -> dict:
    # Your fetch logic β€” could be any API call
    async with aiohttp.ClientSession() as session:
        async with session.get(f"https://api.example.com/users/{user_id}") as resp:
            return await resp.json()

async def main():
    # First call β†’ hits the API, caches result
    user = await client.fetch_query(
        QueryOptions(
            query_key=("user", "123"),
            query_fn=lambda: fetch_user(123)
        )
    )

    # Second call β†’ returns instantly from cache
    user_again = await client.fetch_query(
        QueryOptions(
            query_key=("user", "123"),
            query_fn=lambda: fetch_user(123)
        )
    )

asyncio.run(main())

That’s it. First call fetches from the API. Second call returns from cache β€” no network request, no delay.

Step 3 β€” Explore further:

The GitHub repo includes a docs/ folder with deeper documentation, an examples/ folder with ready-to-run code, and a full test suite.

πŸ€” Who Is This For? β€” Use Cases
If You’re Building… How This Helps
FastAPI backend Cache expensive database queries or third-party API calls. Deduplication prevents redundant requests under load
CLI tools that talk to APIs Stop hitting rate limits by caching responses. Retries handle flaky connections automatically
Desktop apps (Tkinter, PyQt) Sync-safe observer API means your UI doesn’t freeze while data loads in the background
Data pipelines L2 cache (Redis/SQLite) means processed data survives restarts. No re-fetching everything from scratch
Any async Python project If you’re tired of writing if key in cache logic and asyncio.Lock() boilerplate β€” this replaces all of it
πŸ“Š Quick Context β€” Where This Fits

For anyone coming from the JavaScript/React world: this is essentially TanStack Query (React Query) or SWR by Vercel β€” but for Python.

Concept JS Equivalent PyStackQuery
Smart data fetching + caching TanStack Query / SWR :white_check_mark: Same pattern
Stale-While-Revalidate Built into SWR :white_check_mark: Built in
Request deduplication Both libraries do this :white_check_mark: Built in
Background refresh Both libraries do this :white_check_mark: Built in
L2 persistent cache Custom adapters needed :white_check_mark: Redis/SQLite built in

For non-JS devs: Think of it as a caching library that’s smart enough to know when data is stale and refreshes it without you telling it to. That’s the whole concept.

:high_voltage: Heads up: This is a newer project β€” clean codebase, active development, but still early. If you’re using it for something production-critical, test thoroughly.


:high_voltage: Quick Hits

Want Do
:bullseye: Install it pip install pystackquery
:package: Source code GitHub β€” PyStackQuery
:snake: Python version 3.11+ required
:floppy_disk: Persistent cache Plug in Redis or SQLite as L2
:stopwatch: Time to first result ~3 minutes from install to working cache

Less boilerplate. Smarter caching. Your async Python apps just got a serious upgrade.

1 Like