Rate Limiting SDK
You can configure a custom rule with rate limit in your code by using the @vercel/firewall package. This can be useful in the following cases:
- You need to set a rate limit on requests in your backend
- You want to use additional conditions with the rate limit that are not possible in the custom rule configuration of the dashboard
- From your dashboard, select the project that you'd like to configure rate limiting for. Then open Firewall in the sidebar
- Select Configure on the top right of the Firewall overview page. Then, select + New Rule
- Complete the fields for the rule as follows
- Type a name such as "Firewall api rule"
- In the Configure section, for the first If condition, select
@vercel/firewall - Use
update-objectas the Rate limit ID - Use the default values for Rate Limit and Then
- Select Save Rule
- Apply the changes:
- When you make any change, you will see a Review Changes button appear or update on the top right with the number of changes requested
- Select Review Changes and review the changes to be applied
- Select Publish to apply the changes to your production deployment
You can now use the Rate limit ID
update-objectset up above with@vercel/firewallto rate limit any request based on your own conditions. Every request is counted against a rate limit key, and each unique key gets its own bucket. By default, the key is the client IP. Override it by passing arateLimitKey(see Custom rate limit keys).rate-limit.tsimport { checkRateLimit } from '@vercel/firewall'; export async function POST(request: Request) { const { rateLimited } = await checkRateLimit('update-object', { request }); if (rateLimited) { return new Response( JSON.stringify({ error: 'Rate limit exceeded', }), { status: 429, headers: { 'Content-Type': 'application/json', }, }, ); } // Otherwise, continue with other tasks }For your code to run when deployed in a preview deployment, you need to:
- Enable Protection Bypass for Automation in your project
- Ensure System Environment Variables are automatically exposed
Pass a rateLimitKey to bucket requests on something other than the client IP, such as an authenticated user ID or organization ID. The key you pass replaces the default client-IP bucket entirely: every request with the same key shares a bucket, even when the requests come from different IPs. To keep IP as part of the bucket, compose the key yourself (see below).
For example, this code will have a rate limit per authenticated user:
import { checkRateLimit } from '@vercel/firewall';
import { authenticateUser } from './auth';
export async function POST(request: Request) {
const auth = await authenticateUser(request);
const { rateLimited } = await checkRateLimit('update-object', {
request,
rateLimitKey: auth.userId,
});
if (rateLimited) {
return new Response(
JSON.stringify({
error: 'Rate limit exceeded',
}),
{
status: 429,
headers: {
'Content-Type': 'application/json',
},
},
);
}
// Otherwise, continue with the request
}Two or more values can be combined when you want to rate limit on multiple dimensions. Create a compound key from the separate values before passing it as rateLimitKey:
const auth = await authenticateUser(request);
const rateLimitKey = `${auth.orgId}:${auth.userId}`;
const { rateLimited } = await checkRateLimit('update-object', {
request,
rateLimitKey,
});Pick a delimiter that does not appear in your id values, or use a structured encoding if parts are free-form, so two different pairs of values never produce the same string.
The following example shows how to use Firewall rule conditions in the dashboard together with a rateLimitKey you choose in code. The dashboard If conditions narrow which requests hit the rule (for example, a specific request header). Your function then calls checkRateLimit with a custom key.
Edit the custom rule in the dashboard and add an If condition with the following values, then click Save Rule:
- Filter dropdown: #Request Header
- Value:
xrr-internal-header - Operator: Equals
- Match value:
internal
In code, pass a rateLimitKey that matches how the buckets should be separated. This will only apply on request that matches the condition above.
import { checkRateLimit } from '@vercel/firewall';
import { authenticateUser } from './auth';
export async function POST(request: Request) {
const auth = await authenticateUser(request);
const { rateLimited } = await checkRateLimit('update-object', {
request,
rateLimitKey: auth.orgId,
});
if (rateLimited) {
return new Response(
JSON.stringify({
error: 'Rate limit exceeded',
}),
{
status: 429,
headers: {
'Content-Type': 'application/json',
},
},
);
}
}Pass timeout to limit how long the rate-limit fetch can wait. This option applies to checkRateLimit and unstable_checkRateLimit in @vercel/firewall 1.2.6 and later:
| Option | Type | Default | Description |
|---|---|---|---|
timeout | number | No timeout | Fetch timeout in milliseconds. The promise rejects with a TimeoutError when exceeded |
The timeout limits the fetch duration, not the rate-limit counting window. For example, return a service-unavailable response if the check takes too long:
import { checkRateLimit } from '@vercel/firewall';
export async function POST(request: Request) {
try {
const { rateLimited } = await checkRateLimit('update-object', {
request,
timeout: 1000,
});
if (rateLimited) {
return new Response('Rate limit exceeded', { status: 429 });
}
} catch (error) {
if (error instanceof Error && error.name === 'TimeoutError') {
return new Response('Rate-limit check timed out', { status: 503 });
}
throw error;
}
return new Response('Rate-limit check passed', { status: 200 });
}import { checkRateLimit } from '@vercel/firewall';
export async function POST(request) {
try {
const { rateLimited } = await checkRateLimit('update-object', {
request,
timeout: 1000,
});
if (rateLimited) {
return new Response('Rate limit exceeded', { status: 429 });
}
} catch (error) {
if (error instanceof Error && error.name === 'TimeoutError') {
return new Response('Rate-limit check timed out', { status: 503 });
}
throw error;
}
return new Response('Rate-limit check passed', { status: 200 });
}- WAF Rate Limiting: Counting algorithms, limits, and pricing
Was this helpful?