> ## 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.

# Express

> Step-by-step Express.js rate limiting tutorial with @unkey/ratelimit. Set per-route limits, return 429 responses, and skip the Redis dependency.

## What you'll build

An Express server with rate-limited endpoints. Users who exceed the limit get a 429 response.

**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 your Express app">
    ```bash theme={"theme":"kanagawa-wave"}
    mkdir unkey-express-ratelimit && cd unkey-express-ratelimit
    npm init -y
    npm install express @unkey/ratelimit dotenv
    ```
  </Step>

  <Step title="Add your root key">
    Create a `.env` file:

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

    <Warning>Never commit your `.env` file. Add it to `.gitignore`.</Warning>
  </Step>

  <Step title="Create your server">
    Create `index.js`:

    ```js index.js theme={"theme":"kanagawa-wave"}
    const express = require("express");
    const { Ratelimit } = require("@unkey/ratelimit");
    require("dotenv").config();

    const app = express();
    const port = process.env.PORT || 3000;

    // Create limiter instance
    const limiter = new Ratelimit({
      rootKey: process.env.UNKEY_ROOT_KEY,
      namespace: "express-api",
      limit: 10, // 10 requests...
      duration: "60s", // ...per minute
    });

    // Public route
    app.get("/", (req, res) => {
      res.json({ message: "Welcome! Try /api/data" });
    });

    // Rate-limited route
    app.get("/api/data", async (req, res) => {
      // 1. Identify the user (IP, user ID, API key, etc.)
      const identifier = req.headers["x-user-id"] || req.ip || "anonymous";

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

      // 3. Set rate limit headers
      res.set({
        "X-RateLimit-Limit": "10",
        "X-RateLimit-Remaining": remaining.toString(),
        "X-RateLimit-Reset": reset.toString(),
      });

      if (!success) {
        return res.status(429).json({
          error: "Too many requests. Please try again later.",
          retryAfter: Math.ceil((reset - Date.now()) / 1000),
        });
      }

      // 4. Request allowed
      res.json({
        message: "Here's your data!",
        remaining,
      });
    });

    app.listen(port, () => {
      console.log(`Server running at http://localhost:${port}`);
    });
    ```
  </Step>

  <Step title="Run your server">
    ```bash theme={"theme":"kanagawa-wave"}
    node index.js
    ```
  </Step>

  <Step title="Test it">
    ```bash theme={"theme":"kanagawa-wave"}
    # 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:

    ```json theme={"theme":"kanagawa-wave"}
    {
      "error": "Too many requests. Please try again later.",
      "retryAfter": 45
    }
    ```
  </Step>
</Steps>

## 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

For cleaner code, create reusable middleware:

```js middleware/ratelimit.js theme={"theme":"kanagawa-wave"}
const { Ratelimit } = require("@unkey/ratelimit");

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

async function rateLimit(req, res, next) {
  const identifier = req.headers["x-user-id"] || req.ip || "anonymous";
  const { success, remaining, reset } = await limiter.limit(identifier);

  res.set({
    "X-RateLimit-Remaining": remaining.toString(),
    "X-RateLimit-Reset": reset.toString(),
  });

  if (!success) {
    return res.status(429).json({ error: "Rate limit exceeded" });
  }

  next();
}

module.exports = { rateLimit };
```

### createMiddleware helper

Create an Express middleware from a Ratelimit instance:

```js theme={"theme":"kanagawa-wave"}
function createMiddleware(limiter) {
  return async (req, res, next) => {
    const identifier = req.ip ?? req.headers["x-forwarded-for"] ?? "unknown";
    const { success, remaining, reset } = await limiter.limit(identifier);

    if (!success) {
      res.set("Retry-After", Math.ceil((reset - Date.now()) / 1000).toString());
      res.set("X-RateLimit-Remaining", "0");
      return res.status(429).json({ error: "Too many requests" });
    }

    res.set("X-RateLimit-Remaining", remaining.toString());
    next();
  };
}
```

Use on any route:

```js theme={"theme":"kanagawa-wave"}
const { rateLimit } = require("./middleware/ratelimit");

app.get("/api/data", rateLimit, (req, res) => {
  res.json({ data: "protected content" });
});

app.post("/api/submit", rateLimit, (req, res) => {
  res.json({ success: true });
});
```

## Different limits per route

Create multiple limiters:

```js theme={"theme":"kanagawa-wave"}
const apiLimiter = new Ratelimit({
  rootKey: process.env.UNKEY_ROOT_KEY,
  namespace: "api",
  limit: 100,
  duration: "1m",
});

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

// 100/min for general API
app.use("/api", createMiddleware(apiLimiter));

// 5/min for login (prevent brute force)
app.post("/login", createMiddleware(authLimiter), loginHandler);
```

## Next steps

<CardGroup cols={2}>
  <Card title="How it works" icon="bolt" href="/platform/ratelimiting/how-it-works">
    Understand the 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/express">
    Combine with API key authentication
  </Card>
</CardGroup>

## Troubleshooting

<AccordionGroup>
  <Accordion title="Rate limit not applying?">
    * Verify `UNKEY_ROOT_KEY` is set and has `ratelimit.*.limit` permission -
      Make sure you're using a consistent identifier per user - Check that `.env`
      is loaded before creating the limiter
  </Accordion>

  <Accordion title="TypeScript version?">
    Install types and use imports:

    ```bash theme={"theme":"kanagawa-wave"}
    npm install -D typescript @types/express @types/node
    ```

    ```ts theme={"theme":"kanagawa-wave"}
    import express from "express";
    import { Ratelimit } from "@unkey/ratelimit";
    ```
  </Accordion>
</AccordionGroup>
