LangGraph Tutorial (TypeScript): rate limiting API calls for beginners
This tutorial shows how to add rate limiting to a LangGraph workflow in TypeScript so your agent stops hammering an API when traffic spikes or users retry too aggressively. You need this when calling paid APIs, third-party services with quotas, or internal endpoints that will reject bursts with 429s.
What You'll Need
- •Node.js 18+
- •TypeScript 5+
- •A LangGraph project using
@langchain/langgraph - •
zodfor state typing - •An API key for the service you want to protect
- •Optional:
dotenvif you want to load keys from.env
Install the packages:
npm install @langchain/langgraph zod
npm install -D typescript tsx @types/node
Step-by-Step
- •Start with a small graph state that tracks how many calls have been made and when the window resets. This keeps the limiter inside the graph instead of hiding it in random helper code.
import { Annotation, StateGraph } from "@langchain/langgraph";
import { z } from "zod";
const RateState = Annotation.Root({
input: Annotation<string>(),
output: Annotation<string>(),
callCount: Annotation<number>({
default: () => 0,
reducer: (_, next) => next,
}),
windowStart: Annotation<number>({
default: () => Date.now(),
reducer: (_, next) => next,
}),
});
type RateStateType = typeof RateState.State;
- •Add a reusable rate-limit check node. This example allows 3 calls per 10 seconds, which is enough to show the pattern without needing an external store.
const LIMIT = 3;
const WINDOW_MS = 10_000;
async function rateLimitNode(state: RateStateType) {
const now = Date.now();
const windowExpired = now - state.windowStart >= WINDOW_MS;
if (windowExpired) {
return {
callCount: 1,
windowStart: now,
output: "",
};
}
if (state.callCount >= LIMIT) {
throw new Error(
`Rate limit exceeded: ${LIMIT} calls per ${WINDOW_MS / 1000}s`
);
}
return {
callCount: state.callCount + 1,
output: "",
};
}
- •Put your real API call behind another node. For beginners, I recommend keeping the limiter and the fetch separate so you can test them independently.
async function callApiNode(state: RateStateType) {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
const data = (await response.json()) as { title: string };
return {
output: `${state.input}: ${data.title}`,
};
}
- •Wire the nodes into a LangGraph workflow. The graph checks the limit first, then makes the API request only if the request is allowed.
const graph = new StateGraph(RateState)
.addNode("rateLimit", rateLimitNode)
.addNode("callApi", callApiNode)
.addEdge("__start__", "rateLimit")
.addEdge("rateLimit", "callApi")
.addEdge("callApi", "__end__")
.compile();
- •Run the graph multiple times to see the limiter in action. The first three requests pass, and the fourth one fails until the time window resets.
async function main() {
for (let i = 1; i <= 4; i++) {
try {
const result = await graph.invoke({
input: `request-${i}`,
callCount: i === 1 ? undefined : undefined,
windowStart: Date.now(),
output: "",
});
console.log(`OK ${i}:`, result.output);
} catch (error) {
console.error(`FAILED ${i}:`, (error as Error).message);
}
}
}
main().catch(console.error);
Testing It
Run the file with npx tsx your-file.ts. You should see successful outputs for the first few requests and then a thrown error once the limit is hit.
To verify the reset logic, wait at least WINDOW_MS milliseconds and run it again. If you want stronger proof, log callCount and windowStart inside rateLimitNode so you can see exactly when the window rolls over.
If you replace jsonplaceholder with a real API, watch for fewer burst failures and fewer wasted requests. That is the point of putting rate limiting in front of expensive calls instead of reacting after the provider rejects you.
Next Steps
- •Move from in-memory limits to Redis so limits work across multiple server instances.
- •Add exponential backoff and retry handling for transient
429responses. - •Use per-user or per-tenant keys instead of one global limit for all traffic
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit