02criticalREADME→ README.md
Put a runnable Next.js quickstart at the top of the README
The README as fetched is absent and the npm description is raw badge markup, so the page agents land on gives them nothing to use. Claude never fetched anything and worked from memory run 260, 261]. Codex searched and landed on upstash.com docs, not the package run 262, 264]. Nothing says which file to put the code in, which env vars are needed or where to get them.
- npm description: [](https://www.npmjs.com/package/@upstash/ratelimit) [: (absent)
- run 261: pnpm add @upstash/ratelimit @upstash/redis (no URL fetched before install)
The fix
Put a one-line description plus a 25-line Next.js quickstart above all badges. Move badges to the bottom. Include install, the two env vars with the console URL, a proxy.ts example (Next 16, with middleware.ts for earlier versions), the 429 response with Retry-After, and a verify command.
Show proposed README.md · ready to paste
# @upstash/ratelimit
> Serverless rate limiting on Upstash Redis (HTTP, no connection pool). Works on Vercel, Cloudflare Workers, Next.js, Node.
## Install
```bash
npm install @upstash/ratelimit @upstash/redis
```
## Env vars
```bash
UPSTASH_REDIS_REST_URL= # https://console.upstash.com/redis -> your DB -> REST API
UPSTASH_REDIS_REST_TOKEN= # same page
```
## Next.js App Router: limit every /api route per IP
```ts
// src/proxy.ts (Next 16; name it src/middleware.ts and export `middleware` on Next <= 15)
import { NextRequest, NextResponse } from 'next/server';
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(5, '1 m'),
prefix: 'rl',
});
export async function proxy(req: NextRequest) {
const ip = req.headers.get('x-forwarded-for')?.split(',')[0]?.trim() ?? 'anonymous';
const { success, limit, remaining, reset } = await ratelimit.limit(ip);
if (!success) {
return NextResponse.json({ error: 'Too many requests' }, {
status: 429,
headers: { 'Retry-After': String(Math.max(1, Math.ceil((reset - Date.now()) / 1000))), 'X-RateLimit-Limit': String(limit), 'X-RateLimit-Remaining': String(remaining) },
});
}
return NextResponse.next();
}
export const config = { matcher: '/api/:path*' };
```
## Verify
```bash
npm run build && npm start &
for i in 1 2 3 4 5 6; do curl -s -o /dev/null -w '%{http_code} ' localhost:3000/api/hello; done # 200 x5, then 429
```
No Upstash account yet? See 'Local dev without an Upstash account' below.