# Bulk phone number validation for outbound sales teams

_2026-07-18 · Boundstone (https://boundstone.io/blog/bulk-phone-validation-sales)_


You have a list. Maybe it came from a web form, a scraped directory, a purchased file, or three years of CRM sediment. Some of those numbers are typos, some are toll-free lines that will never reply to a text, and a few are landlines you are about to feed into an SMS tool. Bulk phone number validation is the step that sorts the file before your dialer — and your SMS bill — sorts it for you. One job, the whole list, one clean file back.

Here is how to run it, what you get back, and the part most vendors gloss over: what the result does not tell you.

## What one job actually does

Send Boundstone a CSV of numbers and each row comes back with three useful things: whether the number is even possible for its region, the same number normalized to **E.164** (`+16504472983`, the format every dialer and SMS API expects), and its **line type** from the numbering plan — mobile, fixed line, toll-free, VoIP, premium rate, and so on.

That lets you do two concrete things before you spend a dial or a message:

- **Drop the impossible.** Wrong digit counts, dead area codes, fat-fingered entries — gone. These cannot connect, so there is no reason to pay to find that out on the phone.
- **Segment by line type.** Route mobiles to your SMS platform, keep landlines on the human dialer, and pull toll-free numbers you should not be cold-texting anyway.

This is metadata-grade validation. It removes what cannot be valid and labels what remains. It does not promise the phone is ringing on someone's nightstand — more on that below.

## Send the whole list: POST /v1/bulk/phone

Post your file as raw CSV. You get an HTTP 202 and a `job_id` immediately; the work runs in the background.

```bash
curl -X POST https://api.boundstone.io/v1/bulk/phone \
  -H "Authorization: Bearer bs_live_YOUR_KEY" \
  -H "Content-Type: text/csv" \
  --data-binary @call-list.csv
```

When the job finishes, pull the results as a CSV:

```bash
curl https://api.boundstone.io/v1/bulk/JOB_ID/results.csv \
  -H "Authorization: Bearer bs_live_YOUR_KEY"
```

Free accounts run up to **250 rows per job**; paid accounts run up to **10,000**. One credit is reserved per row, and any row that errors is **refunded** — you pay for answers, not for failures. The free tier is 250 credits a month, no card, and the credits never expire, which is enough to clean a small list end to end before you commit to anything.

## Read the results

Each row echoes the verify fields: `valid`, `e164`, `country`, `line_type`, and `national_format`.

```csv
phone,valid,e164,country,line_type,national_format
+16504472983,true,+16504472983,US,fixed_line_or_mobile,(650) 447-2983
+447700900123,true,+447700900123,GB,mobile,07700 900123
+18005550199,false,+18005550199,US,unknown,(800) 555-0199
not-a-number,false,,,,
```

Look at row three. `800-555-0199` is perfectly well-formed and sits in a real toll-free area code — a format-only checker waves it straight through. It comes back `false` because it falls in the reserved 555-01XX range, which cannot be assigned to anyone. Those are the rows that quietly survive a cheap validation pass and then get dialled.

Now row one. In North America the numbering plan often cannot separate mobile from landline, so the honest answer is `fixed_line_or_mobile` — not a coin flip dressed up as certainty. Treat those as "could be either," not "mobile." Want to eyeball a single number first? The [phone validator](/tools/phone-validator) runs the same check in your browser, no key.

## Route by line type

Once every row carries a line type, your segmentation writes itself:

- **`mobile`** → SMS platform or power dialer.
- **`fixed_line`** → human dialer; do not text it.
- **`fixed_line_or_mobile`** → your call, but know it is a guess if you SMS it.
- **`toll_free`** / **`premium_rate`** → pull out of a cold outbound list.

That is the whole hygiene loop: normalize, drop the impossible, sort the rest. For the longer playbook on doing this before a campaign, see [how to clean a phone list before calling](/blog/clean-phone-list-before-calling), and for the full outbound wiring, the [outbound sales use case](/use-cases/outbound-sales).

## No code? Upload the CSV in the dashboard

You do not need a script. The dashboard has a CSV upload and a paste box — drop in a file or paste numbers one per line, pick phone, and download the same results CSV. Same 250-row free cap, same per-row refund on errored rows.

## What "valid" does not mean

Here is the boundary, stated plainly, because it changes how you should use the output.

A valid result means the number is **well-formed and possible** for its region and carries a line-type label. It does **not** mean the number is currently in service, reachable, answered, or still on the carrier it was born on. Confirming that needs a live carrier or HLR lookup, and Boundstone does not do it today. Every response says so, in fields you can read:

```json
"checks": {
  "performed": ["format", "region", "line_type_metadata"],
  "not_performed": ["carrier_lookup", "ported_status", "hlr_liveness"]
}
```

Two more honest edges. Line type is the number's **designated** type in its numbering plan, not a live network probe — a VoIP line that was ported into a mobile range still looks like a mobile to metadata. And validation is not compliance: Boundstone is a data-hygiene tool, not a DNC scrubber and not legal advice. For Do-Not-Call and TCPA obligations you need the official registries and your own counsel. Validation drops the junk; it does not clear you to dial.

## The short version

- **Bulk phone number validation** in one call: `POST /v1/bulk/phone` with a CSV, or upload it in the dashboard.
- Get back `valid`, `e164`, `country`, `line_type`, `national_format` per row.
- **Clean before you dial.** Drop impossible numbers, normalize to E.164, route mobiles to SMS, and skip toll-free.
- **Bulk sizing.** 250 rows per job free, 10,000 paid; one credit per row, refunded on error.
- **It confirms the number is possible, not live.** It labels the line too. Liveness is a live carrier lookup (`carrier_lookup`, `hlr_liveness`), available as a paid opt-in with `hlr:true` at 5 credits.
