All articles

reCAPTCHA v2 vs v3: Differences, Scores, and Why Spam Still Gets Through

Published Updated By GoodInbound team

reCAPTCHA v2 shows a checkbox or challenge; v3 returns a 0.0 to 1.0 score you act on. How each works, picking a threshold, using both, and why spam gets past.

reCAPTCHA v2 asks visitors to prove they're human: they tick an "I'm not a robot" checkbox and sometimes solve an image challenge (with invisible v2, only suspicious visitors see one). reCAPTCHA v3 never interrupts anyone. It returns a score from 0.0 (very likely a bot) to 1.0 (very likely a person), and you decide what to do with it. v2 gives you a pass or a fail; v3 gives you a number and the job of acting on it.

Neither version stops a person typing a sales pitch into your form, which is why so many forms with reCAPTCHA still fill up with junk. The full playbook is in how to stop contact form spam.

GoodInbound Lead Screen catches the spam that gets past reCAPTCHA v2 and v3. Add one script to your existing form, with no CAPTCHA and nothing for the visitor to do:

  • Stops bots and fake sign-ups without a challenge, using how the form was filled in, the network, and the email address.
  • Catches sales pitches from people and AI, which pass both reCAPTCHA versions because a person can tick any box.
  • Labels every submission with a reason, and nothing is deleted, so a real lead screened out by mistake is one click from your inbox.
  • Sends only real leads on, to your CRM and ad platforms, with email, Slack, or Telegram alerts for just the labels you choose.

The free plan screens 500 submissions a month; the message check that catches sales pitches is on Pro. Start free, or read on for the full comparison.

reCAPTCHA v2 vs v3 at a glance

reCAPTCHA v2 reCAPTCHA v3
What the visitor sees A checkbox (or a badge, for invisible v2); an image challenge when Google is unsure Nothing to do, just a badge
What you get back Pass or fail (success) A score from 0.0 to 1.0, plus the action name
Who decides Google, by passing or challenging You, with a threshold per action
Where it runs On the form On forms and actions, and Google recommends other pages too
Setup Two lines of HTML for the checkbox A script call per action, plus server code that reads the score
Tuning Security preference slider Your threshold; Google suggests 0.5 to start
Accessibility Image and audio challenges are hard for some people No challenge unless you add one
Google Cloud console name Website • checkbox Website • score
Stops a person typing spam No No

How reCAPTCHA v2 works

v2 has two web versions (a third, for Android apps, is a separate SDK).

Checkbox. The visitor clicks "I'm not a robot". Google either passes them straight away or shows a challenge, usually picking out images such as street signs. It's the simplest to add:

<script src="https://www.google.com/recaptcha/api.js" async defer></script>

<form action="/contact" method="POST">
  <!-- your fields -->
  <div class="g-recaptcha" data-sitekey="YOUR_V2_SITE_KEY"></div>
  <button type="submit">Send</button>
</form>

When the visitor passes, the widget adds a g-recaptcha-response token to the form for your server to check.

Invisible. There's no checkbox. The check runs when the visitor clicks your submit button, and by default only the most suspicious traffic gets a challenge. A reCAPTCHA badge sits in the corner instead.

For both, the admin console has a Security preference slider from "Easiest for users" to "Most secure", which changes how often people are challenged.

To verify, your server posts secret, response (the token), and optionally remoteip to https://www.google.com/recaptcha/api/siteverify. For v2 the answer looks like this:

{
  "success": true,
  "challenge_ts": "2026-10-10T09:14:03Z",
  "hostname": "example.com",
  "error-codes": []
}

success says whether the token was valid for your site, and hostname is where it was solved. Each token is valid for two minutes and can be verified once; a stale or reused one returns timeout-or-duplicate.

How reCAPTCHA v3 works

v3 has no checkbox and no challenge. You call it when something happens (a submit, a login, a checkout) and it returns a token. Your server sends the token to the same siteverify endpoint and gets a score back.

<script src="https://www.google.com/recaptcha/api.js?render=YOUR_V3_SITE_KEY"></script>
<script>
  form.addEventListener("submit", (event) => {
    event.preventDefault();
    grecaptcha.ready(() => {
      grecaptcha.execute("YOUR_V3_SITE_KEY", { action: "contact" }).then((token) => {
        // add the token to the form data and send it to your server
      });
    });
  });
</script>

Four points from Google's docs matter in practice:

  • Call execute on the action, not on page load. Tokens expire after two minutes, so one fetched when the page opened is often dead by the time someone finishes typing.
  • Name every action. Names can use only letters, numbers, slashes, and underscores, and must not be user-specific. They give you a breakdown of your top ten actions in the admin console, and Google uses them to judge risk in context.
  • Run it on more than the form. Google says reCAPTCHA works best when it sees both legitimate and abusive traffic, and recommends running it in the background of pages too.
  • You decide what a score means. Google suggests a default threshold of 0.5, and advises acting behind the scenes (moderation, email verification, a challenge) rather than blocking traffic outright.

The v3 response adds score and action:

{
  "success": true,
  "score": 0.9,
  "action": "contact",
  "challenge_ts": "2026-10-10T09:14:03Z",
  "hostname": "example.com",
  "error-codes": []
}

Check that action is the one you expected for that form. Google says a mismatch means someone is trying to falsify actions.

v2 and v3 keys aren't interchangeable

You choose the type when you register a key, and it only works as that type. Put a v3 site key in a v2 checkbox and the widget shows "ERROR for site owner: Invalid key type". A v2 key's siteverify response has no score. Each key has its own secret, so verify v3 tokens with the v3 secret and v2 tokens with the v2 secret.

This catches people out when switching a WordPress or form plugin from v2 to v3: the plugin setting and both keys have to change together. In the Google Cloud console, v3 keys show as "Website • score" and v2 checkbox keys as "Website • checkbox". You can no longer create new invisible v2 keys there.

Using v2 and v3 together

The common pattern: run v3 on every submit, accept high scores silently, and show the v2 checkbox only to visitors who score low. Most people never see a challenge, and the doubtful get a chance instead of a block. You need one key of each type. One api.js serves both: load it with the v3 key, and render the checkbox explicitly with the v2 key.

<script src="https://www.google.com/recaptcha/api.js?render=YOUR_V3_SITE_KEY"></script>

<form id="contact" action="/contact" method="POST">
  <!-- your fields -->
  <div id="checkbox"></div>
  <button type="submit">Send</button>
  <p id="status" role="status"></p>
</form>

<script>
  const form = document.getElementById("contact");
  const status = document.getElementById("status");
  let checkboxShown = false;

  form.addEventListener("submit", (event) => {
    event.preventDefault();
    // Second attempt: the form now carries the checkbox's g-recaptcha-response
    if (checkboxShown) return send(new FormData(form));
    grecaptcha.ready(async () => {
      const data = new FormData(form);
      data.append("v3_token", await grecaptcha.execute("YOUR_V3_SITE_KEY", { action: "contact" }));
      send(data);
    });
  });

  async function send(data) {
    const result = await (await fetch(form.action, { method: "POST", body: data })).json();
    if (result.challenge && !checkboxShown) {
      grecaptcha.render("checkbox", { sitekey: "YOUR_V2_SITE_KEY" });
      checkboxShown = true;
      status.textContent = "One more step: tick the box and send again.";
    } else {
      status.textContent = result.ok ? "Thanks, we'll be in touch." : "That didn't send.";
    }
  }
</script>

On the server, verify whichever token arrived with the matching secret:

async function siteverify(secret, token, ip) {
  const res = await fetch("https://www.google.com/recaptcha/api/siteverify", {
    method: "POST",
    body: new URLSearchParams({ secret, response: token, remoteip: ip }),
  });
  return res.json();
}

async function handleContact(form, ip) {
  const v2Token = form.get("g-recaptcha-response");
  const v3Token = form.get("v3_token");

  if (v2Token) {
    const r = await siteverify(process.env.RECAPTCHA_V2_SECRET, v2Token, ip);
    return r.success && r.hostname === "example.com" ? save(form) : { ok: false };
  }

  if (v3Token) {
    const r = await siteverify(process.env.RECAPTCHA_V3_SECRET, v3Token, ip);
    console.log("recaptcha", r.score, r.action, r["error-codes"]); // log before you enforce
    if (!r.success || r.action !== "contact" || r.hostname !== "example.com") return { ok: false };
    return r.score >= 0.5 ? save(form) : { ok: false, challenge: true };
  }

  return { ok: false }; // no token at all: never accept
}

The last line matters most. Google Cloud also offers a key type that does this for you: a policy-based challenge key shows a challenge when the score falls below a threshold you set.

Choosing a v3 threshold

Don't enforce anything on day one. Google says scores in staging, and in the first seven days, can differ from long-term production scores, and suggests running v3 without acting first.

  1. Log first. For a week or two, store each submission's score and action next to what it turned out to be: a real enquiry, a bot, or a pitch.
  2. Expect four values. Google's Cloud docs describe 11 score levels, but without billing on the project you only see 0.1, 0.3, 0.7, and 0.9. So 0.5 splits the 0.3s from the 0.7s, and any threshold from 0.4 to 0.6 behaves the same.
  3. Set thresholds per action. A newsletter sign-up and a quote request don't carry the same risk.
  4. Route low scores, don't drop them. Send them to review or show the checkbox. A real buyer silently dropped is a lead you never learn you lost.
  5. Look at what scores well. Your log will show pitches typed by real people scoring 0.9. No threshold fixes that.

Privacy and accessibility

Accessibility. v2's challenges are the issue. Google says the widget works with major screen readers and offers an audio challenge, but its own Cloud docs admit CAPTCHAs "are not accessible for all users", and it recommends score-based keys for sites with accessibility requirements. If you add a v2 fallback, keep another way to reach you on the page.

Privacy. Both versions load a script that analyzes, in Google's words, "user behavior, device information, IP addresses, and historical interaction patterns". Google says the data is used only for reCAPTCHA's operation and security, not personalized advertising. Also:

  • reCAPTCHA sets a _GRECAPTCHA cookie, which Google calls necessary. To avoid other www.google.com cookies, you can load it from www.recaptcha.net.
  • v3 sees more. Run across many pages, as Google suggests, it observes far more of each visit than a checkbox on one form.
  • Google says moving keys to Google Cloud shifts its role from data controller to data processor. Whether that settles your consent banner is for whoever owns compliance.
  • You may hide the badge only if "This site is protected by reCAPTCHA." appears visibly in the user flow.

Where reCAPTCHA Enterprise and Google Cloud fit now

Much has changed since most v2 vs v3 guides were written. Checked October 2026:

  • All keys live in Google Cloud. Google stopped issuing Classic keys in Q3 2024, began moving them into automatically created Google Cloud projects in Q4 2025, and planned to finish in Q1 2026, locking API access for keys without a project. Existing v2 and v3 keys keep working with no code changes, siteverify included.
  • New name. reCAPTCHA is now sold as part of Google Cloud Fraud Defense, and the v2 and v3 pages on developers.google.com are marked deprecated in favor of the Cloud docs.
  • A smaller free tier. Older guides quote 1 million free calls a month; that was the Classic limit. The free Essentials tier now covers 10,000 assessments per calendar month, shared across your whole organization. With billing on (Premium), 10,001 to 100,000 a month cost an $8 flat fee, then $1 per 1,000. Enterprise is a subscription with a 12-month minimum.
  • Google prefers v3. Its Cloud docs say it doesn't recommend checkbox keys because they add friction "and don't significantly improve accuracy".

Which should you use?

  • v3, if you want no friction and will write the server code to log and act on scores.
  • v2 checkbox, if you want a simple pass or fail, or your plugin only supports v2.
  • v3 with a v2 fallback, for silence for most visitors and a challenge for the doubtful.
  • Something else, if privacy, accessibility, or depending on Google rules reCAPTCHA out. Turnstile vs reCAPTCHA compares Cloudflare's free option, and reCAPTCHA alternatives covers the rest.

Why spam still gets through either version

If you run reCAPTCHA and still get spam, one of these is usually the cause (more in why reCAPTCHA isn't stopping your spam):

  1. The token is never verified on the server. Unless your backend checks the token with siteverify and rejects failures, a bot can skip the widget entirely. Some themes and plugins render reCAPTCHA but check it badly.
  2. The v3 score is ignored. success: true only means the token is valid. A bot scoring 0.1 still gets it, so code that checks only success makes v3 useless.
  3. The endpoint accepts posts without a token. Bots rarely use your page; they post straight to your form handler. If a missing token means "skip the check", reCAPTCHA is decoration.
  4. You're over the free quota. Google says that for migrated keys, siteverify requests over quota fail open: success: true with a score of 0.9. Every bot passes until the month resets.
  5. Solving services. Google's Cloud docs say CAPTCHAs "are also under threat from paid attackers who can solve all types of challenges", and bot tools work hard to look like real browsers to v3.
  6. People. The big one for business forms. An SEO agency, a dev shop, a link seller, or a recruiter is a real person on a real browser. They tick the box, score 0.9, and the pitch lands in your inbox. Neither version reads the message.

What to add on top of reCAPTCHA

reCAPTCHA asks one question: is this a bot? Spam also comes from fake identities and from real people selling things, so add a check for each.

A honeypot and a time trap. Both are invisible and catch simple bots that post straight to your endpoint:

<!-- Honeypot: hidden from people, filled in by simple bots -->
<input name="website" type="text" tabindex="-1" autocomplete="off" aria-hidden="true" style="display:none">
<!-- Time trap: when the form was shown -->
<input name="shown_at" type="hidden">
<script>document.querySelector('[name="shown_at"]').value = Date.now();</script>
const shownAt = Number(form.get("shown_at"));
if (form.get("website")) return reject();                       // a person never sees this field
if (!shownAt || Date.now() - shownAt < 3000) return reject();   // missing, or under 3 seconds

A bot written for your form can fake the timestamp, so set and sign it on the server if this check matters.

Email checks. Reject malformed addresses, check that the domain receives mail, and flag disposable inboxes. Test a suspicious address with the free disposable email checker.

Screening the message. The only fix for point 6: something has to read what was written and weigh it against what you sell. GoodInbound's message check does that, labeling a pitch spam with a one-line reason such as "Offers link-building services", and it's part of Pro. Paste a submission you've received into the free form spam checker to see its label, or read how to stop sales pitches through your contact form.

Keep reCAPTCHA, or replace it

GoodInbound Lead Screen works on the form you already have, with or without reCAPTCHA: one script tag on HubSpot, Webflow, WordPress, Framer, or plain HTML. It checks your rules, how the form was filled in, the network, and the email identity, and on Pro it reads the message. Every submission gets a label (lead, review, spam, junk, or bot) with a reason, and nothing is deleted.

Keep reCAPTCHA and its score is one signal among several. To drop the challenge altogether, see how to stop contact form spam without a CAPTCHA. Either way, the full checklist is in how to stop contact form spam.

FAQ

Is reCAPTCHA v3 better than v2?

For most sites, yes: no visible challenge, more control, and it's what Google now recommends. The cost is work: you verify the score on your server, choose thresholds, and decide what happens to low scores.

What is a good reCAPTCHA v3 score?

1.0 is very likely a person and 0.0 very likely a bot. Google suggests 0.5 as a starting threshold. Without billing you'll only see 0.1, 0.3, 0.7, and 0.9, so most sites treat 0.7 and up as fine and 0.3 and below as doubtful. Log scores for a week or two before enforcing.

Can I use reCAPTCHA v2 and v3 on the same page?

Yes, with two separate keys. Run v3 on submit and render the v2 checkbox only when the score is low, as in the example above.

Can I use my v2 keys for v3?

No. A key is registered as one type, and a v3 key in a v2 widget shows "ERROR for site owner: Invalid key type". Create a score-based key in the Google Cloud console, then update both the site key and the secret.

Is reCAPTCHA still free?

Up to 10,000 assessments a month per organization, checked October 2026. Beyond that you need billing, with an $8 flat fee for up to 100,000 a month. Without billing, migrated keys using siteverify fail open once over quota.

Does reCAPTCHA v3 ever show a challenge?

Not by itself; it only returns a score. To challenge low scorers, add the v2 checkbox as a fallback or use a policy-based challenge key in Google Cloud.

Why am I still getting spam with reCAPTCHA?

Usually the token isn't verified, the score is ignored, the endpoint accepts posts without a token, or the spam comes from a person. Neither version reads the message, so sales pitches typed by people pass both.

Switch in 5 minutes

Setup with your agents