BotID
BotID is available on all plans
Sophisticated bots are designed to mimic real user behavior. They can run JavaScript, solve CAPTCHAs, and navigate interfaces in ways that closely resemble human interactions. Tools like Playwright and Puppeteer automate these sessions, simulating actions from page load to form submission.
These bots do not rely on spoofed headers or patterns that typically trigger rate limits. Instead, they blend in with normal traffic, making detection difficult and mitigation costly.
Vercel BotID is an invisible CAPTCHA that protects against sophisticated bots without showing visible challenges or requiring manual intervention. It adds a protection layer for public, high-value routes, such as checkouts, signups, and APIs, that are common targets for bots imitating real users.
BotID includes a Deep Analysis mode, powered by Kasada. Kasada is a leading bot protection provider trusted by Fortune 500 companies and global enterprises. It delivers advanced bot detection and anti-fraud capabilities.
BotID provides real-time protection against:
- Automated attacks: Shield your application from credential stuffing, brute force attacks, and other automated threats
- Data scraping: Prevent unauthorized data extraction and content theft
- API abuse: Protect your endpoints from excessive automated requests
- Spam and fraud: Block malicious bots while allowing legitimate traffic through
- Expensive resources: Prevent bots from consuming expensive infrastructure, bandwidth, compute, or inventory
- Seamless integration: Works with existing Vercel projects with minimal configuration
- Customizable protection: Define which paths and endpoints require bot protection
- Privacy-focused: Respects user privacy while providing robust protection
- Deep Analysis (Kasada-powered): For the highest level of protection, enable Deep Analyis in your Vercel Dashboard. This leverages Kasada's advanced detection technology to block even the most sophisticated bots.
BotID has two modes:
- Basic - Ensures valid browser sessions are accessing your sites
- Deep Analysis - Connects thousands of additional client side signals to further distinguish humans from bots
With a few lines of code, you can run BotID on any endpoint. It operates by:
- Giving you a clear yes or no response to each request
- Deploying dynamic detection models based on a deep understanding of bots that validates requests on your server actions and route handlers to ensure only verified traffic reaches your protected endpoints
- Quickly assessing users without disrupting user sessions
BotID counters the most advanced bots by:
- Silently collecting thousands of signals that distinguish human users from bots
- Changing detection methods on every page load to prevent reverse engineering and sophisticated bypasses
- Streaming attack data to a global machine learning system that improves protection for all customers
Before setting up BotID, ensure you have a JavaScript project deployed on Vercel.
Add BotID to your project:
pnpm i botid
Use the appropriate configuration method for your framework to set up proxy rewrites. This ensures that ad-blockers, third party scripts, and more won't make BotID any less effective.
next.config.jsimport { withBotId } from 'botid/next/config'; const nextConfig = { // Your existing Next.js config }; export default withBotId(nextConfig);
nuxt.config.tsexport default defineNuxtConfig({ modules: ['botid/nuxt'], });
For other frameworks, add the following configuration values to your
vercel.json
:When using
vercel.json
configuration, these rewrites only apply in production. For local development behavior, see the Local Development Behavior section.vercel.json{ "rewrites": [ { "source": "/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/a-4-a/c.js", "destination": "https://api.vercel.com/bot-protection/v1/challenge" }, { "source": "/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/:path*", "destination": "https://api.vercel.com/bot-protection/v1/proxy/:path*" } ], "headers": [ { "source": "/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/:path*", "headers": [ { "key": "X-Frame-Options", "value": "SAMEORIGIN" } ] } ] }
Choose the appropriate method for your framework:
- Next.js 15.3+: Use
initBotId()
ininstrumentation.client.ts
for optimal performance - Other Next.js: Mount the
<BotIdClient/>
component in your layouthead
- Other frameworks: Call
initBotId()
during application initialization
Next.js 15.3+ (Recommended)
We recommend using
initBotId()
ininstrumentation-client.ts
for better performance in Next.js 15.3+. For earlier versions, use the React component approach.instrumentation-client.tsimport { initBotId } from 'botid/client/core'; // Define the paths that need bot protection. // These are paths that are routed to by your app. // These can be: // - API endpoints (e.g., '/api/checkout') // - Server actions invoked from a page (e.g., '/dashboard') // - Dynamic routes (e.g., '/api/create/*') initBotId({ protect: [ { path: '/api/checkout', method: 'POST', }, { // Wildcards can be used to expand multiple segments // /team/*/activate will match // /team/a/activate // /team/a/b/activate // /team/a/b/c/activate // ... path: '/team/*/activate', method: 'POST', }, { // Wildcards can also be used at the end for dynamic routes path: '/api/user/*', method: 'POST', }, ], });
Next.js < 15.3
app/layout.tsximport { BotIdClient } from 'botid/client'; import { ReactNode } from 'react'; const protectedRoutes = [ { path: '/api/checkout', method: 'POST', }, ]; type RootLayoutProps = { children: ReactNode; }; export default function RootLayout({ children }: RootLayoutProps) { return ( <html lang="en"> <head> <BotIdClient protect={protectedRoutes} /> </head> <body>{children}</body> </html> ); }
plugins/botid.client.tsimport { initBotId } from 'botid/client/core'; export default defineNuxtPlugin({ enforce: 'pre', setup() { initBotId({ protect: [{ path: '/api/post-data', method: 'POST' }], }); }, });
src/hooks.client.tsimport { initBotId } from 'botid/client/core'; export function init() { initBotId({ protect: [ { path: '/api/post-data', method: 'POST', }, ], }); }
- Next.js 15.3+: Use
Use
checkBotId()
on the routes configured in the<BotIdClient/>
component.Important configuration requirements: - Not adding the protected route to
<BotIdClient />
will result incheckBotId()
failing. The client side component dictates which requests to attach special headers to for classification purposes. - Local development always returnsisBot: false
unless you configure thedevelopmentOptions
option oncheckBotId()
. Learn more about local development behavior.- Using API routes
app/api/sensitive/route.tsimport { checkBotId } from 'botid/server'; import { NextRequest, NextResponse } from 'next/server'; export async function POST(request: NextRequest) { const verification = await checkBotId(); if (verification.isBot) { return NextResponse.json({ error: 'Access denied' }, { status: 403 }); } const data = await processUserRequest(); return NextResponse.json({ data }); }
- Using Server Actions
app/actions/create-user.ts'use server'; import { checkBotId } from 'botid/server'; export async function createUser(formData: FormData) { const verification = await checkBotId(); if (verification.isBot) { throw new Error('Access denied'); } const userData = { name: formData.get('name') as string, email: formData.get('email') as string, }; const user = await saveUser(userData); return { success: true, user }; }
BotID actively runs JavaScript on page sessions and sends headers to the server. If you test with
curl
or visit a protected route directly, BotID will block you in production. To effectively test, make afetch
request from a page in your application to the protected route.- Only available on Pro or Enterprise plans
From the Vercel dashboard
- Select your Project
- Click the Firewall tab
- Click Configure
- Enable Vercel BotID Deep Analysis
Mode | Plans Available | Price |
---|---|---|
Basic | All Plans | Free |
Deep Analysis | Pro and Enterprise | $1/1000 checkBotId() Deep Analysis calls |
Calling the checkBotId()
function in your code triggers BotID Deep Analysis
charges. Passive page views or requests that don't invoke the checkBotId()
function are not charged.
Available in botid@1.4.5 and above
When you need fine-grained control over BotID's detection levels, you can specify advancedOptions
to choose between basic and deep analysis modes on a per-route basis. This configuration takes precedence over the project-level BotID settings in your Vercel dashboard.
Important: The checkLevel
in both client and server configurations must
be identical for each protected route. A mismatch between client and server
configurations will cause BotID verification to fail, potentially blocking
legitimate traffic or allowing bots through.
In your client-side protection setup, you can specify the check level for each protected path:
initBotId({
protect: [
{
path: '/api/checkout',
method: 'POST',
advancedOptions: {
checkLevel: 'deepAnalysis', // or 'basic'
},
},
{
path: '/api/contact',
method: 'POST',
advancedOptions: {
checkLevel: 'basic',
},
},
],
});
In your server-side endpoint that uses checkBotId()
, ensure it matches the client-side configuration.
export async function POST(request: NextRequest) {
const verification = await checkBotId({
advancedOptions: {
checkLevel: 'deepAnalysis', // Must match client-side config
},
});
if (verification.isBot) {
return NextResponse.json({ error: 'Access denied' }, { status: 403 });
}
// Your protected logic here
}
By default, BotID validates that requests come from the same host that serves the BotID challenge. However, if your application architecture separates your frontend and backend domains (e.g., your app is served from vercel.com
but your API is on api.vercel.com
or vercel-api.com
), you'll need to configure extraAllowedHosts
.
The extraAllowedHosts
parameter in checkBotId()
allows you to specify a list of frontend domains that are permitted to send requests to your backend:
export async function POST(request: NextRequest) {
const verification = await checkBotId({
advancedOptions: {
extraAllowedHosts: ['vercel.com', 'app.vercel.com'],
},
});
if (verification.isBot) {
return NextResponse.json({ error: 'Access denied' }, { status: 403 });
}
// Your protected logic here
}
Only add trusted domains to extraAllowedHosts
. Each domain in this list can
send requests that will be validated by BotID, so ensure these are domains you
control.
Use this configuration when:
- Your frontend is hosted on a different domain than your API (e.g.,
myapp.com
→api.myapp.com
) - You have multiple frontend applications that need to access the same protected backend
- Your architecture uses a separate subdomain for API endpoints
You can combine extraAllowedHosts
with other advanced options:
const verification = await checkBotId({
advancedOptions: {
checkLevel: 'deepAnalysis',
extraAllowedHosts: ['app.example.com', 'dashboard.example.com'],
},
});
You can add a bypass rule to the Vercel WAF to let through traffic that would have otherwise been detected as a bot by BotID.
You can view BotID checks by selecting BotID on the firewall page dropdown on a project.
Metrics are also available in Observability Plus.
BotID does not support traditional HTML forms that use the action
and method
attributes, such as:
<form id="contact-form" method="POST" action="/api/contact">
<!-- form fields -->
<button type="submit">Send</button>
</form>
Native form submissions bypass this mechanism, preventing BotID from classifying or protecting these requests.
To ensure the necessary headers are attached, handle the form submission in JavaScript and send the request using fetch
or XMLHttpRequest
, allowing BotID to properly verify the request.
Here's how you can refactor your form to work with BotID:
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const response = await fetch('/api/contact', {
method: 'POST',
body: formData,
});
const data = await response.json();
// handle response
}
return (
<form onSubmit={handleSubmit}>
{/* form fields */}
<button type="submit">Send</button>
</form>
);
If you're using Next.js, you can use a server action in your form and use the checkBotId
function to verify the request:
'use server';
import { checkBotId } from 'botid/server';
export async function submitContact(formData: FormData) {
const verification = await checkBotId();
if (verification.isBot) {
throw new Error('Access denied');
}
// process formData
return { success: true };
}
And in your form component:
'use client';
import { submitContact } from '../actions/contact';
export default function ContactForm() {
async function handleAction(formData: FormData) {
return submitContact(formData);
}
return (
<form action={handleAction}>
{/* form fields */}
<button type="submit">Send</button>
</form>
);
}
During local development, BotID behaves differently than in production to facilitate testing and development workflows:
- Default behavior: In development mode,
checkBotId()
always returns{ isBot: false }
, allowing all requests to pass through - This ensures: Your development workflow isn't interrupted by bot protection while building and testing features
If you need to test BotID's different return values in local development, you can use the developmentBypass
option:
import { checkBotId } from 'botid/server';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
const verification = await checkBotId({
developmentOptions: {
bypass: 'BAD-BOT', // default: 'HUMAN'
},
});
if (verification.isBot) {
return NextResponse.json({ error: 'Access denied' }, { status: 403 });
}
// Your protected logic here
}
The developmentOptions
option only works in development mode and is ignored
in production. In production, BotID always performs real bot detection.
This allows you to:
- Test your bot handling logic without deploying to production
- Verify error messages and fallback behaviors
- Ensure your application correctly handles both human and bot traffic
Was this helpful?