Product
Socket Now Supports uv.lock Files
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
@upstash/ratelimit
Advanced tools
An HTTP/REST based Redis client built on top of Upstash REST API. Upstash REST API.
It is the only connectionless (HTTP based) ratelimiter and designed for:
npm install @upstash/ratelimit
import { Ratelimit } from "https://deno.land/x/upstash_ratelimit/mod.ts";
Create a new redis database on upstash
See here for documentation on how to create a redis instance.
import { Ratelimit } from "@upstash/ratelimit"; // for deno: see above
import { Redis } from "@upstash/redis";
// Create a new ratelimiter, that allows 10 requests per 10 seconds
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
});
// Use a constant string to limit all requests with a single ratelimit
// Or use a userID, apiKey or ip address for individual limits.
const identifier = "api";
const { success } = await ratelimit.limit(identifier);
if (!success) {
return "Unable to process at this time";
}
doExpensiveCalculation();
return "Here you go!";
Here's a complete nextjs example
The limit
method returns some more metadata that might be useful to you:
export type RatelimitResponse = {
/**
* Whether the request may pass(true) or exceeded the limit(false)
*/
success: boolean;
/**
* Maximum number of requests allowed within a window.
*/
limit: number;
/**
* How many requests the user has left within the current window.
*/
remaining: number;
/**
* Unix timestamp in milliseconds when the limits are reset.
*/
reset: number;
/**
* For the MultiRegion setup we do some synchronizing in the background, after returning the current limit.
* In most case you can simply ignore this.
*
* On Vercel Edge or Cloudflare workers, you need to explicitely handle the pending Promise like this:
*
* **Vercel Edge:**
* https://nextjs.org/docs/api-reference/next/server#nextfetchevent
*
* ```ts
* const { pending } = await ratelimit.limit("id")
* event.waitUntil(pending)
* ```
*
* **Cloudflare Worker:**
* https://developers.cloudflare.com/workers/runtime-apis/fetch-event/#syntax-module-worker
*
* ```ts
* const { pending } = await ratelimit.limit("id")
* context.waitUntil(pending)
* ```
*/
pending: Promise<unknown>;
};
In case you don't want to reject a request immediately but wait until it can be processed, we also provide
ratelimit.blockUntilReady(identifier: string, timeout: number): Promise<RatelimitResponse>
It is very similar to the limit
method and takes an identifier and returns the
same response. However if the current limit has already been exceeded, it will
automatically wait until the next window starts and will try again. Setting the
timeout parameter (in milliseconds) will cause the returned Promise to resolve
in a finite amount of time.
// Create a new ratelimiter, that allows 10 requests per 10 seconds
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
});
// `blockUntilReady` returns a promise that resolves as soon as the request is allowed to be processed, or after 30 seconds
const { success } = await ratelimit.blockUntilReady("id", 30_000);
if (!success) {
return "Unable to process, even after 30 seconds";
}
doExpensiveCalculation();
return "Here you go!";
For extreme load or denial of service attacks, it might be too expensive to call redis for every incoming request, just to find out the request should be blocked because they have exceeded the limit.
You can use an ephemeral in memory cache by passing the ephemeralCache
options:
const cache = new Map(); // must be outside of your serverless function handler
// ...
const ratelimit = new Ratelimit({
// ...
ephemeralCache: cache,
});
If enabled, the ratelimiter will keep a global cache of identifiers and a reset timestamp, that have exhausted their ratelimit. In serverless environments this is only possible if you create the ratelimiter instance outside of your handler function. While the function is still hot, the ratelimiter can block requests without having to request data from redis, thus saving time and money.
Whenever an identifier has exceeded its limit, the ratelimiter will add it to an internal list together with its reset timestamp. If the same identifier makes a new request before it is reset, we can immediately reject it.
Using a single redis instance has the downside of providing low latencies to the
part of your userbase closest to the deployed db. That's why we also built
MultiRegionRatelimit
which replicates the state across multiple redis
databases as well as offering lower latencies to more of your users.
MultiRegionRatelimit
does this by checking the current limit in the closest db
and returning immediately. Only afterwards will the state be asynchronously
replicated to the other datbases leveraging
CRDTs. Due
to the nature of distributed systems, there is no way to guarantee the set
ratelimit is not exceeded by a small margin. This is the tradeoff for reduced
global latency.
The api is the same, except for asking for multiple redis instances:
import { MultiRegionRatelimit } from "@upstash/ratelimit"; // for deno: see above
import { Redis } from "@upstash/redis";
// Create a new ratelimiter, that allows 10 requests per 10 seconds
const ratelimit = new MultiRegionRatelimit({
redis: [
new Redis({
/* auth */
}),
new Redis({
/* auth */
}),
new Redis({
/* auth */
}),
],
limiter: Ratelimit.slidingWindow(10, "10 s"),
});
// Use a constant string to limit all requests with a single ratelimit
// Or use a userID, apiKey or ip address for individual limits.
const identifier = "api";
const { success } = await ratelimit.limit(identifier);
The MultiRegion setup will do some synchronization between databases after returning the current limit. This can lead to problems on Cloudflare Workers and therefore Vercel Edge functions, because dangling promises must be taken care of:
Vercel Edge: docs
const { pending } = await ratelimit.limit("id");
event.waitUntil(pending);
Cloudflare Worker: docs
const { pending } = await ratelimit.limit("id");
context.waitUntil(pending);
Let's assume you have customers in the US and Europe. In this case you can create 2 regional redis databases on Upastash and your users will enjoy the latency of whichever db is closest to them.
We provide different algorithms to use out of the box. Each has pros and cons.
This algorithm divides time into fixed durations/windows. For example each window is 10 seconds long. When a new request comes in, the current time is used to determine the window and a counter is increased. If the counter is larger than the set limit, the request is rejected.
Create a new ratelimiter, that allows 10 requests per 10 seconds.
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.fixedWindow(10, "10 s"),
});
Builds on top of fixed window but instead of a fixed window, we use a rolling window. Take this example: We have a rate limit of 10 requests per 1 minute. We divide time into 1 minute slices, just like in the fixed window algorithm. Window 1 will be from 00:00:00 to 00:01:00 (HH:MM:SS). Let's assume it is currently 00:01:15 and we have received 4 requests in the first window and 5 requests so far in the current window. The approximation to determine if the request should pass works like this:
limit = 10
// 4 request from the old window, weighted + requests in current window
rate = 4 * ((60 - 15) / 60) + 5 = 8
return rate < limit // True means we should allow the request
Create a new ratelimiter, that allows 10 requests per 10 seconds.
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
});
Not yet supported for MultiRegionRatelimit
Consider a bucket filled with {maxTokens}
tokens that refills constantly at
{refillRate}
per {interval}
. Every request will remove one token from the
bucket and if there is no token to take, the request is rejected.
maxTokens
higher than
refillRate
Create a new bucket, that refills 5 tokens every 10 seconds and has a maximum size of 10.
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.tokenBucket(5, "10 s", 10),
});
Create a new redis database on upstash and copy the url and token.
UPSTASH_REDIS_REST_URL=".." UPSTASH_REDIS_REST_TOKEN=".." deno test -A
FAQs
[![npm (scoped)](https://img.shields.io/npm/v/@upstash/ratelimit)](https://www.npmjs.com/package/@upstash/ratelimit) [![Tests](https://github.com/upstash/ratelimit/actions/workflows/tests.yaml/badge.svg)](https://github.com/upstash/ratelimit/actions/workf
The npm package @upstash/ratelimit receives a total of 59,099 weekly downloads. As such, @upstash/ratelimit popularity was classified as popular.
We found that @upstash/ratelimit demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.
Security News
PEP 770 proposes adding SBOM support to Python packages to improve transparency and catch hidden non-Python dependencies that security tools often miss.