Challenge
Still on reCAPTCHA? Stop asking people to solve puzzles
Image puzzles lose you real people. Challenge runs a proof-of-work check in the background instead, in the visitor's browser, after they press Send. Nobody sees it.
No credit card required.
Fig. 1Cost of the check
CAPTCHAs. Same job, different cost
Both stop bots. Only one asks the person filling in your form to do the work.
- Friction
- A visible CAPTCHAA puzzle on every submit. Some people try twice; some close the tab.
- ChallengeNothing to solve. The browser does it in the background after Send.
- Accessibility
- A visible CAPTCHAImage and audio puzzles shut out many people with visual, hearing or cognitive disabilities.
- ChallengeKeyboard and screen reader users submit like everyone else.
- Cookies
- A visible CAPTCHAEU regulators have fined sites for loading reCAPTCHA without consent.
- ChallengeChallenge sets no cookies and builds no profile of the visitor.
- Who pays
- A visible CAPTCHAThe person filling in your form.
- ChallengeThe bot, in CPU time on every submission.
| A visible CAPTCHA | Challenge, the invisible one | |
|---|---|---|
| Friction | A puzzle on every submit. Some people try twice; some close the tab. | Nothing to solve. The browser does it in the background after Send. |
| Accessibility | Image and audio puzzles shut out many people with visual, hearing or cognitive disabilities. | Keyboard and screen reader users submit like everyone else. |
| Cookies | EU regulators have fined sites for loading reCAPTCHA without consent. | Challenge sets no cookies and builds no profile of the visitor. |
| Who pays | The person filling in your form. | The bot, in CPU time on every submission. |
How it works. Three steps, none of them for your visitor
The visitor presses Send
Nothing runs while the page loads. When the visitor presses Send, the browser library fetches a signed challenge and solves it.The form carries the solution
The solution rides along as one hidden field,_botpoison. Nothing else changes for the visitor.You check it with one call
Your server passes the field to /verify. A valid solution goes through; a missing or forged one does not.
After. Your form, exactly as you built it
No widget, no checkbox, no grid of crosswalks. The check runs between Send and the form going out, and only bots ever notice it was there.
Fig. 2What your visitor sees
Switching. Off your CAPTCHA in three edits
Swap the script, the key and the check. Your form keeps its fields, its layout and its thank-you page.
Swap the script
Remove Google's script tag and add ours. It loads async, like theirs did.
<script src="https://www.google.com/recaptcha/api.js" async defer></script><script src="https://unpkg.com/@botpoison/browser" async></script>Swap the key on the form
Delete the widget's div and put your public key on the form itself. Nothing renders in its place.
<form action="/contact" method="POST" data-botpoison-public-key="pk_xxxxxxxx"> <textarea name="message"></textarea> <div class="g-recaptcha" data-sitekey="your_site_key"></div> <button type="submit">Send</button></form>Swap the check on your server
Post the same form to /verify: secretKey and _botpoison in place of secret and g-recaptcha-response, and ok in place of success.
const google = await fetch("https://www.google.com/recaptcha/api/siteverify", {const verified = await fetch("https://api.botpoison.com/verify", { method: "POST", body: new URLSearchParams({ secret: process.env.RECAPTCHA_SECRET, response: req.body["g-recaptcha-response"], secretKey: process.env.BOTPOISON_CHALLENGE_SECRET_KEY, solution: req.body._botpoison, }),});const { success: ok } = await google.json();const { ok } = await verified.json();if (!ok) return res.status(403).end();$google = file_get_contents("https://www.google.com/recaptcha/api/siteverify", false, stream_context_create(["http" => [$verified = file_get_contents("https://api.botpoison.com/verify", false, stream_context_create(["http" => [ "method" => "POST", "header" => "Content-Type: application/x-www-form-urlencoded", "content" => http_build_query([ "secret" => getenv("RECAPTCHA_SECRET"), "response" => $_POST["g-recaptcha-response"], "secretKey" => getenv("BOTPOISON_CHALLENGE_SECRET_KEY"), "solution" => $_POST["_botpoison"], ]),]]));$ok = json_decode($google)->success;$ok = json_decode($verified)->ok;if (!$ok) { http_response_code(403); exit; }google = requests.post("https://www.google.com/recaptcha/api/siteverify", data={ "secret": os.environ["RECAPTCHA_SECRET"], "response": request.form["g-recaptcha-response"],verified = requests.post("https://api.botpoison.com/verify", data={ "secretKey": os.environ["BOTPOISON_CHALLENGE_SECRET_KEY"], "solution": request.form["_botpoison"],})ok = google.json()["success"]ok = verified.json()["ok"]if not ok: abort(403)res, err := http.PostForm("https://www.google.com/recaptcha/api/siteverify", url.Values{ "secret": {os.Getenv("RECAPTCHA_SECRET")}, "response": {r.FormValue("g-recaptcha-response")},res, err := http.PostForm("https://api.botpoison.com/verify", url.Values{ "secretKey": {os.Getenv("BOTPOISON_CHALLENGE_SECRET_KEY")}, "solution": {r.FormValue("_botpoison")},})if err != nil { http.Error(w, "", http.StatusBadGateway) return}var out struct{ Ok bool `json:"success"` }var out struct{ Ok bool `json:"ok"` }json.NewDecoder(res.Body).Decode(&out)if !out.Ok { http.Error(w, "", http.StatusForbidden) return}curl "https://www.google.com/recaptcha/api/siteverify" \ --data-urlencode "secret=$RECAPTCHA_SECRET" \ --data-urlencode "response=<the g-recaptcha-response field>"# {"success": true, ...}curl "https://api.botpoison.com/verify" \ --data-urlencode "secretKey=$BOTPOISON_CHALLENGE_SECRET_KEY" \ --data-urlencode "solution=<the _botpoison field>"# {"ok": true, ...}Private by design. Nothing to solve. No cookies
No image puzzles
Accessible by default
No cookies
No visitor profile
Works the way forms work
Checked in the EU
Better together. Challenge stops the bots. Classify reads the rest
Use one, or both. Together they stop bulk spam before it is sent, and read what still gets through after.
01Before the submit
Challenge
Invisible CAPTCHA alternative
Turns away scripts that skip the check, and makes every bot submission cost CPU time. Nobody sees a puzzle.
02After the submit
Classify
Spam classifier
Reads what got through and flags the pitches and scams in it, whoever wrote them.
Free to start. One plan covers both products
Every plan carries an allowance for each product. Using one never eats into the other, and no credit card is needed to start.
See plans and priceschallenges a month
- Free
- 1,000
- Standard
- 5,000
- Plus
- 50,000
- Pro
- 250,000
In short. The facts about Challenge
Botpoison Challenge is an invisible alternative to CAPTCHAs for web forms. Visitors see no puzzle, checkbox or widget.
When a visitor presses Send, their browser runs a small proof-of-work check and sends the solution with the form.
Your server verifies the solution with one call to the Botpoison API.
Challenge sets no cookies.
The Free plan includes 1,000 challenges a month, with no credit card.
There is no puzzle for a person to get wrong. A visitor can only be stopped if their browser cannot run the check: JavaScript is off, something blocks our script or our API, or the browser is too old to run it. Listen for the
botpoison-challenge-errorevent to tell them what happened.The challenge sets no cookies, stores nothing in the browser and builds no profile of the visitor. Whether your site needs a banner for anything else is a question for your own counsel.
A form like any other. A bot that skips the check sends no solution and your server turns it away. One that solves it pays the cost on every single submission.
No. A person sending a pitch passes the check like any visitor. That is what Classify is for: it reads the message and flags it.
The visitor's browser cannot fetch a challenge, so the form fires
botpoison-challenge-errorinstead of sending. Use it to show a message or another way to reach you. On your server,verify()throws when it cannot reach us, and answersok: falsewith the messageInternal server errorwhen the fault is ours. You decide whether to accept or hold those submissions.