How to Reduce OpenAI API Costs With a Redis Cache

Every identical prompt hitting the LLM is a paid call you didn't need to make. A Redis cache-aside layer - check the cache first, call the LLM only on a miss, store the result with a TTL - cuts cost proportional to your cache hit rate with about five lines of code.

AI Engineerllmrediscaching

Why LLM costs pile up fast

OpenAI charges per token, and real apps repeat the same prompts constantly. A customer-support bot gets asked "what's your refund policy?" dozens of times per hour. A documentation assistant re-embeds the same query. Each call pays the same price whether it's the first or the five-hundredth time.

The fix is a prompt-level cache: before calling the LLM, check Redis for a stored response keyed on the prompt. Return it directly on a hit. Call the LLM only on a miss, then store the result for next time.

The cache-aside pattern

import redis
import requests

r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
LLM_URL = "https://api.openai.com/v1/chat/completions"

def ask(prompt: str) -> str:
    key = f"llm:{prompt}"

    cached = r.get(key)
    if cached:
        return cached                   # cache hit - no LLM call

    response = requests.post(
        LLM_URL,
        headers={"Authorization": f"Bearer {OPENAI_API_KEY}"},
        json={
            "model": "gpt-4o-mini",
            "messages": [{"role": "user", "content": prompt}],
        },
        timeout=60,
    )
    response.raise_for_status()
    answer = response.json()["choices"][0]["message"]["content"]

    r.set(key, answer, ex=86400)        # store with 24h TTL
    return answer

r.get(key) returns None on a miss, so the check is a single condition. On a miss, r.set(key, answer, ex=86400) stores the response for 24 hours - after which Redis evicts it automatically, so stale model answers don't linger forever.

Picking the right TTL

Measuring the hit rate

Track a counter per request to see how often the cache is saving you a call:

llm_calls = 0

def ask(prompt: str) -> str:
    key = f"llm:{prompt}"
    cached = r.get(key)
    if cached:
        return cached
    global llm_calls
    llm_calls += 1
    # ... LLM call and store ...

Expose it on a /metrics or /counter endpoint. If hit rate is low, the prompts are too varied - semantic caching (embed the prompt, look up the nearest stored vector) handles that case at the cost of more infrastructure.

Going further

Exact-string keys are a good start but won't catch near-duplicates ("what's your return policy?" vs "what is your return policy?"). Production systems often layer:

  1. Exact match in Redis - zero-latency, free.
  2. Semantic similarity lookup - embed the prompt, search a vector store for a cached embedding within a cosine-similarity threshold.
  3. Provider-level prompt caching - OpenAI and Anthropic both cache long system prompts at the API layer automatically, reducing token cost for the static prefix of every request.

Start with exact-match Redis caching - it is free, simple, and often sufficient for high-traffic question-answering endpoints.

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

How do I cache OpenAI API responses to reduce costs?

Use a Redis cache-aside layer: before calling the API, GET a key derived from the prompt. On a hit, return the cached string. On a miss, call the LLM, store the result with SET ... EX <ttl>, and return it. Identical prompts never hit the API again until the TTL expires.

What TTL should I use for caching LLM responses?

24 hours (86400 seconds) works well for FAQ and static content where answers don't change often. Use shorter TTLs or skip caching for dynamic or personalised responses. Always set some TTL so stale answers eventually expire.

What is semantic caching for LLMs?

Semantic caching embeds the incoming prompt into a vector and looks up the nearest stored embedding rather than doing an exact string match. It catches near-duplicate questions that differ only in wording. It requires a vector store and is more complex than Redis exact-match caching - add it when the exact-match hit rate is too low.

How do I reduce OpenAI API costs?

The highest-impact levers: cache identical responses (a Redis cache-aside layer avoids paying for repeat prompts), use the smallest model that meets quality, cap output with max_tokens, trim prompts and system messages, batch where possible, and add semantic caching for near-duplicate questions. Caching usually wins first because repeat traffic is free.

Keep learning

Restore a Broken LLM API IntegrationAI/ML projectHarden an LLM Pipeline Against API FailuresAI/ML projectParse JSON From an LLM (Strip Markdown Fences)AI/ML projectAI/ML roadmapStep by step to hiredAI/ML interview questionsSTAR answersAll AI/ML projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →