SMS pumping

Stop SMS pumping before the code is sent

SMS pumping turns your phone verification form into someone else's revenue: a script submits thousands of numbers, and every code you send is billed to you. FindIP Shield scores each submit by the network it came from, caps how many sends one address or browser can trigger, and gives your server a check to run before it calls the SMS provider. It never sees the phone number.

Free Preview · 10,000 events per site per day · No credit card · Terms apply

Hosting / datacenter Proxy Tor Malicious IP Submits per IP Country
The problem

Every code you send is billed, whoever asked for it.

Artificially inflated traffic, or SMS pumping, is simple: a script fills your one-time-code form with numbers on expensive routes, and the operators on those routes share the termination fees. The bill arrives at the end of the month, with no account ever created.

A phone number check cannot help much: the numbers are real. Per-number limits do nothing when each submit uses a new number. What the numbers have in common is how they arrived: from scripts, on hosting networks and proxy pools, many submits from one address in minutes.

That arrival pattern is visible before the send. Shield reports it on the form, and your server makes the last call before any message is paid for.

One event, explained

What one of these looks like in Shield

Every visit and form submit is recorded with a score, the reasons behind it, the session around it, and what the page did. A hosting network or a VPN is used legitimately every day; the reasons are there so you can judge, not a verdict on a person.

A recorded eventSample data

signup_attempt

86 · critical

/verify-phone · a moment ago

Why this risk score
  • hosting_networkThe IP belongs to hosting infrastructure rather than a consumer access network.
  • proxy_detectedThe current IP is a known anonymizing proxy.
  • signup_attemptA sensitive action: the reasons above weigh more here than on a page view.
Session
Network
AS64502 · hosting · Singapore
Form
phone verification · has a phone field (value never sent)
Custom rule
max 3 submits per IP in 10 minutes · matched
Time from first page to submit
2 seconds
What happened
Shield recommended
block
A verdict for your site to use. On its own it stops nothing.
Observed in the page
Stopped in the page
The velocity rule stopped the submit in the browser, and the server's own check also received a block recommendation. No code was sent and no message was billed.
How Shield helps

From the first pumped submit to an SMS bill you can predict

Every step happens before the send, which is the only moment a code costs you nothing.

Start by monitoring

Install Shield on the page that asks for a phone number. Nothing changes for your users. Every submit arrives with its score and network; Shield records that the form has a phone field, never the number.

Read the reasons

Pumping runs from scripts: hosting networks, proxy pools, many submits from one address in minutes, almost no time on the page. Shield names the network reasons on each event, with the session around it.

Add a velocity rule

A submit-velocity rule per IP or per visitor, for example more than three sends in ten minutes, stops or challenges the extra submits in the page. A country rule covers visitors from where you have no customers.

Challenge before the send

Give the phone form a response for high scores: a Cloudflare Turnstile check costs a real person one click and a script the run. Chosen per form, changeable at any time, every outcome logged.

Verify before you send the code

In the handler that calls your SMS provider, verify the session with your secret key first. No code is sent on a block recommendation, and unknown is never treated as safe.

What Shield shows you

The evidence, then the response you choose

Where each send request came from

Every submit of the phone form arrives with a 0 to 100 score and its network reasons: hosting_network, proxy_detected, tor_detected, malicious_ip. Shield records that the form has a phone field and never reads the number.

A cap per address or per browser

A submit-velocity rule stops or challenges the N-th submit from one IP or one visitor in your window. A country rule covers places you do not serve. Rules apply in the page and in the verify response alike.

A check in the send handler

Verify the session with your secret key in the route that calls your SMS provider. A script that skips the page has no verified session, and gets no code.

Setup

Install on your phone form in three steps

For sites where you can edit the HTML or the application bundle. The same snippet works with React, Next.js, Vue, SvelteKit, Astro or plain HTML.

Create a Shield site

Sign in, add the domain you control, and pick a privacy mode. You get a public site key; nothing connects until it is on a page.

Add the snippet

Paste one script tag before the closing body tag, or run npm install @findip/shield and call init() with your site key. Auto page and form tracking is on by default.

Watch the first real event

Submit a test on your own form. The event, its risk score and the reasons behind it appear on the site dashboard moments later.

Prefer another route? Install through Google Tag Manager without touching the bundle.

In your stack

One check in the route that calls Twilio, Vonage or any SMS provider. The phone number never goes to Shield; only the session ID does.

// POST /api/send-code  { phone, findip_session_id }
const verdict = await verifyShieldSession(body.findip_session_id, 'signup_attempt');
if (verdict.decision === 'reject') {
  return Response.json({ error: 'try_again_later' }, { status: 429 });   // no SMS sent
}
await sms.send(body.phone, generateCode());
Server verification reference

A client component starts the SDK once; the signup handler sends the Shield session ID to a route handler, which verifies it with the secret key before creating the account. The same shape works for SvelteKit form actions and Astro server pages.

// app/shield.tsx  ('use client')
useEffect(() => { init({ siteKey: process.env.NEXT_PUBLIC_FINDIP_SHIELD_SITE_KEY! }); }, []);

// app/signup/signup-form.tsx — send the session with the signup
await fetch('/api/signup', { method: 'POST',
  body: JSON.stringify({ email, findip_session_id: getSession().sessionId }) });

// app/api/signup/route.ts — the decision that counts
const verdict = await verifyShieldSession(body.findip_session_id, 'signup_attempt');
if (verdict.decision === 'reject') return Response.json({ error: 'signup_declined' }, { status: 403 });
// 'review' (monitor, unknown, or no key yet): create the account and flag it for a look
Runnable Next.js example, with lib/shield-verify.ts

One POST from your server with the secret key, before the sensitive action. verified tells you the session is real; recommendation is allow, monitor, challenge or block; risk_status unknown means FindIP had no intelligence for that IP, which you treat as monitor, never as safe.

curl -X POST https://shield.findip.net/v1/shield/sessions/verify \
  -H "Authorization: Bearer $FINDIP_SHIELD_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{"session_id": "<from the browser or the findip_session field>", "event": "signup_attempt"}'

# → {"verified": true, "risk_status": "available", "risk_score": 78,
#    "recommendation": "challenge", "challenge_passed": false}
Server verification reference
Boundaries

What a risk score is, and what it is not

An assessment, not proof. A VPN, a hosting network or a changing IP is common for privacy-conscious and corporate users too. Shield explains the reasons so you can decide; it does not pass judgement on a person.
Friction, not a security boundary. Anything decided in the browser can be bypassed by someone who controls it. For decisions that matter, verify the session from your server with your secret key.
Unknown is never safe. Where no intelligence was available the status is unknown. Shield fails open: if a decision does not arrive, the form submits as normal.
Questions

Frequently asked

No. Shield records the shape of the form, for example that it has a phone field, and never its values. Phone numbers, emails, passwords and payment fields are never sent.

No. Shield judges the visitor, not the number. Use your SMS provider's own geo permissions and fraud tools for the destination side; Shield covers who is asking, from which network, and how often.

Yes, which is why the check belongs in the send handler. A script that never ran the page has no verified Shield session, so the verify call does not return verified and you send nothing.

Not unless you configure it. Installing is monitor only. Set the velocity rule above what a real person retrying a code would trigger, and use a Turnstile check rather than a stop for the middle scores. Shield fails open in the page: if a decision does not arrive, the form submits as normal.

10,000 events per site per day, no credit card. Beyond the quota events are refused with a 429 and shown as refused; they are never silently dropped as safe.

Start by watching one real flow.

Create a free Shield site, install it, and look at the first real event before you decide on any response.

Monitor my phone form

Next: create a free Shield site, install it on the phone form, and see the first real event. Add a velocity rule and the check in your send handler when you are ready.

Free Preview · Informational risk signals · You control enforcement