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

# Prospect.find

> List the people in the role you sell to at companies that fit; a search by title is free, and contact details come later.

`POST https://api.sb/{your-domain}/prospects/find`

List the people in the role you sell to at companies that fit; a search by title is free, and contact details come later.

- **Price:** 3.8¢ per 100 rows
- **Success rate:** 99.99% of 636,024 calls
- **Freshness:** Fresh for 30 days
- **Emits:** `Prospect.found`
- **Capability:** [Enrichment](https://startupbuilder.dev/systems/sales/enrichment)

```text title="Run it · Ask"
Who should I write to at harborview-pm.com: the owner, or the director of property management? Spend up to 1¢.
```

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

const result = await $.try.Prospect.find({ organizations: ['harborview-pm.com'], roles: ['owner', 'director of property management'], limit: 1, maxCost: 0.01 })
```

```json title="Run it · Response"
{
  "prospects": [
    {
      "kind": "person",
      "name": "Dana Whitfield",
      "title": "Director of Property Management",
      "seniority": "director",
      "organization": {
        "name": "Harborview Property Management",
        "hostname": "harborview-pm.com"
      },
      "location": "Seattle, WA",
      "profileUrl": "https://www.linkedin.com/in/dana-whitfield-pm",
      "hasEmail": true,
      "reason": "Director at a company that fits; first of 3 by role.",
      "observedAt": "2026-10-27T14:02:11Z",
      "method": "sampled"
    }
  ],
  "after": null,
  "total": 1,
  "ignored": [],
  "cost": {
    "usd": 0,
    "maxUsd": 0.0005,
    "units": 1,
    "misses": 0
  }
}
```

## Options and prices
| You get | Input that selects it | Price | Success rate |
| --- | --- | --- | --- |
| People by title, or at a company domain | `{"where":{"titles":["property manager"]}}` | Free | 99.99% of 636,024 calls |
| People at companies that match an industry, keywords or size | `{"where":{"titles":["property manager"],"organizationWhere":{"keywords":["property management"]}}}` | About 3.8¢ per 100 rows | 100% of 1,384,179 calls |
| The person to write to at each company | `{"organizations":["harborview-pm.com"],"roles":["owner"]}` | About 5¢ per 100 rows | 99.95% of 71,697 calls |
| People who fit a sentence | `{"question":"…"}` | About 7¢ per 100 rows | 99.93% of 200,832 calls |
| Creators by audience | `{"kind":"creator","creator":{}}` | About 60¢ per 100 creators | 99.9% of 12,654 calls |
| People who engaged with a post | `{"kind":"engager","engagedWith":"…"}` | About 0.3¢ a post | 99.95% of 43,072 calls |

## Inputs
| Name | Type | Meaning |
| --- | --- | --- |
| `icp` | `string` | An ICP id; its Persona fills `where` and `roles`. Pass one of `icp`, `where`, `question`, `organizations` or `engagedWith`. |
| `where` | [`ProspectFilter`](/code/data/types#prospectfilter) | Titles, seniority, departments, locations and the company filters. |
| `organizations` | [`Hostname[]`](/code/data/types#hostname) | Up to 100 companies; the rows are the people at each, ranked by `roles`. |
| `roles` | `string[]` | With `organizations`: the roles in priority order. Default `the ICP’s Persona`. |
| `question` | `string` | The people in one sentence; each row gains a `reason`. |
| `kind` | `'person' \| 'creator' \| 'engager'` | Which people, and so which output shape: people in a role, creators by audience on a site, or the people who engaged with `engagedWith`. Default `'person'`. |
| `creator` | `{ venues: VenueId[]; followers?: Range; engagementRate?: Range; niche?: string }` | With `kind: creator`. Sites: Instagram, TikTok, YouTube and X. |
| `engagedWith` | `string` | With `kind: engager`: a post URL on LinkedIn, X or Reddit; the people who reacted to it or commented on it. |
| `limit` | `number` | Rows a page, at most 100. You pay for rows returned, never rows asked for. Default `25`. |
| `after` | [`Cursor`](/code/data/types#cursor) | The cursor from the last page. |
| `save` | `number` | Save the first n rows as Prospects; free, and it follows the call’s Mode. Default `0`. |
| `maxCost` | `number` | The most this call may spend, in USD. Over it, the call is refused before it spends, or stops early with what it has. Default `1.00`. |

## Outputs
| Field | Type | What `null` means |
| --- | --- | --- |
| `prospects` | `Prospect[]` | Never null: empty when no one matched, at $0. |
| `prospects[].kind` | `'person' \| 'creator' \| 'engager'` | Never null: the shape of the row, as `kind` asked. |
| `prospects[].name` | `string` | Never null. |
| `prospects[].title` | `string \| null` | Unknown. |
| `prospects[].seniority` | `'owner' \| 'c-level' \| 'vp' \| 'director' \| 'manager' \| 'individual' \| null` | Unknown. |
| `prospects[].organization` | `{ name: string; hostname: Hostname \| null } \| null` | No current employer on record. |
| `prospects[].location` | `string \| null` | Unknown. |
| `prospects[].profileUrl` | `string \| null` | No public profile was found. |
| `prospects[].hasEmail` | `boolean \| null` | The source does not say. Revealing the address is `ContactPoint.findEmail` or `Prospect.verify`. |
| `prospects[].reason` | `string` | Absent on a plain filter: why this row, from the sentence, the role rank or the comment left. |
| `prospects[].creator` | `{ venue: VenueId; handle: string; followers: number; engagementRate: number \| null }` | Absent unless `kind` is `creator`. |
| `prospects[].engaged` | `{ post: string; as: 'reaction' \| 'comment'; text: string \| null; at: string }` | Absent unless `kind` is `engager`. |
| `prospects[]` | [`Observed`](/code/data/types#observed) | Each row also carries `observedAt` and `method`, and `venue` and `locale` where the reading has them. |
| `after` | [`Cursor`](/code/data/types#cursor) | The last page: there are no more rows. |
| `total` | `number \| null`, in people | The source does not report a total. |
| `ignored` | `string[]` | Never null: empty when every filter you sent was applied. |
| `cost` | [`Cost`](/code/data/types#cost), in USD | Never null. In try mode `usd` is 0 and `maxUsd` is what the call would have cost. |

## Output shapes
`kind` picks the shape, and `prospects[].kind` says which one came back, so your code switches on it.

### People in a role
When `kind` is `person`. The default: people by title, seniority and company, with whether a work email is on file.

| Field | Type | What `null` means |
| --- | --- | --- |
| `prospects[].title` | `string \| null` | Unknown. |
| `prospects[].organization` | `{ name: string; hostname: Hostname \| null } \| null` | No current employer on record. |
| `prospects[].hasEmail` | `boolean \| null` | The source does not say. |

### Creators by audience
When `kind` is `creator`. People found by the audience they hold on one site, for a sponsorship or a guest spot. Titles and employers are often unknown.

| Field | Type | What `null` means |
| --- | --- | --- |
| `prospects[].creator` | `{ venue: VenueId; handle: string; followers: number; engagementRate: number \| null }` | Never null on a creator row. `engagementRate` is null under 10 recent posts. |
| `prospects[].organization` | `null` | A creator row names no employer. |

### People who engaged with a post
When `kind` is `engager`. Everyone who reacted to or commented on the post in `engagedWith`, each with what they did. Priced per post read, not per person.

| Field | Type | What `null` means |
| --- | --- | --- |
| `prospects[].engaged` | `{ post: string; as: 'reaction' \| 'comment'; text: string \| null; at: string }` | Never null on an engager row. `text` is null on a reaction. |
| `prospects[].reason` | `string` | Never null on an engager row: the comment, or the reaction, in a few words. |

```json
{
  "kind": "person",
  "name": "Dana Whitfield",
  "title": "Director of Property Management",
  "organization": {
    "name": "Harborview Property Management",
    "hostname": "harborview-pm.com"
  },
  "hasEmail": true
}
```

```json
{
  "kind": "creator",
  "name": "Marisol Ortega",
  "title": null,
  "organization": null,
  "creator": {
    "venue": "youtube",
    "handle": "@smalllandlordmath",
    "followers": 41200,
    "engagementRate": 0.046
  }
}
```

```json
{
  "kind": "engager",
  "name": "Maya Chen",
  "title": "Owner",
  "organization": {
    "name": "Cedar Hollow Rentals",
    "hostname": "cedar-hollow-rentals.example"
  },
  "engaged": {
    "post": "https://www.linkedin.com/posts/rent-roll-chaos-7134",
    "as": "comment",
    "text": "We still reconcile rent by hand every month.",
    "at": "2026-10-24T13:10:00Z"
  },
  "reason": "Commented that rent is still reconciled by hand."
}
```

## Read the result
| Field | Value | What it means | What to do |
| --- | --- | --- | --- |
| `reason` | Ranked first of 3 by role | The person your `roles` put first at that company. | Write to one person a company first; a second only after 2 weeks without a reply. (one to two people a company, from the list rule) |
| `hasEmail` | `false` | No source holds a work email for this person. | Skip them for email; reach them on LinkedIn or through `ContactPoint.findPhone` later. (the free-miss rule) |
| `title` | Matches none of your titles | Title search widened to similar titles. | Pass `similarTitles: false` in `where` to keep exact titles only. (the ProspectFilter default) |

## 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**

Rows are returned, not saved, unless you pass `save`. Readings about people are never shared across startups.

**Emits**

`Prospect.found` with `{ prospects, where, total }`

**Performed by**

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

**Price**

About 3.8¢ per 100 rows. Free: a search by title or company domain alone, saving rows as Prospects. `maxCost` defaults to $1.00. The person to write to at 100 companies that fit: 100 × 0.05¢ a row is about 5¢.

**Reliability**

The first route succeeds 99.99% of 636,024 calls. If it fails, the call refuses with `sources-busy` and nothing is spent.

**Freshness**

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

**Batch and pages**

Up to 100 rows a page. Pages with an `after` cursor, 25 rows a page by default and 100 at most. 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
Who should I write to at harborview-pm.com: the owner, or the director
of property management? Spend up to 1¢.
```

**API**

```bash
curl -X POST "https://api.sb/leaseline.app/prospects/find?mode=try" \
  -H "Authorization: Bearer $SB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"organizations":["harborview-pm.com"],"roles":["owner","director of property management"],"limit":1,"maxCost":0.01}'
```

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

**CLI**

```bash
npx startup-builder do Prospect.find --try --data '{"organizations":["harborview-pm.com"],"roles":["owner","director of property management"],"limit":1,"maxCost":0.01}'
```

**MCP**

```json
{
  "name": "do",
  "arguments": {
    "mode": "try",
    "code": "return await $.Prospect.find({ organizations: ['harborview-pm.com'], roles: ['owner', 'director of property management'], limit: 1, maxCost: 0.01 })"
  }
}
```

**SDK**

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

const result = await $.try.Prospect.find({
  organizations: ['harborview-pm.com'],
  roles: ['owner', 'director of property management'],
  limit: 1,
  maxCost: 0.01,
})
```

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
- **Workflows:** [Build a verified list](/capabilities/find-customers/build-a-verified-list) · [Pick who to sell to first](/capabilities/find-customers/pick-who-to-sell-to) · [Post-engager harvest](/capabilities/find-customers/post-engagers)
- **Agent tasks:** [Link pitches](/capabilities/earn-first-links/link-pitches)
- **Jobs:** [Find your first customers](/capabilities/find-customers)

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