Playbooks · Recipe

People Search to Email Waterfall: One Billing Model Per Leg

Search builds the list; the finder and verifier resolve it. The search leg bills per result whether or not you end up using the row, while the other two are waterfall endpoints and cost zero on a miss. Three calls, two billing models, and the search leg is the one that charges you for rows you throw away.

Last updated September 22, 2026

Pattern: people_search with your ICP filters, paged → filter the results down to the rows you actually want → email_finder on the survivors → email_verifier on the hits. The filtering step sits between legs one and two on purpose, because everything after it is priced per row.

Credit math: Say you pull 2,000 profiles. people_search at 0.1 credits per result bills 2,000 × 0.1 = 200 credits, and it bills on every returned row including the ones you discard. Cut that to 1,200 that match your ICP properly. email_finder at 5 credits on 1,200 at a 60% hit rate bills 720 × 5 = 3,600, with 480 misses free. email_verifier at 2 credits on those 720 is 1,440. Total 5,240 credits for 720 verified addresses off a 2,000-profile pull, of which only 200 was the search.

ROI math: The search leg is cheap enough to be invisible and the resolve leg is not, so what the run costs is decided entirely by how hard you filter between them. Tightening the filter from 2,000 rows to 1,200 saves 800 × 5 × your hit rate in finder credits, which dwarfs the 200 you spent searching. Credit tiers on /pricing.

Owned internally by: Whoever defines the ICP writes the filters; whoever writes the loop handles the paging.

Best for: Building a list from scratch against a title-and-headcount ICP, and anyone replacing a prospecting seat with a script.

example
# Leg 1 — search. Per-result pricing: every row returned bills,
# whether or not you keep it. limit and page are both required.
curl -s https://api.richapi.ai/api/v1/people_search \
  -H "x-api-key: $RICHAPI_KEY" \
  -H "content-type: application/json" \
  -d '{
    "job_title": ["VP Engineering", "Head of Platform"],
    "industry": ["Software Development"],
    "employee_size_start": 200,
    "employee_size_end": 2000,
    "location": ["United States"],
    "limit": 100,
    "page": 0
  }'

# Filter here, in your own code, before spending 5 credits a row.

# Leg 2 — find. Waterfall: a miss is 200, success:false, billed:false, 0 credits.
curl -s https://api.richapi.ai/api/v1/email_finder \
  -H "x-api-key: $RICHAPI_KEY" \
  -H "content-type: application/json" \
  -d '{"linkedin_url": "https://www.linkedin.com/in/dana-weiss"}'

# Leg 3 — verify, only on rows leg 2 resolved.
curl -s https://api.richapi.ai/api/v1/email_verifier \
  -H "x-api-key: $RICHAPI_KEY" \
  -H "content-type: application/json" \
  -d '{"email": "dana.weiss@acme.com"}'

People search to email waterfall: one billing model per leg

Three calls: `/people_search`, `/email_finder`, `/email_verifier`. Two billing models between them, and if you only internalise one thing from this page, make it which leg charges you for rows you end up deleting. **`/people_search` bills per result, on every row it returns, whether you use it or not.** 0.1 credits per result. It is not a waterfall endpoint and there is no free-miss case. Pull 2,000 profiles and discard 800, you paid for 2,000. **`/email_finder` and `/email_verifier` are waterfall endpoints.** Providers run cheapest-first; if none return data you get a 2xx with `success: false`, `billed: false`, and zero credits. Misses on those two legs are genuinely free. [waterfall email then verify](/use-cases/waterfall-email-then-verify) has that pair in detail.

Model it on a real list size

Numbers are meaningless on a per-result endpoint without a stated count, so here is one. 2,000 profiles pulled, 1,200 kept after filtering, 60% finder hit rate. | Leg | Priced on | Volume | Credits | |---|---|---|---| | `people_search` @ 0.1/result | every row returned | 2,000 | 200 | | `email_finder` @ 5/call | rows that resolved | 720 of 1,200 | 3,600 | | `email_verifier` @ 2/call | addresses found | 720 | 1,440 | | **Total** | | | **5,240** | 720 verified addresses. The search leg is 200 credits of 5,240, under 4%. Which is the interesting part. The search is nearly free and the *filtering* is where the money is. Every row you let through to leg two is a 5-credit bet at your hit rate. Tighten the filter from 2,000 to 1,200 and you save 800 × 5 × 0.6 = 2,400 credits, twelve times what the whole search cost. Loosen your ICP to "see what comes back" and you pay for it two legs later. So: search broadly if you like, it's cheap. Then be ruthless before leg two.

Getting the filters right before you spend anything

`/search_reference_data` costs 0 credits and returns the valid filter labels: seniority levels, industries, functions. Call it first. An industry string you invented returns an empty page, and while an empty page bills nothing on a per-result endpoint, the hour you spend wondering why isn't free. `/geo_id_search` at 0.01 credits turns "San Francisco" into a LinkedIn Geo ID if you want precision beyond free-text location matching. Both are covered in [gtm data utilities](/use-cases/gtm-data-utilities) along with the rest of the plumbing endpoints.

Paging, which is where the surprises live

`limit` and `page` are both required, and `page` is **zero-based**. Set `limit` small on the first pull. Look at twenty rows, confirm your filters mean what you think they mean, then widen. The mistake is treating the first query as a rehearsal. On a per-result endpoint it isn't; it's a charge. It's a small charge, which is exactly why people run six exploratory queries at `limit: 500` before noticing.

Feed the LinkedIn URL forward

Search results carry a LinkedIn profile URL. Pass that to `/email_finder` as `linkedin_url` rather than reassembling `first_name` + `last_name` + `company_domain` from the row. Same price, better resolution, and it sidesteps the domain problem entirely, which matters because turning a company name into a domain is its own unsolved step and [domain clean then company enrich](/use-cases/domain-clean-then-company-enrich) explains why.

The failure mode: re-searching instead of resuming

The expensive bug here is in the loop, not the finder leg. A script pages through `people_search`, dies at page 7 of 20, and gets restarted from page 0. Pages 0–6 are fetched again and billed again. Per-result pricing means the retry cost is proportional to how far you got, so the longer the run, the worse the restart. Write each page to disk as you fetch it and resume from the highest page you have. It's the same discipline as any paginated ingest and people skip it because 0.1 credits sounds like nothing until it's 0.1 × 3,500 twice. The quieter bug is skipping the dedupe against your existing contacts. The search leg has no idea what's in your CRM. It will return the person you emailed in March, you'll spend 5 credits confirming an address you already have, and the only place that shows up is the invoice.

The receipt on search to email

The two waterfall legs return an `execution_log`: the provider attempts on that request, each with a status. `people_search` does not; it isn't a waterfall and there's no provider chain to log.

Where search to email goes wrong

No prospecting UI. This is an API and a list you keep yourself; if you want a grid to click around in, we don't have one. No intent data, no scoring, no sending. The chain ends with verified addresses in your store. And no de-dupe against your CRM, because we don't connect to your CRM. That's your join.

Search to email FAQ

**Does a search that returns nothing cost anything?** Per-result pricing with zero results means zero rows billed. But it's not a waterfall endpoint, so don't generalise that to "misses are free" the way it's true for the finder. **Can I filter by domain instead of industry?** Yes, `people_search` takes a `domain` array, which is the move when you already have target accounts and need people at them. **Why is page zero-based?** It is. Check your first pull against your second and make sure you didn't skip or duplicate a hundred rows. **Is 0.1 credits per result the same as 0.1 credits per useful result?** No, and that's the whole point of the filtering section. You pay for what's returned. **How do I test the chain cheaply?** 25 free credits covers a small search plus a couple of finder calls. Use `limit: 10`.

Where to go after search to email

[people search](/api/people-search) for the filter reference, [list build and verify](/use-cases/list-build-and-verify) for the shorter version of this play, [phone append after email hit](/use-cases/phone-append-after-email-hit) to add numbers on top, [gtm data utilities](/use-cases/gtm-data-utilities) for the reference-data and geo lookups, and [use cases](/use-cases) for the rest. Tiers on [pricing](/pricing). **25 free credits, no card.** Run one `limit: 10` search and see the per-result billing on your own account before you write the paging loop.

Frequently asked.

Does a search that returns nothing cost anything?
Per-result pricing with zero results means zero rows billed. But it's not a waterfall endpoint, so don't generalise that to "misses are free" the way it's true for the finder.
Can I filter by domain instead of industry?
Yes, `people_search` takes a `domain` array, which is the move when you already have target accounts and need people at them.
Why is page zero-based?
It is. Check your first pull against your second and make sure you didn't skip or duplicate a hundred rows.
Is 0.1 credits per result the same as 0.1 credits per useful result?
No, and that's the whole point of the filtering section. You pay for what's returned.
How do I test the chain cheaply?
25 free credits covers a small search plus a couple of finder calls. Use `limit: 10`.

More playbooks

Try it with 25 free credits.