Contact list hygiene for CRM and B2B sales
Phone numbers go stale in ways that email addresses don't. A Gmail address that was valid in 2019 is still valid in 2026 — it'll just bounce or sit unread. A phone number that was valid in 2019 may have been disconnected, reassigned to a different person, ported to a different carrier, or all three. By 2026 it is statistically likely to have moved at least once. Standard email-style validation cannot see any of that.
Sales operations and CRM teams treat this as an annual data-decay cost — somewhere between
5% and 15% of records per year, depending on the segment. The cheap fix is a scheduled rescrub:
run every phone number in the list through POST /v1/lookup on a cadence, and
update the record's status from the fields that actually change.
The fields that flag a stale record
-
deliverable— the topline."false"means the number is unreachable today. Most teams suspend the contact when this flips false. -
reason— whydeliverableis false. Common values include"Number disconnected","Invalid number", and (for litigator / blacklist hits)"Associated with TCPA Litigator DO NOT CONTACT". Useful in your data-quality dashboard so you can break down decay by cause. -
deactivationDate— when the number was last deactivated by a subscriber, formattedYYYY-MM-DD. The interesting case:deliverableis"true"ANDdeactivationDatehas a value. That means the number was deactivated by the previous holder, then reassigned to a new subscriber. Reachable, but not the same person. -
dipCarrier/dipPorted— the current carrier and whether the number has been ported. A port between carriers doesn't make the contact wrong, but it does mean your SMS routing assumptions may need updating.
See the full field reference for every signal returned per lookup.
A complete request
One lookup per number — no add-ons needed for hygiene:
curl --request POST \
--url 'https://api.checkthatphone.com/v1/lookup' \
--header 'Authorization: Bearer YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
"phone": "4155550100",
"ip": "136.38.145.14"
}' A disconnected or unassigned number returns:
{
"success": true,
"credits_used": 1,
"data": {
"subscriber": "4155550100",
"optDate": "2026-05-23T22:01:00.211Z",
"action": "unsubscribe",
"deliverable": "false",
"reason": "Not a valid mobile number",
"nanpType": "not-mobile",
"blackList": "false",
"ipResult": "valid-v4",
"dip": "success",
"dipLrn": "4155550100",
"dipPorted": "false",
"dipOcn": "MULT",
"dipCarrier": "MULTIPLE OCN LISTING",
"dipCarrierSubType": "GENERAL",
"dipCarrierType": "landline",
"dipMessagingLookup": "false",
"dipMessagingEnabled": "false",
"geoCountry": "US",
"geoState": "UT",
"geoCity": "Salt Lake City",
"geoMetro": 770,
"geoSource": "ip",
"timezone": "America/Denver",
"tzOffset": 7,
"error": "false"
}
}
A reassigned number is the trickier case: it answers the call, the SMS goes through, the
contact looks reachable — but the human on the other end is somebody new. There's no static
sample we can show that's permanently in that state (numbers move in and out of reassignment
over time), but the signal is a deactivationDate in the recent past combined
with deliverable: "true".
A scoring rule that treats records matching that pattern as "reachable but unverified —
pause outreach until we confirm" catches the reassignment problem cleanly without
false-positive purging real customers who simply switched carriers. The
deactivationDate is the load-bearing signal here.
Wiring it into the CRM
1. Quarterly rescrub job
A scheduled job (cron, dbt, Airflow — whatever owns your data pipeline) pulls every phone
number in the CRM that hasn't been verified in the last 90 days, calls
/v1/lookup on each, and updates the contact's status fields. Cheap, low-effort,
and catches the long-tail decay.
2. On-event verification
Fire the lookup at lifecycle moments — opportunity created, deal closed-won, list added to a campaign. Avoids the "we found out two months later that the number was disconnected" story when you push a stale record into a campaign tool.
3. CSV one-shot
Sometimes you just want to clean a list once and import it. Upload the file straight from your dashboard's bulk uploader — we deduplicate, look up every unique number, and hand back a results CSV with the same fields as the live API alongside your original columns. No SDK or batching code; useful for one-off scrubs, list-buyer QA, and pre-import audits. Up to 1M rows per file.
For continuous validation on new-lead arrival, the live API in workflow 2 is the better fit — the CSV uploader is for existing lists you've already collected.
Inline FAQ
How is this different from email validation?
Email validation checks SMTP reachability; it does not see when a Gmail address gets re-issued (Gmail does not re-issue). Phone numbers do get re-issued. A number that was your customer last year may be answered by a stranger today — email validation cannot detect that, but the deactivationDate and reason fields can.
What does a deactivationDate of "2024-08-12" mean if deliverable is also "true"?
The number was deactivated by its previous owner on that date, then later reassigned to a new subscriber. It is reachable now, but it is not the same person it was on your list. Treat it as a re-verify candidate, not a confirmed contact.
How often should we rescrub?
Phone-number churn in the US runs around 0.5–1% per month, so a quarterly rescrub catches most decay. Lists that drive SMS or outbound calling are worth a monthly cadence — the marginal cost of a single lookup per number is small compared to the cost of contacting wrong people.
Will this clean my emails too?
No — CheckThatPhone is phone-number only. We deliberately do not bundle email validation; we are not the right product for that. Most teams pair us with a dedicated email-validation vendor.
What this isn't
Phone hygiene is not a substitute for consent management. A number can be perfectly reachable and still wrong to contact under TCPA, an internal opt-out, or a DNC list. If your use case is SMS or outbound calling, pair this with the TCPA litigator scrub add-on rather than relying on hygiene alone.
Pricing for this use case
One credit per number, no add-on cost. The math for a rescrub job is easy to predict:
- 50,000-contact CRM, quarterly rescrub on the 100K plan — 50,000 credits × 4 quarters / 12 months ≈ 17,000/month, all included. $100/month flat.
- 500,000-contact CRM, quarterly rescrub on the 100K plan — ≈167,000/month average. 100,000 included + 67,000 overage at $0.001 = $167/month average.
- One-shot 20K list cleanup on Pay As You Go — 20,000 × $0.010 = $200, one time.
For lists in the millions or per-day rescrub cadence, talk to us about enterprise pricing — we are happy to quote against a real list size and frequency.
Related
- Field reference: all API response fields
- Companion use case: TCPA litigator scrub
- Companion use case: Sales lead validation
- Background reading: How to clean a contact list for an SMS campaign
- Back to all use cases