What you’ll build
A Hono app with rate-limited endpoints. Users who exceed the limit get a 429 response.
Time to complete: ~5 minutes
Prerequisites
Create a Hono app
npm create hono@latest unkey-hono-ratelimit
cd unkey-hono-ratelimit
Choose your preferred runtime.Install the SDK
npm install @unkey/ratelimit
Add your root key
Create a .env file:UNKEY_ROOT_KEY="unkey_..."
Add rate limiting
Update src/index.ts:import { Hono } from "hono";
import { Ratelimit } from "@unkey/ratelimit";
const app = new Hono();
// Create limiter instance
const limiter = new Ratelimit({
rootKey: process.env.UNKEY_ROOT_KEY!,
namespace: "hono-api",
limit: 10, // 10 requests...
duration: "60s", // ...per minute
});
// Public route
app.get("/", (c) => {
return c.json({ message: "Welcome! Try /api/data" });
});
// Rate-limited route
app.get("/api/data", async (c) => {
// 1. Identify the user
const identifier = c.req.header("x-user-id")
?? c.req.header("x-forwarded-for")
?? "anonymous";
// 2. Check the rate limit
const { success, remaining, reset } = await limiter.limit(identifier);
// 3. Set headers
c.header("X-RateLimit-Limit", "10");
c.header("X-RateLimit-Remaining", remaining.toString());
c.header("X-RateLimit-Reset", reset.toString());
if (!success) {
return c.json(
{ error: "Too many requests. Try again later." },
429
);
}
// 4. Request allowed
return c.json({ message: "Here's your data!", remaining });
});
export default app;
Test it
# Hit the endpoint 12 times
for i in {1..12}; do
curl http://localhost:3000/api/data -H "x-user-id: test-user"
echo ""
done
First 10 requests succeed. Requests 11+ get:{ "error": "Too many requests. Try again later." }
What’s in the response?
limiter.limit() returns:
| Field | Type | Description |
|---|
success | boolean | true if allowed, false if rate limited |
remaining | number | Requests left in current window |
reset | number | Unix timestamp (ms) when window resets |
limit | number | The configured limit |
Using as middleware
Create reusable middleware for cleaner code:
src/middleware/ratelimit.ts
import { Context, Next } from "hono";
import { Ratelimit } from "@unkey/ratelimit";
const limiter = new Ratelimit({
rootKey: process.env.UNKEY_ROOT_KEY!,
namespace: "api",
limit: 100,
duration: "60s",
});
export async function rateLimit(c: Context, next: Next) {
const identifier = c.req.header("x-user-id")
?? c.req.header("x-forwarded-for")
?? "anonymous";
const { success, remaining, reset } = await limiter.limit(identifier);
c.header("X-RateLimit-Remaining", remaining.toString());
c.header("X-RateLimit-Reset", reset.toString());
if (!success) {
return c.json({ error: "Rate limit exceeded" }, 429);
}
await next();
}
Apply to routes:
import { Hono } from "hono";
import { rateLimit } from "./middleware/ratelimit";
const app = new Hono();
// Public
app.get("/", (c) => c.json({ message: "Welcome" }));
// Rate-limited routes
app.use("/api/*", rateLimit);
app.get("/api/data", (c) => {
return c.json({ data: "protected content" });
});
app.get("/api/user", (c) => {
return c.json({ user: "info" });
});
export default app;
Different limits per route group
const apiLimiter = new Ratelimit({
rootKey: process.env.UNKEY_ROOT_KEY!,
namespace: "api",
limit: 100,
duration: "60s",
});
const authLimiter = new Ratelimit({
rootKey: process.env.UNKEY_ROOT_KEY!,
namespace: "auth",
limit: 5,
duration: "60s",
});
// Apply different limiters to different route groups
app.use("/api/*", createRateLimitMiddleware(apiLimiter));
app.use("/auth/*", createRateLimitMiddleware(authLimiter));
Deploying to Cloudflare Workers
For Cloudflare Workers, access env through the context:
app.get("/api/data", async (c) => {
const limiter = new Ratelimit({
rootKey: c.env.UNKEY_ROOT_KEY, // Access from c.env
namespace: "api",
limit: 10,
duration: "60s",
});
const { success } = await limiter.limit("user-id");
// ...
});
Set your secret with wrangler:
npx wrangler secret put UNKEY_ROOT_KEY
Next steps
How it works
Understand the architecture
Per-user overrides
Give specific users higher limits
SDK Reference
All configuration options
Add API key auth
Combine with API key authentication
Troubleshooting
- Verify
UNKEY_ROOT_KEY is set correctly
- For Workers: use
c.env.UNKEY_ROOT_KEY, not process.env
- Check your root key has
ratelimit.*.limit permission
Environment not loading locally?
- For Node.js: Install
dotenv and add import 'dotenv/config'
- For Bun:
.env loads automatically
- Restart the dev server after changes
Last modified on February 6, 2026