Getting Started
From zero to production with NeoCaptcha in under 15 minutes.
Updated 2/22/2026
Getting Started with NeoCaptcha
This guide takes you from a fresh account to a verified, production-ready captcha flow.
What you are building
By the end of this guide you will have:
- A NeoCaptcha widget rendered on your form.
- A secure backend verification route.
- A submission flow that rejects invalid or replayed tokens.
Prerequisites
- A NeoCaptcha account at neocaptcha.com.
- A website or app where users submit a form (signup, login, contact, checkout, etc.).
- Backend runtime capable of making HTTPS requests (Node.js, Python, Go, Java, .NET, etc.).
Step 1: Create a site in the dashboard
In the NeoCaptcha dashboard:
- Create a Site.
- Add allowed domains:
- Local:
localhostand127.0.0.1 - Production: your exact hostnames (for example
app.example.com)
- Local:
- Save and copy:
- Site Key (public; safe for frontend)
- Site Secret (private; backend only)
Keep your Site Secret out of frontend code, public repos, and browser-accessible env vars.
Step 2: Add the captcha to your page
Choose one of two integration approaches.
Option A — CDN (no build step)
Add the script tag once per page, then add a container element with your site key. The script auto-discovers the element and mounts the widget inside it.
<script src="https://cdn.neocaptcha.com/v1/browser.js" defer></script>
<div id="neocaptcha" sitekey="YOUR_SITE_KEY"></div>Listen for DOM events on the container:
const captcha = document.getElementById('neocaptcha');
let captchaToken = null;
captcha.addEventListener('neocaptcha:complete', (e) => {
captchaToken = e.detail.verifyToken;
document.getElementById('submit-btn').disabled = false;
});
captcha.addEventListener('neocaptcha:error', () => {
captchaToken = null;
document.getElementById('submit-btn').disabled = true;
});
captcha.addEventListener('neocaptcha:expire', () => {
captchaToken = null;
document.getElementById('submit-btn').disabled = true;
});Option B — npm package (init)
Install the package:
npm install @neocaptcha/sdkCall init with a container element and your site key:
import { init } from '@neocaptcha/sdk';
let captchaToken: string | null = null;
init({
container: document.getElementById('captcha-slot')!,
siteKey: process.env.NEOCAPTCHA_SITE_KEY!,
onComplete: (verifyToken) => {
captchaToken = verifyToken;
document.getElementById('submit-btn')!.removeAttribute('disabled');
},
onError: () => {
captchaToken = null;
},
onExpire: () => {
captchaToken = null;
},
});Step 3: Send token with your form
When the user submits your form, include the verifyToken in your request body.
await fetch('/api/signup', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
email,
password,
captchaToken,
}),
});Step 4: Verify on your server (required)
Your server must call the NeoCaptcha verify endpoint before processing sensitive actions.
const verifyRes = await fetch('https://neocaptcha.com/api/v1/verify', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
solution: req.body.captchaToken,
siteKey: process.env.NEOCAPTCHA_SITE_KEY,
secret: process.env.NEOCAPTCHA_SITE_SECRET,
expectedOrigin: 'https://app.example.com',
}),
});
const verification = await verifyRes.json();
if (!verification.valid) {
return res.status(400).json({ error: 'Captcha verification failed' });
}If verification succeeds, continue your business logic.
Frontend Examples
CDN
<!DOCTYPE html>
<html>
<head>
<title>NeoCaptcha Example</title>
<script src="https://cdn.neocaptcha.com/v1/browser.js" defer></script>
</head>
<body>
<form id="contact-form">
<label for="email">Email:</label>
<input type="email" id="email" name="email" required />
<label for="message">Message:</label>
<textarea id="message" name="message" required></textarea>
<div id="neocaptcha" sitekey="YOUR_SITE_KEY"></div>
<button type="submit" id="submit-btn" disabled>Submit</button>
</form>
<script>
let captchaToken = null;
const captcha = document.getElementById('neocaptcha');
captcha.addEventListener('neocaptcha:complete', (e) => {
captchaToken = e.detail.verifyToken;
document.getElementById('submit-btn').disabled = false;
});
captcha.addEventListener('neocaptcha:error', () => {
captchaToken = null;
document.getElementById('submit-btn').disabled = true;
});
captcha.addEventListener('neocaptcha:expire', () => {
captchaToken = null;
document.getElementById('submit-btn').disabled = true;
});
document.getElementById('contact-form').addEventListener('submit', (e) => {
if (!captchaToken) {
e.preventDefault();
alert('Please complete the captcha first');
}
});
</script>
</body>
</html>npm (init)
import { init } from '@neocaptcha/sdk';
let captchaToken: string | null = null;
init({
container: document.getElementById('captcha-slot')!,
siteKey: process.env.NEOCAPTCHA_SITE_KEY!,
onComplete: (verifyToken) => {
captchaToken = verifyToken;
},
onError: () => {
captchaToken = null;
},
onExpire: () => {
captchaToken = null;
},
});
document.getElementById('contact-form')!.addEventListener('submit', async (e) => {
e.preventDefault();
if (!captchaToken) {
alert('Please complete the captcha first');
return;
}
const email = (document.getElementById('email') as HTMLInputElement).value;
const message = (document.getElementById('message') as HTMLTextAreaElement).value;
await fetch('/api/contact', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, message, captchaToken }),
});
});Production checklist
- Domain allowlist configured for all production hostnames.
- Site Secret only available in backend environment variables.
- All sensitive POST routes verify captcha before action.
- Replay prevention enabled (single-use token behavior).
- Logs include challenge ID/token ID for incident investigation.
- Monitoring alerts on spikes in invalid captcha attempts.
Next steps
- Read Server Verification for backend architecture and anti-abuse patterns.
- Read Browser for full CDN and
initoption reference. - Read React for SPA/Next.js integration patterns.