A direct dial is a phone number that rings one person, not a switchboard or a receptionist. B2B teams find them by filtering for people who hold a number, then looking up each number and reading its type. This guide runs that in three API calls.
The word "direct" is a vendor label, not a promise. In B2B data it usually covers a desk line with its own number (a DID) or the person's mobile. A switchboard is neither. So the job is not "get a number". It is "get a number and check what kind it is".
TL;DR
- Count people with a number for free, search for the ones you want, then look up each number. Total time: about 10 minutes.
- A 5-person test spends 10 records: 5 search rows and 5 phone lookups. Counting spends none.
- Read the
typeon every number (mobile, direct, office) before you build a dial list.
Prerequisites
- A LeadOcean API key in
$LEADOCEAN_API_KEY. The Free plan gives you 1,000 records for life, no card, and 5 requests a second. Get one here. curlandjq.- The company domains or titles you want to call. Placeholders below use
acme.com.
Step 1: Size the pool for free
Send count=true as a query parameter and limit 1 in the body. The call spends no records and returns the total in meta.total.
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"jobLevel": ["VP"],
"jobFunction": ["Sales & Business Development"],
"country": ["US"],
"hasPhone": true,
"limit": 1
}' | jq '.meta | {total, countOnly}'hasPhone: true keeps only people with a number on file. meta.total is capped at 100,000 on this route. The MCP tool leadocean_count_leads takes the same filters, also free.
Filter values are exact strings. jobLevel takes C-Team, VP, Director, Manager, Staff or Other. Check a value with leadocean_list_enum_values before you guess.
Step 2: Search for people who hold a number
Run the same filters without the count flag and narrow to one company. Every row you get has a number behind it, so no lookup in step 3 is wasted.
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
-H "x-api-key: $LEADOCEAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"domain": ["acme.com"],
"jobLevel": ["VP", "Director"],
"hasPhone": true,
"limit": 5
}' > people.json
jq -r '.data[].meta.source_ids.person_id' people.json > ids.txt
wc -l ids.txtThe body is flat JSON. Filters are arrays of strings, and hasPhone is a boolean. limit runs from 1 to 100. Page with meta.nextCursor passed back as cursor.
Search rows carry a has_phone flag and an id, not the number itself. Each row costs 1 record.
Step 3: Look up each person's numbers
The phone endpoint returns every number held for one person, best first. Send one identifier: person_id, contact_id or linkedin_url.
while read -r id; do
curl -s -G "https://api.leadocean.io/v2/people/phone" \
--data-urlencode "person_id=$id" \
-H "x-api-key: $LEADOCEAN_API_KEY"
echo
sleep 0.25
done < ids.txt > phones.jsonlThe sleep keeps you under the Free limit of 5 requests a second. Each call costs 1 record, including a call that finds nothing. The endpoint returns data and no meta, so read GET /v1/usage to see what you spent.
Step 4: Keep the real direct dials
Each number has a type, such as mobile, direct or office. The label is the source's own and not a closed list. Keep what fits the role. Desk roles such as inside sales and finance are reachable on a direct or office line. Executives and field roles are easier to reach on a mobile.
jq -c 'select(.data.found == true)
| .data as $p
| {person_id: $p.person_id,
desk: ([$p.phones[] | select(.type == "direct" or .type == "office")][0].phone),
mobile: ([$p.phones[] | select(.type == "mobile")][0].phone)}' \
phones.jsonl > sorted.jsonlTreat any type you do not recognise as unknown, not as an error. The v2 endpoint does not publish a do-not-call flag, so check your own do-not-call lists before you dial.
What you get
The free count from step 1 ran in leadocean_count_leads on 2026-10-21. Passing hasPhone means the tool reports no totalAll and no email-status note, so read this total as people with a phone on file, not a mailable count.
{
"total": 275259,
"totalIsExact": true,
"distinctCompanies": 135386,
"distinctCompaniesIsExact": false,
"credits": 0
}Filters: jobLevel VP, jobFunction Sales & Business Development, country US, hasPhone true. The lookup response, as the response shape from the API reference, with placeholder values.
{
"success": true,
"data": {
"person_id": "123456789",
"found": true,
"phones": [
{ "phone": "+14155550133", "type": "mobile" },
{ "phone": "+14155550134", "type": "office" }
],
"best": { "phone": "+14155550133", "type": "mobile" }
}
}A person we hold nothing for returns found: false. A count says how many people hold a number. It does not say how many are mobile, direct or still dialable.
Troubleshooting
| Error | Cause | Fix |
|---|---|---|
| 401 | The x-api-key header is missing or wrong. | Check that $LEADOCEAN_API_KEY is set in the shell that runs curl. |
| 429 | You are above your plan's rate limit: 5 requests a second on Free, 200 on Agency. | Wait for Retry-After, add a sleep, or move to a plan with a higher limit. |
| 402 | Your records are spent. On Free the 1,000 are a one-off allowance. | Check GET /v1/account, then see pricing. |
Empty data array | A filter value does not match the enum, or the domain has nobody with a number. | Drop one filter and recount. Check values with GET /v1/enums. |
| 400 on the phone call | You sent two identifiers, or none. | Send one of linkedin_url, contact_id or person_id. |
| Export refused | A bulk export is over 50,000 rows a request or 500,000 a day, or you are on Free. | Split the filters into smaller exports. Bulk CSV export is not on Free. |
FAQ
What is the difference between a direct dial and a mobile?
A direct dial usually rings a desk line or extension that belongs to a seat. A mobile follows the person. Both reach one person, but a vendor may label a mobile as "direct", so read the type on each number. See what is a direct dial for the full definition.
How many records does finding a direct dial cost?
One record per search row and one per phone lookup. A lookup that finds nothing still costs 1 record on the v2 endpoint. Counting and listing filters cost nothing. A 5-person test costs 10 records.
Can I find a direct dial from a name and company?
Not in one call. There is no name plus domain lookup. Search by domain and title, take the person_id, then call the phone endpoint. The work phone guide walks through it.
How do I do this for thousands of people?
Use the Exports page in the app: a filter builder, a column picker with phone columns, the record price before you start and a CSV download. Bulk CSV export is included from Solo at $97 a month. Agency is $497 a month with 50,000 rows a file. To compare tools on hit rate, see we tested 9 phone enrichment tools.
Find your first direct dials free
Free to start. No credit card. 1,000 records to spend whenever you like.
Get your free API key →