> ## Documentation Index
> Fetch the complete documentation index at: https://unkey.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js

> Step-by-step Next.js rate limiting tutorial with @unkey/ratelimit. Throttle API routes and return 429 responses without provisioning Redis.

## What you'll build

A Next.js API route that limits each user to a set number of requests per time window. Excess requests get rejected with a 429.

**Time to complete:** \~5 minutes

## Prerequisites

* [Unkey account](https://app.unkey.com/auth/sign-up) (free)
* [Root key](https://app.unkey.com/settings/root-keys) with `ratelimit.*.limit` permission
* Node.js 18+

<Steps titleSize="h3">
  <Step title="Create a Next.js app">
    Skip if you have an existing project.

    <CodeGroup>
      ```bash npm theme={"theme":"kanagawa-wave"}
      npx create-next-app@latest my-app
      cd my-app
      ```

      ```bash pnpm theme={"theme":"kanagawa-wave"}
      pnpm create next-app@latest my-app
      cd my-app
      ```

      ```bash bun theme={"theme":"kanagawa-wave"}
      bunx create-next-app my-app
      cd my-app
      ```
    </CodeGroup>
  </Step>

  <Step title="Install the SDK">
    <CodeGroup>
      ```bash npm theme={"theme":"kanagawa-wave"}
      npm install @unkey/ratelimit
      ```

      ```bash pnpm theme={"theme":"kanagawa-wave"}
      pnpm add @unkey/ratelimit
      ```

      ```bash bun theme={"theme":"kanagawa-wave"}
      bun add @unkey/ratelimit
      ```
    </CodeGroup>
  </Step>

  <Step title="Add your root key">
    Create or update `.env.local`:

    ```bash .env.local theme={"theme":"kanagawa-wave"}
    UNKEY_ROOT_KEY="unkey_..."
    ```

    <Warning>Never commit your root key. Add `.env.local` to `.gitignore`.</Warning>
  </Step>

  <Step title="Create a rate-limited route">
    ```ts app/api/protected/route.ts theme={"theme":"kanagawa-wave"}
    import { NextResponse } from "next/server";
    import { Ratelimit } from "@unkey/ratelimit";

    // Create limiter instance outside the handler
    const limiter = new Ratelimit({
      rootKey: process.env.UNKEY_ROOT_KEY!,
      namespace: "my-app", // Group related limits
      limit: 10, // 10 requests...
      duration: "60s", // ...per minute
    });

    export async function POST(req: Request) {
      // 1. Identify the user (IP, user ID, API key, etc.)
      const identifier =
        req.headers.get("x-user-id") ??
        req.headers.get("x-forwarded-for") ??
        "anonymous";

      // 2. Check the rate limit
      const { success, remaining, reset } = await limiter.limit(identifier);

      // 3. Add rate limit headers (optional but nice for clients)
      const headers = {
        "X-RateLimit-Limit": "10",
        "X-RateLimit-Remaining": remaining.toString(),
        "X-RateLimit-Reset": reset.toString(),
      };

      if (!success) {
        return NextResponse.json(
          { error: "Too many requests. Please try again later." },
          { status: 429, headers },
        );
      }

      // 4. Request allowed, do your thing
      return NextResponse.json({ message: "Hello!", remaining }, { headers });
    }
    ```
  </Step>

  <Step title="Run your app">
    <CodeGroup>
      ```bash npm theme={"theme":"kanagawa-wave"}
      npm run dev
      ```

      ```bash pnpm theme={"theme":"kanagawa-wave"}
      pnpm dev
      ```

      ```bash bun theme={"theme":"kanagawa-wave"}
      bun dev
      ```
    </CodeGroup>
  </Step>

  <Step title="Test it">
    ```bash theme={"theme":"kanagawa-wave"}
    # Hit the endpoint multiple times
    for i in {1..12}; do
      curl -X POST http://localhost:3000/api/protected \
        -H "x-user-id: test-user"
      echo ""
    done
    ```

    First 10 requests return `200`. Requests 11+ return `429`:

    ```json theme={"theme":"kanagawa-wave"}
    { "error": "Too many requests. Please try again later." }
    ```

    Wait 60 seconds and the limit resets.
  </Step>
</Steps>

## What's in the response?

`limiter.limit()` returns:

| Field       | Type      | Description                                           |
| ----------- | --------- | ----------------------------------------------------- |
| `success`   | `boolean` | `true` if request is allowed, `false` if rate limited |
| `remaining` | `number`  | Requests remaining in current window                  |
| `reset`     | `number`  | Unix timestamp (ms) when the window resets            |
| `limit`     | `number`  | The configured limit                                  |

## Choosing an identifier

The identifier determines *who* gets rate limited. Common choices:

| Identifier | Use case                   | Example                              |
| ---------- | -------------------------- | ------------------------------------ |
| User ID    | Authenticated users        | `req.auth.userId`                    |
| API key    | Per-key limits             | `req.headers.get("x-api-key")`       |
| IP address | Anonymous/public endpoints | `req.headers.get("x-forwarded-for")` |
| Combo      | Extra specificity          | `${userId}:${endpoint}`              |

## Creating a reusable limiter

For cleaner code, create a utility:

```ts lib/ratelimit.ts theme={"theme":"kanagawa-wave"}
import { Ratelimit } from "@unkey/ratelimit";

export const apiLimiter = new Ratelimit({
  rootKey: process.env.UNKEY_ROOT_KEY!,
  namespace: "api",
  limit: 100,
  duration: "1m",
});

export const authLimiter = new Ratelimit({
  rootKey: process.env.UNKEY_ROOT_KEY!,
  namespace: "auth",
  limit: 5,
  duration: "1m",
});
```

Then use in routes:

```ts app/api/login/route.ts theme={"theme":"kanagawa-wave"}
import { authLimiter } from "@/lib/ratelimit";

export async function POST(req: Request) {
  const ip = req.headers.get("x-forwarded-for") ?? "unknown";
  const { success } = await authLimiter.limit(ip);

  if (!success) {
    return Response.json({ error: "Too many login attempts" }, { status: 429 });
  }

  // Handle login...
}
```

## Next steps

<CardGroup cols={2}>
  <Card title="How it works" icon="bolt" href="/platform/ratelimiting/how-it-works">
    Understand the rate limiting architecture
  </Card>

  <Card title="Per-user overrides" icon="sliders" href="/platform/ratelimiting/overrides">
    Give specific users higher limits
  </Card>

  <Card title="SDK Reference" icon="book" href="/libraries/ts/ratelimit/ratelimit">
    All configuration options
  </Card>

  <Card title="Add API key auth" icon="key" href="/quickstart/apis/nextjs">
    Combine rate limiting with authentication
  </Card>
</CardGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Rate limit not working?">
    * Check that `UNKEY_ROOT_KEY` is set in `.env.local` - Verify your root key
      has `ratelimit.*.limit` permission - Make sure you're using the same
      identifier each request - Restart the dev server after changing `.env.local`
  </Accordion>

  <Accordion title="Getting network errors?">
    * Unkey's SDK retries failed requests automatically - If errors persist, check
      [status.unkey.com](https://status.unkey.com) - Check
      [status.unkey.com](https://status.unkey.com) if issues persist
  </Accordion>

  <Accordion title="Want different limits per route?">
    Create multiple `Ratelimit` instances with different namespaces and limits.
    Each namespace tracks limits independently.
  </Accordion>
</AccordionGroup>
