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.
Before setting up BotID, ensure you have a JavaScript project deployed on Vercel.
Add BotID to your project:
Terminalpnpm i botidUse 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);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'], });nuxt.config.jsexport default defineNuxtConfig({ modules: ['botid/nuxt'], });Choose the appropriate method for your framework:
- Next.js 15.3+: Use
initBotId()ininstrumentation-client.tsfor optimal performance - Other Next.js: Mount the
<BotIdClient/>component in your layouthead - Other frameworks: Call
initBotId()during application initialization
plugins/botid.client.tsimport { initBotId } from 'botid/client/core'; export default defineNuxtPlugin({ enforce: 'pre', setup() { initBotId({ protect: [{ path: '/api/post-data', method: 'POST' }], }); }, });plugins/botid.client.jsimport { 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', }, ], }); }src/hooks.client.jsimport { initBotId } from 'botid/client/core'; export function init() { initBotId({ protect: [ { path: '/api/post-data', method: 'POST', }, ], }); }client.tsimport { initBotId } from 'botid/client/core'; export function init() { initBotId({ protect: [ { path: '/api/post-data', method: 'POST', }, ], }); }client.jsimport { 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: falseunless you configure thedevelopmentOptionsoption oncheckBotId(). Learn more about local development behavior.sensitive.posts.tsimport { checkBotId } from 'botid/server'; export default defineEventHandler(async (event) => { const verification = await checkBotId(); if (verification.isBot) { throw createError({ statusCode: 403, statusMessage: 'Access denied', }); } const data = await processUserRequest(event); return { data }; }); async function processUserRequest(event: any) { // Your business logic here const body = await readBody(event); // Process the request... return { success: true }; }sensitive.posts.jsimport { checkBotId } from 'botid/server'; export default defineEventHandler(async (event) => { const verification = await checkBotId(); if (verification.isBot) { throw createError({ statusCode: 403, statusMessage: 'Access denied', }); } const data = await processUserRequest(event); return { data }; }); async function processUserRequest(event) { // Your business logic here const body = await readBody(event); // Process the request... return { success: true }; }+server.tsimport { checkBotId } from 'botid/server'; import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; export const POST: RequestHandler = async ({ request }) => { const verification = await checkBotId(); if (verification.isBot) { throw error(403, 'Access denied'); } const data = await processUserRequest(request); return json({ data }); }; async function processUserRequest(request: Request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }+server.jsimport { checkBotId } from 'botid/server'; import { json, error } from '@sveltejs/kit'; import type { RequestHandler } from './$types'; export const POST: RequestHandler = async ({ request }) => { const verification = await checkBotId(); if (verification.isBot) { throw error(403, 'Access denied'); } const data = await processUserRequest(request); return json({ data }); }; async function processUserRequest(request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }api/sensitive.tsimport { checkBotId } from 'botid/server'; export async function POST(request: Request) { const verification = await checkBotId(); if (verification.isBot) { return Response.json({ error: 'Access denied' }, { status: 403 }); } const data = await processUserRequest(request); return Response.json({ data }); } async function processUserRequest(request: Request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }api/sensitive.jsimport { checkBotId } from 'botid/server'; export async function POST(request) { const verification = await checkBotId(); if (verification.isBot) { return Response.json({ error: 'Access denied' }, { status: 403 }); } const data = await processUserRequest(request); return Response.json({ data }); } async function processUserRequest(request) { // Your business logic here const body = await request.json(); // Process the request... return { success: true }; }BotID actively runs JavaScript on page sessions and sends headers to the server. If you test with
curlor visit a protected route directly, BotID will block you in production. To effectively test, make afetchrequest 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 Rules
- 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?