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

> A practical guide for affiliate networks: where sellers and buyers should call the CheckThatPhone API in a ping-post flow, and how to map validation fields to acceptance rules.

Published 2026-08-01, updated 2026-10-03 by Marcus Whelan. Canonical: https://checkthatphone.com/blog/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 number. Your dialer flags it as uncontactable. Your compliance team flags it as a potential TCPA exposure. You just paid for garbage.

This happens in ping-post lead-gen because many 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:

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

Add `landlineSmsLookup: true` if your buyers run SMS campaigns, and pass the submitter's `ip` if you have it. Every flag in the response is a string (`"true"` or `"false"`), not a JSON boolean. The response fields you want to capture and forward in the ping are:

- `dipCarrierType`: the line type from a live carrier dip at lookup time. Values are `mobile`, `landline`, `invalid`, or `undefined`. This is the single most important field for most buyers.
- `nanpType`: the line type from numbering-plan data (`mobile`, `landline`, or `not-mobile`). It doesn't follow a number when it ports, so treat `dipCarrierType` as the authority when they disagree.
- `dipCarrier`: the current serving carrier name.
- `dipCarrierSubType`: the carrier classification. This is where you catch VoIP (`IPES`) and wireless resellers (`WRSL`), which some buyers reject outright.
- `dipPorted`: `"true"` when the number now sits with a different carrier than the one it was originally assigned to. It tells you that a port happened, not when.
- `deliverable`: `"true"` when the number can take an SMS and nothing else blocks it. It always agrees with `action`.
- `action`: the API's recommended disposition: `"send"`, `"unsubscribe"`, or `"error"` (a malformed request). Check `reason` for why a number got `"unsubscribe"`.
- `litigator`: only present when `litigatorFilter: true` is set. If this comes back `"true"`, stop. Do not ping.
- `litigator_type` and `litigator_name`: how the match was classified (`"litigator"`, `"plaintiff"`, or `"agitator"`) and the name on record, if any.
- `ipResult`: whether the IP you passed is valid (`"valid-v4"` or `"valid-v6"`). Only present when you send an `ip`.
- `geoState` and `geoSource`: the state, and whether it came from the IP (`"ip"`) or the area code (`"area-code"`). Comparing an IP-derived state with the state on the form is a cheap consistency check.

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"`, kill the lead at the source. Do not ping it. The litigator scrub add-on exists precisely for this moment. If `action` is `"unsubscribe"` for another reason (a landline, a blacklist hit, a recent deactivation), decide based on what your buyers accept: a landline is useless to an SMS buyer but may be fine for a voice buyer. For more on why the litigator check matters from a TCPA liability standpoint, see the [TCPA litigator scrub use case](/use-cases/tcpa-litigator-scrub) and [the affiliate marketer's TCPA exposure](/blog/the-affiliate-marketer-s-tcpa-exposure-and-the-five-validation-fields-that-reduce-it).

## 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_carrier_type: mobile
phone_nanp_type: mobile
phone_carrier: T-Mobile USA
phone_carrier_subtype: WIRELESS
phone_ported: false
phone_deliverable: true
phone_action: send
phone_litigator: false
phone_geo_state: CA
phone_geo_source: ip
phone_validated_at: 2026-10-03T14:02:11Z
```

If you ran `landlineSmsLookup`, include `dipMessagingEnabled` and `dipMessagingProvider` too. A buyer planning an SMS sequence on a number that turns out to be a landline needs to know whether it can take a text, and `dipMessagingEnabled: "false"` is a fast rejection signal.

A common seller mistake: 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:

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

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

- Does `dipCarrierType` match what the seller sent? A seller claiming `mobile` on a number that returns `landline` is a red flag.
- Does `dipCarrierSubType` show `IPES` (VoIP) when the seller claimed a wireless line? Reject.
- Is `litigator` now `"true"`? Our litigator data is refreshed daily, so it could have changed between ping and post, or the seller may not have run the scrub. Either way, reject.
- Is `deliverable` `"false"`? Check `reason`: the number may have been deactivated in the window between ping and post.
- Does the IP-derived `geoState` disagree with the state on the form? 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 `dipCarrierType` is `"mobile"`, `dipCarrierSubType` is not `"IPES"`, `litigator` is `"false"`, and `action` is `"send"`. 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](/use-cases/sales-lead-validation) has more detail on building these filters, and [how lead buyers score affiliate phone leads](/blog/how-lead-buyers-score-affiliate-phone-leads-and-how-sellers-can-beat-the-cut) covers the scoring side.

The buyer-side call adds a synchronous round-trip to your post handling. Measure it from your own servers and budget for it within your network's post response deadline. If the deadline is too tight, accept the post provisionally and run the check asynchronously before the number reaches your dialer.

## 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](/bulk-phone-validation) in the CheckThatPhone dashboard (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 unique number, and the upload and result file are hard-deleted within 24 hours. This is the right path for contact list hygiene work. See [contact list hygiene](/use-cases/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:

- Hard reject: `litigator: "true"`, `action: "error"`, `dipCarrierType: "invalid"`, `dipCarrierSubType: "IPES"`
- Soft reject / review: `action: "unsubscribe"` for a reason other than a litigator match, `dipPorted: "true"` combined with an IP-derived `geoState` that doesn't match the form, `dipCarrierType: "undefined"`
- Accept: `dipCarrierType: "mobile"`, `litigator: "false"`, `action: "send"`

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

The [affiliate and lead-gen use case](/use-cases/affiliate-leadgen-validation) shows the same flow from the network's side. The full field reference is in the [API docs](/docs), and the [pricing page](/pricing) 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.

---

CheckThatPhone is a phone validation API for US and Canada numbers: https://checkthatphone.com (docs: https://checkthatphone.com/docs.md, pricing: https://checkthatphone.com/pricing.md).
