Field guideDispatch N°03

What a reCAPTCHA score actually means — and where to set your threshold

Most spam apps present merchants with a dial from 0.1 to 0.9 and leave. Few explain what the number underneath represents, why it drifts, or what is traded away at each end. None mention that on a free-tier key, most positions on that dial are indistinguishable from their neighbours. This report includes a rack you can drag.

A Shopify merchant tuning their spam filter is adjusting a control with nine positions, of which roughly four produce distinct results. One of the nine blocks every submission the store will ever receive, customers included. The vendor help page generally says that 0.5 is a good starting point and moves on.

The number under that slider is a Google reCAPTCHA score. It is worth understanding precisely, for two reasons. Most wrong decisions merchants make about form spam begin with a wrong model of what the score measures. And the threshold debate is downstream of a fact established in our first dispatch: a solved reCAPTCHA v3 token sells for about $1.45 per thousand, so no setting on the dial makes a store unattackable. It sets a price and decides who pays it.

What the number is

Google's v3 documentation puts it plainly: reCAPTCHA "returns a score (1.0 is very likely a good interaction, 0.0 is very likely a bot)". The Cloud documentation adds the precision: the scale is quantised into eleven levels, running 0.0 to 1.0 in steps of 0.1. Until a billing account is attached to the project, a site receives only four of them: 0.1, 0.3, 0.7 and 0.9.

That is the most useful fact in this report and it is rarely repeated. An event log showing scores that are always one of four values is not broken. The store is on the free tier and Google is quantising. It also means most positions on the threshold dial are decorative, a point demonstrated below.

The score is not a probability. A 0.3 does not mean a 30 percent chance the visitor is human, and the four-value free tier is the proof: a function with four possible outputs is not estimating anything to two decimal places. Our working model, which Google does not publish and which should be attributed to us rather than to them, is that the score expresses similarity. It reports how closely an interaction resembles the traffic Google has learned to treat as legitimate on that specific site.

Google's Cloud documentation attaches reason codes to low scores. They are visible only on reCAPTCHA Enterprise, since a classic v3 verification returns a bare number and nothing else, but they indicate what the number is made of:

Two of those codes allege automation. The other three, covering traffic volume, unexpected usage patterns and insufficient data, report unfamiliarity. A submission can be scored down for being unusual on a particular site without anything about it being robotic. That distinction is the one merchants most often miss.

A low score does not mean robot. It means unfamiliar. A customer on a VPN is unfamiliar too.

Why it drifts, and why a new store's scores cannot be trusted yet

Look at that last reason code again: LOW_CONFIDENCE_SCORE. Google states that reCAPTCHA "learns by monitoring real traffic on your site", and that "scores in a staging environment and within 7 days of implementation might differ from the long-term production scores."

Note what that does and does not say. It states that the number is unreliable early on. It does not state which direction the error runs, and in our own logs it runs both ways. Some new stores score almost everything at 0.9, and the filter does nothing. Others block first customers who did nothing wrong. The result is a predictable failure mode.

The new-store trap

A merchant launches a store, installs spam protection on day one, and sets the threshold to something plausible such as 0.7. For the next fortnight the model has almost no traffic to compare against, so the number it returns is not yet information. Whichever way it errs, the merchant draws a conclusion: either the app does nothing, or it blocks a real customer and gets uninstalled.

Run lenient at 0.3 for a fortnight and treat that period as reporting rather than filtering. A filter that cannot yet be calibrated should be keeping records rather than making decisions.

The same effect explains scores that look wrong on established stores. A viral post sends unfamiliar traffic. A new market brings unfamiliar geography. A campaign lands a cohort of first-time visitors on a form. All unfamiliar, all scored down, none of them bots.

Drag the threshold

Below are twelve submissions of the kind a small Shopify store receives on an ordinary day, each carrying a score chosen to be plausible, sorted live against whatever threshold is set. Every score is one of the four values a free-tier key actually emits.

One detail matters more than it appears to. A submission scoring exactly the threshold is blocked rather than passed. Our own filter passes only when the score is strictly greater, and most implementations behave the same way. On a four-value key that boundary is hit constantly, so the rack applies the same rule.

Drag the dial and watch all three counters, along with the line above the list.

Figure 01 — The sorting rackN°01 · interactive
Spam threshold0.7
0.1 · LENIENT0.9 · STRICT
Spam stopped4
Spam through2
Customers lost2

0.9sarah.m@gmail.com — “Is this back in stock in size M?”contactCustomerPASSED
0.9partnerships@growth-agency.io — “Guest post opportunity for your blog”contactSpamPASSED
0.9james.k@outlook.com — “Love the mug, question about care”contactCustomerPASSED
0.7t.whitfield@bigpond.com — “Order #4471 arrived damaged”contactCustomerBLOCKED
0.7lena.b@gmx.de — newsletter signup after reading two postsnewsletterCustomerBLOCKED
0.7Residential-proxy automation — newsletter signup ×80, no bad historynewsletterSpamBLOCKED
0.3marc@protonmail.com — “Wholesale enquiry, 200 units” (on a VPN)contactCustomerBLOCKED
0.3Office network, 40 staff behind one IP — newsletter signupnewsletterCustomerBLOCKED
0.3seo-guru-backlinks@yandex.com — “Rank #1 on Google today”contactSpamBLOCKED
0.3bot-4471@tempmail.io — newsletter signup ×412 in nine minutesnewsletterSpamBLOCKED
0.1crypto-profits4u@mail.ru — “Dear sir, expand your business…”contactSpamBLOCKED
0.1Headless session — blog comment containing six linkscommentSpamBLOCKED
Three findings. The dial has nine positions and roughly four distinct behaviours: 0.1 and 0.2 are the same filter, as are 0.3 through 0.6, while 0.9 blocks everything including every customer. Tightening costs the merchant the wholesale enquiry and the office-network signup, whose only distinguishing feature is a VPN and a shared IP address. And no setting anywhere on the rack catches row two, a real person in a real browser typing a real pitch. Scoring is a bot filter rather than a spam filter. Scores are illustrative, chosen to be representative rather than measured.

The rack makes the trade explicit. A threshold is not a quality setting on which higher is better. It selects between two kinds of failure, and the choice depends on what the form is for.

One dial, several jobs

A single store-wide threshold is the wrong shape for the problem. Google's guidance says as much, recommending that action be taken "in the context of your site" and varied by action. A blocked blog comment costs a merchant nothing. A blocked checkout costs a customer, and possibly the relationship.

Including in our own product

Spambuster applies one threshold across every form a merchant enables, as do most apps in this category. The table below is therefore not a set of per-form settings that can be entered anywhere. It is a method for working out which form is the binding constraint. Select the value the most sensitive enabled form can tolerate, and switch off unneeded forms rather than attempting to tune them separately. We regard per-form thresholds as the correct design and do not yet offer them. The same question is worth putting to any vendor in this category.

Figure 02 — Suggested thresholds, by formN°02
How strict each form can afford to be — the strictest you can set is dictated by the most sensitive form you leave on
FormToleratesCost of a false positiveReasoning
Blog comments0.7NegligibleNobody churns over a rejected comment. Strictest useful setting.
Newsletter signup0.3–0.7Low, but realHigh-volume bot target. A lost signup is cheap; a poisoned list isn't.
Account registration0.3ModerateFake accounts accumulate. But a blocked signup is a blocked customer.
Login / password reset0.1–0.3HighLocking a real customer out of their account is the worst outcome here.
Contact form0.1–0.3HighEvery blocked message may be a pre-sale question. Prefer noise to silence.
Checkout / order0.1, or offSevereAt 0.1 the app is logging rather than filtering, which on this form is the point.
The gradient runs one way. The further a form sits from the money, the stricter a merchant can afford to be. Anything on the purchase path should be lenient enough to be close to invisible, catching flagrant automation and nothing more. Because one dial serves every form, the bottom row sets the ceiling: a store that leaves checkout protection enabled has already decided to run lenient everywhere.

The option this framing leaves out

Everything above treats the decision as binary: pass or reject. Google's own recommendation for the middle of the range is neither. It advises site owners to step up, by presenting a v2 checkbox, sending a confirmation email or holding the submission for review, and to take the action "in the background instead of blocking traffic."

That largely dissolves the dilemma this report is built around, which is why it belongs in the body rather than a footnote. A mid-scoring submission does not have to be gambled on. It can be asked one further question.

Spambuster does not do this today. The app passes or blocks, and allows a form to be switched to a v2 checkbox wholesale rather than conditionally. We regard graduated response as the right design and the single change that would most improve this category, our own part of it included. Until it exists, the threshold table above is the honest way to reason about a binary tool, and a step-up option should weigh heavily in favour of any competitor that offers one.

How to tune Set lenient, run a fortnight, then read your event log — in Spambuster that is the dashboard's event log; on a classic v3 key it is the admin console at google.com/recaptcha/admin. Find the score at which the character of the traffic changes. That number is your threshold, not the one in any guide including this one.

What scoring can't see

A score is one opinion, and Google does not publish what forms it. What we can report is what we observe. The spam reaching our customers with high scores attached tends to originate from fresh automation running on residential proxies, which is to say setups with no history for a reputation system to hold against them. Row six of the rack above is that case.

This is also where the solving market rejoins the argument. A purchased token certifies that a challenge was answered. It certifies nothing about the browser that submitted the form, and a service charging $1.45 per thousand is not going to simulate plausible mouse movement for each one. So a second and independent opinion is worth holding: what did the visitor's browser and body actually do?

Below are the signals our own detector collects, with the weights assigned to each. These are taken from our source rather than described in the abstract, because unfalsifiable claims about advanced AI detection are the reason this category is difficult to trust.

Figure 03 — Behavioural signal sheet, with weightsN°03
Automation surfaces — the browser admits it
30navigator.webdriver 25automation API present 20UA: headless 20UA: puppeteer 20UA: selenium 20UA: playwright 20UA: phantom 20UA: cypress 20UA: crawl 20UA: spider 15webdriver property tampered
Environment anomalies — the disguise doesn't fit
20Chrome UA, no chrome object 15zero window dimensions 15virtual GPU renderer 10zero plugins 10no languages array 10zero CPU cores 10zero network RTT 10pixel-ratio mismatch 5screen matches viewport exactly
Motor behaviour — nobody moves like that
20superhuman typing (<15ms/key) 15uniform keystroke rhythm 15uniform mouse velocity 10perfectly straight mouse paths 10synthetic bezier curves 10rapid clicks (<50ms apart) 10programmatic scrolling
Form timing — reading takes time
20form filled in under 300ms 15form filled in under 800ms
The number that makes the rest of this readable: the flag threshold is 50. Weights accumulate, each signal fires at most once per session, and a session is flagged when the running total reaches 50. So navigator.webdriver at 30, the heaviest single signal on the sheet, is not decisive on its own, and neither is anything else. Two signals from different families almost always are. That is deliberate: a person on an accessibility tool, or a browser with an unusual extension, can trip one signal. They should not trip three.

Two honest caveats. The eight user-agent terms accumulate rather than stopping at the first match, so a user agent containing two of them clears 50 by itself. And crawl and spider will fire on well-behaved search-engine bots, which is acceptable here only because a crawler does not submit forms. As shipped in client v301, July 2026.
On publishing the weights. The argument against is that attackers will tune around them, and it has force. The detectors run in the attacker's own browser and are inspectable, but this table still saves a determined one an afternoon. We judge it the right trade. Anyone willing to do that work is already through this layer, which is why it is not the only one. Merchants deciding whether to trust a filter with their contact form deserve to see what they are buying.
Both opinions, on every form

Google's scoring and the signals above, working together

Spambuster runs invisible reCAPTCHA scoring on all seven Shopify form types, with one threshold set between 0.1 and 0.9. The Premium plan adds the behavioural signal sheet and honeypot traps. Every decision is logged with the score and the reason. It will not stop the guest-post pitch in row two of the rack. Nothing sold in this category will.

Start 14-day free trial →
No credit card to try·Basic $3 · Premium $12·Billed through Shopify

The cheapest layer in the stack

Before any of the above, there is a control that costs nothing, adds no latency and asks nothing of a customer: a field only a script can see.

Figure 04 — Honeypot, in cross-sectionN°04
What a person sees
Name
Email address
Message
SEND →
Three fields, filled by hand, over 34 seconds.
→ Delivered.
What a script sees in the DOM
Name
Email address
Company URL
Message
SEND →
Four fields. Automation fills every input it finds, including the one positioned off-screen.
→ Returned to sender.
The trap only works if it's invisible to people and visible to machines. That means positioning it off-screen, or hiding it in ways a scraper will not parse. Not type="hidden", which any competent bot skips, and not anything a screen reader will read aloud to a real customer. Ours is scoped per form type, and every trigger is counted so you can see which forms are being probed.

Honeypots have one property worth more than it appears. They produce very few false positives. Almost no real customer completes a field they cannot see. The exception is password managers and browser autofill, which will populate an off-screen input if its name looks familiar, and that exception is the whole craft of building one. The trap field needs an unusual name and no recognisable autocomplete attribute, or the store will block precisely the customers organised enough to use a password manager.

Built correctly, it is the one layer that can run at full strictness without costing a sale, which is the reverse of the trade the threshold dial imposes.

Honeypots also have a hard ceiling. A bot written for one specific form, rather than for forms in general, will skip the trap. None of them stop the human sending guest-post pitches. This is the recurring finding of our first dispatch: every control in this category addresses one route and leaves the others open.

The setting nobody asks about

One final matter, and it is the question we would put to any vendor in this category ahead of the pricing question.

What happens when verification fails?

Every scoring system depends on a network round trip, and networks fail. When one does, the filter must choose between rejecting a submission it could not verify, which is to fail closed, and allowing it, which is to fail open.

Most vendors answer this as though it were one question. It is two, and they warrant different answers. Ours follow, in specific terms, because a vague answer is of no use to a merchant.

The distinction that matters is not open against closed. It is visible against silent. The first failure is ours, is invisible to the customer, and resolves itself. The second is visible, tells the customer something is wrong, and is recoverable: they can retry, or email the store directly. What no merchant should accept is a filter that fails closed silently, rejecting real orders with no error that anyone ever sees.

Any vendor in this category should be able to say which of those two behaviours applies, and to which failure. Ours are stated above. A competitor unable to point at the equivalent paragraph in their own documentation has answered the question in a different way.

About this report

Spambuster builds form-spam protection for Shopify storefronts. Figure 03 lists the signals and weights taken from our production detector as shipped in client v301. Scores in Figure 01 are illustrative, chosen to represent traffic patterns we observe rather than measured from any single store. Prices quoted are vendors' published rates, checked 26 July 2026. Corrections to contact@jola.com.au

Set the dial once. Forget the spam.

Invisible Google reCAPTCHA scoring on every Shopify storefront form, a threshold you control, honeypot traps and behavioural signals on Premium, and a full log of every decision with the score behind it.

Install on Shopify · free for 14 days →
No credit card to try·2-minute setup·Cancel anytime