Skip to main content

APICache

A lightweight proxy-cache that sits between your app and a public API.

It helps avoid rate limits, enables reproducible experiments, and incrementally builds up datasets by caching structured responses — not raw HTTP.

Solving the frustration of API rate limits, inconsistent responses, and unreproducible workflows by caching structured API responses during development for reuse, graphing, and analysis.

Installation

pip install apicache

Behind the scenes:

  • All API responses are cached in a local SQLite store
  • Cached responses are versioned with timestamps and TTL-aware
  • The system gracefully falls back to live data when needed
  • Cached entries can be purged, inspected, or used to reconstruct state

Basic Usage

"""proxied-cache to UK govs Companies House API"""
from cache import APICache
import requests
import os

API_KEY = os.environ["COMPANIES_HOUSE_API_KEY"]
BASE_URL = "https://api.company-information.service.gov.uk"

def fetch_from_ch_api(url, params):
    """function to call on cache miss"""
    full_url = url.format(**{**params, "BASE_URL": BASE_URL})
    response = requests.get(full_url, auth=(API_KEY, ""))
    if response.status_code != 200:
        raise ValueError(f"Failed [{response.status_code}]")
    return response.json()

# define the cache object
cache = APICache(request_fn=fetch_from_ch_api, ttl=3600)

# make a request to the backend API
# cache miss will call the `fetch_from_ch_api` function
cache_hit, data = cache.request("{BASE_URL}/company/{company_number}", {"company_number": "12345678"})
print("Cache hit?", cache_hit)
print("Company:", data["company_name"])
print("Stats:", cache.stats())

Use Cases

  • Data scientists experimenting with public APIs
  • Reproducible research pipelines without rate-limit worries
  • Mockable APIs for CI/offline testing
  • API usage minimization or budgeting
  • Building incremental graphs or datasets over time

Examples

Google News RSS

import feedparser
from cache import APICache

def fetch_google_news(url, params):
    query = params["q"]
    full_url = f"https://news.google.com/rss/search?q={query}"
    return feedparser.parse(full_url)

cache = APICache(request_fn=fetch_google_news, ttl=86400)

hit, parsed = cache.request("https://news.google.com/rss/search?q={q}", {"q": "next high tide"})
for entry in parsed["entries"]:
    print(entry["title"])

GitHub API (with headers)

import requests
from cache import APICache

def fetch_github(url, params):
    full_url = url.format(**params)
    resp = requests.get(full_url, headers={"Accept": "application/vnd.github+json"})
    return resp.json()

cache = APICache(request_fn=fetch_github)

hit, user = cache.request("https://api.github.com/users/{username}", {"username": "torvalds"})
print("Name:", user["name"])

Extended usage

The cache indexs over url: str and params: dict, so any data may be cached via this method. Cache misses are resolved with the fetch_fn callback, which can custom process any actual interaction with the backend API.

Slow Fibonacci

Of course, you should explore other caching options like functools.lru_cache but it is sometimes useful to capture the cached data.

from cache import APICache
import time

def slow_fib(_, params):
    """Pretend this is a slow remote call."""
    n = params["n"]
    time.sleep(0.2)  # simulate latency
    if n <= 1:
        return n
    return fib_cache.request("fib", {"n": n - 1})[1] + fib_cache.request("fib", {"n": n - 2})[1]

fib_cache = APICache(request_fn=slow_fib)

for i in range(20):
    hit, result = fib_cache.request("fib", {"n": i})
    print(f"fib({i}) = {result} {'(cache hit)' if hit else '(computed)'}")

print("\nCache stats:", fib_cache.stats())

OpenAI library calls

In this example, we wrap the APICache in a simple object to proxy the OpenAI backend without losing the rich object responses defined in the OpenAI library.

NB: not fully tested

import openai
from openai.util import convert_to_openai_object
from cache import APICache
import hashlib
import json


class OpenAICache:
    def __init__(self, ttl: int = 86400, location: str = None):
        self.cache = APICache(request_fn=self._fetch_from_openai, ttl=ttl, location=location)

    def _fetch_from_openai(self, key: str, params: dict):
        if key == "openai:chat":
            response = openai.ChatCompletion.create(**params)
        elif key == "openai:image":
            response = openai.Image.create(**params)
        else:
            raise ValueError(f"Unsupported OpenAI API type in key: {key}")

        return response.to_dict()

    def chat(self, **params):
        """Cached chat completion"""
        hit, raw = self.cache.request("openai:chat", params)
        return convert_to_openai_object(raw), hit

    def image(self, **params):
        """Cached image generation"""
        hit, raw = self.cache.request("openai:image", params)
        return convert_to_openai_object(raw), hit

API Reference

cache = APICache(request_fn, ttl=3600, location="api-cache.sqlite")

cache.request(url, params) → (hit: bool, response: dict)
cache.read(url, params)
cache.write(url, params, response)
cache.has(url, params)
cache.clear_for_url("/some/api/prefix")
cache.prune_old_versions()
cache.stats() → dict

Metadata

Release files for apicache 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Built distribution (wheel)

Table of built distributions (wheels) for apicache 0.1.1
File Interpreter ABI Platform
apicache-0.1.1-py3-none-any.whl Python 3 none any Details

Release files / apicache-0.1.1-py3-none-any.whl

Download URL apicache-0.1.1-py3-none-any.whl
Size 6.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8d97fdd88585bc9e64c874e7ca38ee00a4e7a254144afe6dbbe1965b30ae2723
BLAKE2b-256 checksum
How to use checksums
7403baea428f382fbee3d3dfe02045fe3958558a233f87aad9c2e8bb8f8a7db9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.11.2

Release history Release notifications | RSS feed

This release

0.1.1 This release

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page