URL: /code/data/people/find-phone · Id: `page:/code/data/people/find-phone`

# ContactPoint.findPhone

> Find a person’s direct phone and whether it is a mobile, from their profile URL, email address or name and company; a miss is free.

`POST https://api.sb/{your-domain}/contact-points/find-phone`

Find a person’s direct phone and whether it is a mobile, from their profile URL, email address or name and company; a miss is free.

- **Price:** 1.7¢ a number found
- **Success rate:** 92.9% of 1,002,342 calls
- **Freshness:** Fresh for 90 days
- **Emits:** `ContactPoint.found`
- **Capability:** [Enrichment](https://startupbuilder.dev/systems/sales/enrichment)

```text title="Run it · Ask"
What is Dana Whitfield’s direct phone? Her profile is linkedin.com/in/dana-whitfield-pm; spend up to 5¢.
```

```ts title="Run it · SDK"
import { $ } from 'startup-builder'

const result = await $.try.ContactPoint.findPhone({ profileUrl: 'https://www.linkedin.com/in/dana-whitfield-pm', maxCost: 0.05 })
```

```json title="Run it · Response"
{
  "number": "+12065550148",
  "lineType": "mobile",
  "country": "US",
  "observedAt": "2026-10-27T14:02:11Z",
  "method": "sampled",
  "cost": {
    "usd": 0,
    "maxUsd": 0.05,
    "units": 1,
    "misses": 2
  }
}
```

## Inputs
| Name | Type | Meaning |
| --- | --- | --- |
| `profileUrl` | `string` | Their LinkedIn profile URL. Pass this, `email`, or `name` with `hostname`. |
| `email` | `string` | Their work email. |
| `name` | `string` | Their full name, with `hostname`. |
| `hostname` | [`Hostname`](/code/data/types#hostname) | Their company’s domain, with `name`. |
| `checkLine` | `boolean` | Report the line type: mobile, landline or VoIP. Default `true`. |
| `maxCost` | `number` | The most this person may cost, in USD. Routes are walked cheapest first and stop here. Default `0.05`. |

## Outputs
| Field | Type | Unit | What `null` means |
| --- | --- | --- | --- |
| `number` | `string \| null` | E.164 | No route found a number under `maxCost`: a miss, and free. |
| `lineType` | `'mobile' \| 'landline' \| 'voip' \| null` | None. | Unknown, or `checkLine: false`. |
| `country` | `string \| null` | ISO 3166-1 | Unknown. |
| `observedAt` | `string` | ISO 8601 | Never null. |
| `method` | `Observed['method']` | None. | Never null. |
| `cost` | [`Cost`](/code/data/types#cost) | USD | Never null. In try mode `usd` is 0 and `maxUsd` is what the call would have cost. |

## Read the result
| Field | Value | What it means | What to do |
| --- | --- | --- | --- |
| `number` | `null` at the 5¢ cap | About half of people get a number under the default cap; the rest sit on dearer routes. | Raise `maxCost` to 0.30 for the people worth a call: about 9 in 10 get a number, at about 9¢ each. (the route plan, assuming independent misses: our planning number) |
| `lineType` | `landline` or `voip` | Likely a switchboard or a desk line. | Call in business hours, or write instead. (our planning number) |
| `number` | The wrong person answers | Reviews of phone reads report numbers that belong to someone else. | Start with email; a phone is a later step, after a reply. (reviews of phone reads) |

## Effects
**Action class**

None: it reads, so no approval rule names it

**Modes**

- `try`: Runs the read and returns real rows; nothing is saved and nothing is billed.
- `send`: Returns a pending Action at once; its Event carries the rows when the read finishes.
- `do`: Waits for the read and returns the rows, billed per result.

**Idempotency**

The same `Idempotency-Key` with the same input replays the first answer at $0; with other input it returns 409.

**Saved or shared**

Nothing is written to your records. Readings about people are never shared across startups.

**Emits**

`ContactPoint.found` with `{ number, lineType, country }`

**Performed by**

[Prospector](https://startupbuilder.dev/agents/sales/prospector)

**Price**

About 1.7¢ a number found. At most 5¢ a number found. Free: a miss, on every route. `maxCost` defaults to 5¢. 20 people worth a call: 20 × 1.7¢ a number found is about 34¢.

**Reliability**

The first route succeeds 92.9% of 1,002,342 calls. If it fails, the call refuses with `sources-busy` and nothing is spent.

**Freshness**

A reading younger than 90 days returns at $0. Readings about people are never shared across startups.

**Batch and pages**

Up to 1 person a call. It answers in one response.

**Rate**

[The shared rate rules](/code/data#rate): 10 calls a second per key, and an array input counts as one call.

## Refuses when
This call has no refusals of its own. Every data call can also meet [the shared data-call refusals](/code/refusals#data-calls): [`filter-unsupported`](/code/refusals#filter-unsupported), [`input-missing`](/code/refusals#input-missing), [`limit-exceeded`](/code/refusals#limit-exceeded), [`max-cost`](/code/refusals#max-cost), [`rate-limited`](/code/refusals#rate-limited), [`sources-busy`](/code/refusals#sources-busy), [`venue-unsupported`](/code/refusals#venue-unsupported).

## When it waits
Returns in one response; nothing waits.

## Example

**Ask**

```text
What is Dana Whitfield’s direct phone? Her profile is
linkedin.com/in/dana-whitfield-pm; spend up to 5¢.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/contact-points/find-phone?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"profileUrl":"https://www.linkedin.com/in/dana-whitfield-pm","maxCost":0.05}'
```

The response is the one in the Run it window above.

**CLI**

```bash
npx startup-builder do ContactPoint.findPhone --try --data '{"profileUrl":"https://www.linkedin.com/in/dana-whitfield-pm","maxCost":0.05}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.ContactPoint.findPhone({ profileUrl: 'https://www.linkedin.com/in/dana-whitfield-pm', maxCost: 0.05 })"
  }
}
```

**SDK**

```ts
import { $ } from 'startup-builder'

const result = await $.try.ContactPoint.findPhone({
  profileUrl: 'https://www.linkedin.com/in/dana-whitfield-pm',
  maxCost: 0.05,
})
```

To retry, send the same `Idempotency-Key`: the retry returns the first answer for $0. A new key reads again.

A `sk_test_` key answers these the same way every time, at $0 ([every test value](/code/data#test-values)).

| Test value | Always returns | What it is for |
| --- | --- | --- |
| `maxCost: 0.0001` | Refuses with [`max-cost`](/code/refusals#max-cost) | Any priced call with a maxCost of 0.0001 is refused before it spends: the price is above the cap. |
| `rate-limited.test` | Refuses with [`rate-limited`](/code/refusals#rate-limited) | Any call naming this hostname answers 429 with Retry-After, so your retry logic runs. |
| `sources-busy.test` | Refuses with [`sources-busy`](/code/refusals#sources-busy) | Any call naming this hostname answers 503 as if every route were busy, and nothing is spent. |

## Used by
None.

## Related
[`Prospect.count`](/code/data/people/count) · [`Prospect.find`](/code/data/people/find) · [`Prospect.verify`](/code/data/people/verify) · [`ContactPoint.findEmail`](/code/data/people/find-email) · [`ContactPoint.verifyEmail`](/code/data/people/verify-email) · [`Contact.enrich`](/code/data/people/enrich)