Introduction
In our last lesson, we engineered a robust, production-ready LLM API endpoint using FastAPI, complete with structured schemas and sophisticated error handling. That gives us a reliable "front door" to our model. However, in a real-world system with many users, just having a working endpoint isn't enough. Every call to an LLM costs money and takes time. Uncontrolled access can quickly lead to budget overruns, slow performance, and even service outages.
Today, we will address this by implementing two of the most critical strategies for managing a production service: caching and rate-limiting. Your goal is to learn how to implement both exact and semantic caching to reduce redundant API calls, and how to apply rate-limiting to protect your API from abuse and ensure fair usage. Mastering these techniques is fundamental to building scalable, cost-efficient, and performant LLM applications—a core competency for an elite AI engineer.
Part 1: Caching for Cost and Performance
Every time your API calls an LLM, you incur costs in latency, API fees (tokens), and compute power. Caching is the strategy of storing the results of expensive operations and returning the cached result when the same (or a similar) input occurs again.
Caching Techniques for LLM Applications — Part 1: Exact ...
To understand the fundamental importance of caching in LLM systems, let's start with a clear overview of the problem and the two primary caching solutions.
Please read the first three sections: 'Why caching matters for LLM systems', 'Exact-match (Key-Value) caching', and 'Semantic caching'. Focus on the 'How it works' and 'Advantages/Limitations' for each type.
As you just read, we have two main tools in our caching toolbox. Let's explore them in more detail.
Strategy 1: Exact-Match Caching
This is the simplest form of caching. It works just like a standard dictionary or key-value store. You create a unique key (typically a hash) from the exact prompt and its parameters (model name, temperature, etc.). If you see that exact key again, you return the stored response instead of calling the LLM.
Implementation:
This can be implemented with simple in-memory stores for single-process applications or more robust distributed stores like Redis for multi-server deployments.
Slash API Costs: Mastering Caching for LLM Applications
The video 'Slash API Costs' provides a great practical demonstration of implementing a simple in-memory cache and a more persistent SQLite cache within a LangChain application. This will make the concept of exact-match caching concrete.
Watch from 01:33 to 10:39. Pay attention to how a simple cache is implemented first with cachetools, then integrated into LangChain first as an in-memory cache, and finally as a persistent SQLite cache. Notice the dramatic difference in response time and token cost on the second identical request.
When to Use:
Exact-match caching is perfect for applications with highly templated or repeated prompts, such as classification tasks, deterministic agent tool outputs, or standardized data extraction queries.
Limitations:
Its major drawback is its brittleness. Any minor change to the prompt—a different word, an extra space, or a typo—results in a cache miss. This makes it less effective for conversational or free-form user queries.
Strategy 2: Semantic Caching
This is where things get interesting for LLM applications. Instead of matching the exact text of a prompt, semantic caching matches its meaning.

How it Works:
- When a new prompt arrives, you generate an embedding vector for it.
- You perform a similarity search (e.g., cosine similarity) against a vector database of previously cached prompt embeddings.
- If you find a stored prompt whose similarity score is above a certain threshold (e.g., > 0.99), you return its cached response.
- If not, you call the LLM, then store the new prompt's embedding and the LLM's response in the cache for future use.
This approach dramatically increases the cache hit rate for natural language queries.
Cutting LLM Costs with MongoDB Semantic Caching
Let's see a real-world implementation of semantic caching using LangChain and MongoDB Atlas. This video clearly demonstrates the setup and the power of this technique.
Watch from 07:09 to 17:02. Focus on how a semantic cache is configured (embedding model, vector store, similarity threshold) and how it successfully handles a slightly varied query ('where does Eric work?' vs. 'where does Eric work.') that an exact-match cache would miss.
A Critical Gotcha for RAG Systems:
Semantic caching seems like a silver bullet, but it has nuances, especially in complex systems like Retrieval-Augmented Generation (RAG)—a topic you're aiming to master.
In a RAG pipeline, the final prompt sent to the LLM is a combination of the user's query and the retrieved documents. Even if two user queries are semantically identical, the retrieval step might fetch slightly different documents (due to index updates, a different retrieval model, etc.). This changes the final composed prompt, leading to a semantic cache miss.
Cutting LLM Costs with MongoDB Semantic Caching
The same video explores this exact challenge. Understanding this is crucial for designing production-grade RAG systems.
Continue watching from 17:02 to 28:50. The key insight here is understanding why a semantically similar query like 'who is the mayor of San Francisco' vs. 'the mayor of San Francisco is whom' results in a cache miss. It's because the retrieved context documents were different, leading to a different final prompt embedding.
A Hybrid Approach:
For a production system, the most effective strategy is often a hybrid one:
- First, check an exact-match cache (like Redis). It's extremely fast and has zero risk of a false positive.
- If that's a miss, then perform a search in the semantic cache.
- If that's also a miss, call the LLM and populate both caches.
Part 2: Rate-Limiting for Stability and Fairness
While caching reduces the number of LLM calls, rate-limiting controls the frequency of those calls. It's a crucial mechanism to protect your API from being overwhelmed, manage costs, ensure fair resource allocation among users, and prevent abuse.

Let's dive deeper into the server-side implementation and best practices.
API Rate Limits Explained: Best Practices for 2025
This article provides a thorough explanation of rate-limiting concepts, algorithms, and best practices specifically tailored for LLM applications.
Please read the sections 'Understanding API Rate Limiting', 'Mechanisms of API Rate Limiting', and 'Rate Limiting Strategies for LLM Applications'. Focus on understanding the purpose of rate limiting and the conceptual differences between the fixed window, sliding window, and token bucket algorithms.
Key Rate-Limiting Algorithms:
- Fixed Window: Simple but can lead to traffic spikes at the start of each window. E.g., 100 requests/minute. A user could send all 100 in the first second.
- Sliding Window: A more robust approach that counts requests in a rolling time window, smoothing out bursts. This is generally a better choice for APIs.
- Token Bucket: A flexible algorithm where clients consume "tokens" from a bucket that refills at a constant rate. This allows for short bursts of traffic as long as tokens are available, while enforcing an average rate over time. This is what many LLM providers (like OpenAI) use for their RPM/TPM limits.
Handling Exceedances Gracefully
When a client hits a rate limit, simply returning an error isn't always the best user experience. The orq.ai article you read discussed best practices for this. The most important is exponential backoff, a client-side strategy where the client waits for progressively longer intervals before retrying after receiving a 429 Too Many Requests status code. This prevents clients from hammering your API when they are being throttled.
Test your understanding!
You are designing an API for a multi-tenant application with two user tiers: "Free" and "Pro".
- Free users make many repetitive, semantically similar queries. Your goal is to minimize your operational costs for this tier.
- Pro users pay for the service and expect low latency and high availability for their diverse queries.
Propose a combined caching and rate-limiting strategy to serve both tiers effectively. Justify your choices.
Show answer
Here is a possible strategy:
For Free Users:
- Caching: Implement a hybrid caching strategy.
- Exact-Match Cache (Redis): A fast first-pass for identical queries.
- Semantic Cache (Vector DB): If the exact cache misses, use a semantic cache with a moderately aggressive similarity threshold (e.g., >0.95). This maximizes the cache hit rate for their semantically similar queries, significantly reducing LLM calls and costs.
- Rate Limiting: Implement a stricter rate limit using a Token Bucket algorithm. This prevents any single free user from overwhelming the system and allows for small bursts while enforcing a low average request rate.
For Pro Users:
- Caching: Also use a hybrid strategy, but with different tuning.
- Exact-Match Cache: Shared with free users or a separate instance.
- Semantic Cache: Use a very high similarity threshold (e.g., >0.995) or disable it entirely. This minimizes the risk of returning a slightly incorrect cached response for a nuanced query, ensuring higher accuracy. Pro users are paying for fresh, precise results.
- Rate Limiting: Implement a much more generous rate limit, also using a Token Bucket algorithm. This gives them the high throughput they pay for, allowing for significant bursts of requests to support their applications.
This tiered approach effectively balances cost-saving for the free tier with performance and reliability for the paying pro tier.
Conclusion
You have now learned the essential operational patterns for making an LLM API not just functional, but truly production-grade. These strategies directly impact your application's user experience, stability, and financial viability.
Key Takeaways:
- Caching is a must: It drastically reduces latency and API costs.
- Use the right cache for the job: Exact-match caching is simple and fast for deterministic tasks, while semantic caching is powerful for handling the variability of natural language.
- Beware of caching complexities: In complex pipelines like RAG, be aware that changes in intermediate steps (like retrieval) can invalidate the cache, even for similar inputs.
- Rate-limiting protects your service: It prevents overload, ensures fair usage, and helps manage costs by controlling the frequency of expensive LLM calls.
- Combine strategies: A robust production system uses a layered approach, combining caching and tiered rate-limiting to balance performance, cost, and reliability.
What's Next?
We've focused on building and fortifying the infrastructure around our LLM. In the next lesson, we will shift our focus to the human in the loop: the developer. We will explore how to integrate AI coding assistants (e.g., Cursor, GitHub Copilot) into the development workflow to accelerate LLM application engineering. This moves us from building the product to optimizing the process of building it.