Get Started with BotID
This guide shows you how to add BotID protection to your Vercel project. BotID blocks automated bots while allowing real users through, protecting your APIs, forms, and sensitive endpoints from abuse.
The setup involves three main components:
- Client-side component to run challenges.
- Server-side verification to classify sessions.
- Route configuration to ensure requests are routed through BotID.
BotID works best with Vercel in front. If your app is serving traffic through a reverse proxy, BotID can work in a degraded mode. Please reach out to our support team for assistance in getting BotID to work with a reverse proxy in front.
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.tsimport { withBotId } from 'botid/next/config'; const nextConfig = { // Your existing Next.js config }; export default withBotId(nextConfig);
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> ); }
- 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(request); return NextResponse.json({ data }); } async function processUserRequest(request: NextRequest) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }
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 }; } async function saveUser(userData: { name: string; email: string }) { // Your database logic here console.log('Saving user:', userData); return { id: '123', ...userData }; }
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.BotID Deep Analysis are available on Enterprise and Pro plans
From the Vercel dashboard
- Select your Project
- Click the Firewall tab
- Click Configure
- Enable Vercel BotID Deep Analysis
Client-side code for the BotID Next.js implementation:
'use client';
import { useState } from 'react';
export default function CheckoutPage() {
const [loading, setLoading] = useState(false);
const [message, setMessage] = useState('');
async function handleCheckout(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setLoading(true);
try {
const formData = new FormData(e.currentTarget);
const response = await fetch('/api/checkout', {
method: 'POST',
body: JSON.stringify({
product: formData.get('product'),
quantity: formData.get('quantity'),
}),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Checkout failed');
}
const data = await response.json();
setMessage('Checkout successful!');
} catch (error) {
setMessage('Checkout failed. Please try again.');
} finally {
setLoading(false);
}
}
return (
<form onSubmit={handleCheckout}>
<input name="product" placeholder="Product ID" required />
<input name="quantity" type="number" placeholder="Quantity" required />
<button type="submit" disabled={loading}>
{loading ? 'Processing...' : 'Checkout'}
</button>
{message && <p>{message}</p>}
</form>
);
}
Server-side code for the BotID Next.js implementation:
import { checkBotId } from 'botid/server';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
// Check if the request is from a bot
const verification = await checkBotId();
if (verification.isBot) {
return NextResponse.json(
{ error: 'Bot detected. Access denied.' },
{ status: 403 },
);
}
// Process the legitimate checkout request
const body = await request.json();
// Your checkout logic here
const order = await processCheckout(body);
return NextResponse.json({
success: true,
orderId: order.id,
});
}
async function processCheckout(data: any) {
// Implement your checkout logic
return { id: 'order-123' };
}
Was this helpful?