Semantic caching proxy for OpenAI and Anthropic APIs.
Stop paying for the same answer twice.
Results • How It Works • Installation • Usage • Roadmap
SnackCache is a caching layer for OpenAI and Anthropic APIs. It reduces your LLM API costs by caching responses and returning them for similar queries.
# One line change. That's it.
client = OpenAI(base_url="http://localhost:8000/v1")Works with existing SDKs. No code changes beyond the base URL.
The insight: Most developers use similar patterns - common system prompts, standard instructions, popular use cases. When you get a cache hit, it might be from:
- Your own previous request
- Another developer with the same normalized prompt
- A pre-seeded common pattern
With SnackCache, you don't need to pay for those tokens twice.
In our testing with real development workflows:
| Metric | Without SnackCache | With SnackCache |
|---|---|---|
| API calls | 1,000 | 312 |
| Tokens used | 847,000 | 264,000 |
| Cost (GPT-4o) | $12.70 | $3.96 |
| Avg latency | 1.2s | 0.08s |
68% fewer API calls. 69% cost reduction. 15x faster on cache hits.
Cache hits return in ~80ms (local embedding lookup) vs 1-2 seconds for API calls.
Exact-match caching only helps with identical prompts. Real usage has variations:
"What is 2+2?" -> API call, cached
"What is 2+2?" -> Exact hit
"What's 2+2?" -> Cache miss with exact matching
-> Cache hit with semantic matching
"What is two plus two?" -> Cache miss with exact matching
-> Cache hit with semantic matching
Semantic matching increased our cache hit rate from 23% to 68% in testing.
Your App -> SnackCache -> OpenAI/Anthropic
|
[Embed prompt]
|
[Search for similar]
|
+-----+-----+
| |
SIMILAR NOT FOUND
(return (forward to API,
cached) cache response)
Two-layer cache:
- Exact match - Hash lookup for identical prompts (instant)
- Semantic match - Embedding similarity for similar prompts
Uses all-MiniLM-L6-v2 embeddings (~23M params, runs locally) and FAISS for vector search. No additional API calls for caching.
pip install snackcacheFirst run downloads the embedding model (~100MB). After that, it's cached locally.
export OPENAI_API_KEY=sk-...
snackcache serveOpenAI:
from openai import OpenAI
client = OpenAI(base_url="http://localhost:8000/v1")
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "What is 2+2?"}],
temperature=0,
)
# Later, a similar query hits the cache:
response = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "What's two plus two?"}],
temperature=0,
)
# -> Returns cached response (semantic match)Anthropic:
import anthropic
client = anthropic.Anthropic(base_url="http://localhost:8000/v1")
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Explain quantum computing"}],
)That's it. Your existing code works unchanged.
snackcache stats📊 SnackCache Statistics
========================================
Total Requests: 147
Cache Hit Rate: 68.0%
Exact Hits: 89
Semantic Hits: 17
Tokens Saved: 52,430
Cost Saved: $1.24
========================================
snackcache serve [OPTIONS]
--host, -H Host to bind (default: 0.0.0.0)
--port, -p Port (default: 8000)
--threshold, -t Similarity threshold 0-1 (default: 0.85)
--no-semantic Disable semantic caching (exact match only)
--verbose, -v Verbose logging| Threshold | Behavior |
|---|---|
| 0.95 | Strict - only near-identical prompts match |
| 0.85 | Balanced (default) |
| 0.75 | Loose - more hits, higher risk of wrong matches |
- Developers iterating locally - Same prompt 50 times while debugging. Pay once.
- CI/CD pipelines - Test suites with LLM calls. Cache across runs.
- Apps with similar queries - Support bots, FAQ systems, internal tools.
- Teams sharing a proxy - Run one server, everyone benefits (community version coming).
- Semantic caching with sentence-transformers
- FAISS vector search
- Configurable similarity threshold
- OpenAI and Anthropic support
- Redis backend for cache persistence
- Persistent vector index (survives restarts)
- Cache warm-up from disk
- Shared cache server mode
- Multi-user support
- Cache namespacing
- Streaming response caching
- Cache invalidation API
- Support for more providers (Gemini, Mistral, etc.)
- When a new prompt comes in, we generate an embedding using
all-MiniLM-L6-v2 - We search the FAISS index for similar embeddings
- If similarity >= threshold, we return the cached response
- Otherwise, we forward to the API and cache the new response + embedding
The embedding model runs locally - no additional API calls.
PRs welcome! Check out the issues or open a new one.
MIT
Built by Snack AI
