Checks on every contact
These routes run checks over every contact on your account at once: a TCPA scrub and a line-type validation, with their quotes. To check numbers one by one instead, use Scrub numbers and Validate numbers.
These run over the contacts on your account. If your key belongs to a team member who sees only their assigned contacts, they cover that member’s contacts and unassigned ones.
#Scrub your contacts
Checks your contacts against the TCPA litigator and do-not-call lists your account screens, and locks the ones that match, the same way a scrub before a send does. Each call checks up to 5,000 contacts that were never scrubbed, oldest first, for about 20 seconds; call it again with after_id set to next_after_id for the next ones. Needs the edit_contacts permission.
scrub_rate_usd in Get scrub categories), cached answers included. With rescan: true contacts are checked again and, on per-number billing, charged again. Get a scrub quote prices an import’s scrub, not this run.Parameters
Idempotency-Keystring · header- Recommended. A repeat with the same key gets the first answer back and runs nothing (Idempotency).
after_idintegerDefault0- Carry on after this contact: pass
next_after_idfrom the previous answer. Without it a contact that got no answer (seedata.errors) comes back first on the next call. Also needed to walk arescanthrough all your contacts. rescanbooleanDefaultfalsetruechecks up to 5,000 contacts whether they were scrubbed before or not."false"and0count asfalse.
Returns
data.total, data.clean, data.landlines, data.litigatorsinteger- Your contacts after the run: all of them, the ones flagged neither landline nor litigator, and each flag.
data.scrubbed_nowinteger- Contacts checked in this call.
data.new_matchesinteger- Of those, how many matched a list you screen and are now blocked.
data.errorsinteger- Contacts that got no answer. They are left as they were. When TCPA scrubbing is off for your account, every contact lands here and
data.notesays so. data.notestring- When TCPA scrubbing is off for your account (or every scrub list is off): nothing was checked or charged, and this says how to turn it on. Otherwise only when
data.errorsis above 0: how many got no answer, and to run this again later withoutafter_idorrescanto check them. data.morebooleantruewhen the call stopped at its time limit before checking every contact it read. Call again withafter_id.data.next_after_idinteger or null- The last contact this call read; pass it as
after_idto carry on.nullwhen there was nothing to check. data.scrubbed_atstring- When the run finished, ISO 8601.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | rescan isn’t true or false. Nothing was scrubbed or charged. |
| 429 | scrub_already_running | A scrub is already running for this account. Nothing was charged. |
| 429 | rate_limited | Too many requests in the last minute. |
| 503 | tcpa_not_configured | Scrubbing isn’t available right now. Nothing was scrubbed or charged. |
#Get a scrub quote
What scrubbing count contacts on import costs, the same figure the import charges. On per-number billing the price per contact drops with the contacts you’ve already scrubbed this month (UTC). While scrubbing is billed per segment (the default) the import scrub is free and per_segment is true. Free, and charges nothing. Needs the view_contacts permission.
Parameters
countinteger · queryDefault0Max5000000- How many contacts to price.
Returns
The fields are at the top level, not in data.
countinteger- The count priced, after the cap.
freebooleantruewhen your account is never billed for scrubbing.per_segment, per_segment_usdboolean, number | null- Whether scrubbing is billed per segment you send, and your price per segment.
per_segment_usdisnullwhen it couldn’t be read or doesn’t apply. prior_volumeinteger- Contacts already scrubbed this month that set the price.
0when it doesn’t change the price. cost_usdnumber- The total, in dollars.
per_contactnumber- The average price per contact. With
count0, the full per-contact price. bulkbooleantruewhen part of the count gets a lower volume price.
Errors
| HTTP | Code | When |
|---|---|---|
| 503 | account_unavailable | Your account couldn’t be read, so no price. Retry. |
| 503 | scrub_price_unavailable | This month’s scrub volume couldn’t be read. Retry. |
#Validate your contacts
Looks up the line type (mobile or landline) of every contact not checked yet, up to 20,000 per call. Contacts already known to be landlines are skipped. It is quote first: without confirm: true it returns the count and price and charges nothing. With it, the check runs in the background. Needs the edit_contacts permission.
free: true. On per-number billing, each number that gets an answer costs per_number, your price per number, cached answers included. Billing-exempt accounts are never charged, and a run never takes your balance below zero. A charge over $50 needs confirmation_text: "VALIDATE".Parameters
confirmboolean- Send
trueto run. Anything else, including"true", gets a quote. quoted_countinteger- Recommended. The
countfrom your quote. If more contacts need checking now, a paid run is refused withconfirmation_requiredso you can see the new price. confirmation_textstringVALIDATE, when the quote saysrequires_typed_confirm: true.
Returns
The fields are at the top level, not in data.
quotebooleantrueon a quote. A quote also carriesper_number,free,requires_typed_confirmandtyped_confirm_phrase.count, cost_usdinteger, number- Contacts to check (up to 20,000) and the price in dollars.
cappedbooleantruewhen more than 20,000 need checking. Call again after this run.started, job_idboolean, integer | null- On a confirm: whether the check started, and the id of its background job.
started: falsewithcount: 0when there is nothing new to check.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | invalid_field | A field has the wrong type, or quoted_count is negative. |
| 400 | confirmation_required | A paid run over $50 without VALIDATE, or more contacts than quoted_count. The body carries the new count and cost_usd. |
| 429 | validation_already_running | A landline check is already running for this account, and nothing was charged for this request. Wait for it to finish (GET /bulk-jobs/{job_id}), then try again. The response carries a Retry-After header. |
| 500 | internal_error | Couldn’t start. Nothing was charged. |
| 503 | validation_unavailable | Validation isn’t available right now. Nothing was charged. |
| 503 | account_unavailable | Your account couldn’t be read, so no price. Retry. |
#Check number formats
Checks whether numbers are well formed, the check an import runs with validate_phones: true. Without it an import still takes numbers flagged invalid_length; it always skips the other reasons. No carrier lookup, nothing is saved, and it’s free. Only the first 1,000 numbers are checked. Needs the edit_contacts permission.
Parameters
phonesstring[]Required- The numbers to check.
Returns
data.valid, data.invalidinteger- How many passed and failed.
data.received, data.checked, data.truncatedinteger, integer, boolean- Numbers sent, numbers checked, and whether some weren’t checked.
data.details[]object[]- One per number checked:
phone,valid, andnormalized(E.164) when valid, orreasonandmessagewhen not.reasonisempty,invalid_length,invalid_area_code,restricted_numberorfictional_number.
Errors
| HTTP | Code | When |
|---|---|---|
| 400 | phones_required | No phones. |
| 400 | invalid_field | phones isn’t a list. |
#Get a validation quote
What validating count numbers on import costs, priced the way the charge is. free is true when you’d be charged nothing, as while validation is billed per segment (the default). Free, and charges nothing. Needs the view_contacts permission.
Parameters
countinteger · queryDefault0Max5000000- How many numbers to price.
Returns count, free, per_segment (true while validation is billed per segment you send, so the import check itself costs nothing), cost_usd and per_contact (the price per answered number on per-number billing) at the top level.
Errors
| HTTP | Code | When |
|---|---|---|
| 503 | account_unavailable | Your account couldn’t be read, so no price. Retry. |
#Estimate landlines
An estimate of how many of your contacts are landlines, what texting them costs per campaign, and what validating every contact would cost. It assumes a typical 7% unless your validated contacts show a higher share. Updated every few minutes. Free, and charges nothing. Needs the view_contacts permission.
Returns
data.total_contactsinteger- Your contacts.
data.est_landlinesinteger- Estimated landlines, never fewer than the ones already known.
data.landline_pct_sourcestringtypical(the 7% figure) ormeasured(your own share).data.wasted_per_campaign_usdnumber | null- Estimated landlines times your price per segment.
nullwhen your price couldn’t be read. data.validate_all_cost_usdnumber- What validating every contact costs today.
0while validation is billed per segment. data.per_numbernumber- The price per answered number on per-number billing.
data.freebooleantruewhen your account is never billed for validation.
Errors
| HTTP | Code | When |
|---|---|---|
| 500 | internal_error | Your contacts couldn’t be counted. Retry. |
| 503 | account_unavailable | Your account couldn’t be read. Retry. |