Phone validation in a ping-post lead-gen flow: where to call the API on each side

The problem with trusting the seller’s ping

You’re a buyer. A ping comes in carrying a phone number, a claimed line type, and an opt-in timestamp. You post, get charged, dial the number, and hit a Google Voice VoIP that was ported six months ago from a prepaid carrier. Your dialer flags it as uncontactable. Your compliance team flags it as a potential TCPA exposure. You just paid for garbage.

This happens constantly in ping-post lead-gen, and it happens because most networks validate the phone number exactly once, on the seller side, at form submit, and then trust that result forever. The fix is straightforward: validate at form submit on the seller side, attach the raw API response fields to the ping payload, and re-validate on the buyer side at post time before you accept the lead. Two API calls, two chances to catch a bad number.

Here is how to wire that up.

Seller side: validate at form submit, not at ping dispatch

The seller’s job is to call POST https://api.checkthatphone.com/v1/lookup the moment the user submits the form, before any ping goes out. Not in a background job. Not after the lead is stored. Right then, synchronously, while the user is still on the page.

A minimal request body looks like this:

{
  "phone": "6505551234",
  "litigatorFilter": true
}

Add landlineSmsLookup: true if your buyers run SMS campaigns. The response fields you want to capture and forward in the ping are:

  • nanpType: the line type. Values are mobile, landline, or not-mobile. This is the single most important field for most buyers.
  • dipCarrier: the current serving carrier name.
  • dipCarrierType and dipCarrierSubType: carrier category. dipCarrierSubType is where you catch values like VoIP or prepaid, which many buyers reject outright.
  • dipPorted: boolean. A number ported in the last 90 days is a different risk profile than one stable for years.
  • deliverable: whether the number is reachable at all.
  • action: the API’s recommended disposition. Typical values are accept, review, or reject.
  • litigator: boolean, only present when litigatorFilter: true is set. If this comes back true, stop. Do not ping.
  • litigator_type and litigator_name: context on why the number hit the litigator list.
  • ipResult: geo-consistency check between the number’s area code and the submitting IP.
  • geoState: the state associated with the number.

Do not just log these and move on. Store the full response against the lead record and include the key fields in your ping payload as structured metadata, not a freeform notes field. Buyers who have written good acceptance rules need to read these values programmatically.

If litigator is true or action is reject, kill the lead at the source. Do not ping it. The litigator scrub add-on exists precisely for this moment. For more on why that matters from a TCPA liability standpoint, see the TCPA litigator scrub use case.

What the ping payload should carry

Different networks use different ping schemas (LeadConduit, proprietary XML, JSON over HTTP). Whatever your format, the phone validation metadata should ride alongside the lead data as first-class fields, not as a blob or an encoded string.

At minimum, buyers need to see:

phone_nanp_type: mobile
phone_carrier: T-Mobile USA
phone_carrier_subtype: postpaid
phone_ported: false
phone_deliverable: true
phone_action: accept
phone_litigator: false
phone_ip_result: match
phone_geo_state: CA

If you ran landlineSmsLookup, include dipMessagingEnabled and dipMessagingProvider too. A buyer running an SMS sequence on a landline number that happens to have a wireless area code is a real scenario, and dipMessagingEnabled: false is a fast rejection signal.

One thing I have seen sellers get wrong: they validate at ping time using a cached result from earlier in the session, or worse, from a prior lead with the same number. Numbers get ported and deactivated. A validation from 20 minutes ago is fine. One from yesterday is not. Call the API fresh at form submit, every time.

Buyer side: re-validate at post, not just read the ping fields

Here is the part most buyers skip. The ping fields tell you what the seller claims. The post is when you commit money. Before you accept, call POST https://api.checkthatphone.com/v1/lookup yourself.

Your post-time request should include the same add-ons you care about:

{
  "phone": "6505551234",
  "litigatorFilter": true,
  "landlineSmsLookup": true
}

Then compare the response against what came in on the ping. Specifically:

  • Does nanpType match what the seller sent? A seller claiming mobile on a number that returns landline is a red flag.
  • Does dipCarrierSubType show VoIP when the seller claimed postpaid wireless? Reject.
  • Is litigator now true? Could have changed between ping and post, or the seller may not have run the scrub. Either way, reject.
  • Is deliverable false? The number may have been deactivated in the window between ping and post. It happens.
  • Does ipResult show a mismatch? Not always a reject on its own, but a good signal to combine with other flags.

Your acceptance rules should be explicit. Something like: accept only if nanpType is mobile, dipCarrierSubType is not VoIP, litigator is false, and deliverable is true. Everything else goes to review or auto-reject. Write these as code, not as a checklist in a wiki. The sales lead validation use case has more detail on building these filters.

The latency cost of the buyer-side call is real. CheckThatPhone’s lookup is fast, typically under 200ms, but you need to budget for it in your post acceptance SLA. If your network requires a post response in 500ms, you have enough headroom. If it requires 100ms, you need to think about async post acceptance with a callback.

Handling bulk historical lists

The ping-post flow above covers real-time lead traffic. If you’re a buyer auditing a batch of leads you already received, or a seller cleaning a list before activating a campaign, the single-number /v1/lookup endpoint is the wrong tool. Calling it in a loop at volume is slow, fragile, and will hit rate limits.

For that, use the bulk CSV verification in the CheckThatPhone dashboard: Dashboard, then Bulk CSV. Upload your file, select the add-ons you want (litigator scrub, landline SMS lookup), and download the result file. It deduplicates automatically, runs carrier and line-type validation on every row, and the files are deleted within 24 hours of delivery. This is the right path for contact list hygiene work. See contact list hygiene for more on that workflow.

A practical rejection threshold

If you’re a buyer building your acceptance rules from scratch, here is a reasonable starting point based on what I have seen work in practice:

  • Hard reject: litigator: true, action: reject, deliverable: false, dipCarrierSubType: VoIP
  • Soft reject / review: dipPorted: true within the last 30 days, ipResult: mismatch, nanpType: not-mobile
  • Accept: nanpType: mobile, litigator: false, deliverable: true, action: accept

Adjust based on your vertical. A home services buyer might accept landlines. An insurance SMS campaign cannot.

The full field reference is in the API docs, and the pricing page has current costs for the litigator add-on and landline SMS lookup if you’re modeling unit economics on a per-lead basis.

Stop trusting the ping. Validate it yourself.

Start validating phone numbers today

CheckThatPhone provides real-time carrier, line type, portability, and deliverability data for US & Canada numbers in a single API call.