Use cases / Outbound

6 min read

Waterfall email enrichment without the waterfall bill

The thing enrichment platforms charge a platform fee to orchestrate, as one call with one price and no credits to pre-buy.

Give this to your agent
$ Set up https://www.routergrowth.com/SKILL.md, then enrich this CSV of names and company domains into verified work emails and tell me the cost per hit.
On this page

A waterfall is a simple idea: ask provider A for an email, and if A has nothing, ask B, then C, until someone answers. Coverage on any single email provider sits somewhere in the 40 to 70% range depending on the segment, so chaining three or four of them is the difference between half a list and most of a list.

The idea is simple. Buying it is not. The usual shape is a platform subscription plus pre-purchased credits, and you carry the provider relationships, the credit balances that expire and the reconciliation.

contact.find is a waterfall already. Six providers sit behind one capability, auto routing tries them in order, and only the one that delivers is charged.

What routing actually does

curl -X POST https://api.routergrowth.com/v1/run \
  -H "Authorization: Bearer $ROUTERGROWTH_API_KEY" \
  -H "Idempotency-Key: row-4821" \
  -d '{
    "capability": "contact.find",
    "input": {"first_name": "Alex", "last_name": "Rivera", "company_domain": "acme.com"},
    "routing": {"provider": "auto", "strategy": "best_value", "max_cost": "0.05"}
  }'

Four things happen that are worth being precise about:

  1. The maximum billable amount is reserved before any provider is touched. Not enough balance, and the call fails with insufficient_balance before it costs anything.
  2. Providers are tried in the order the strategy picks. best_value weighs price against measured success rate. cheapest and highest_success_rate are the two ends of that, and fastest exists for interactive paths where a user is waiting.
  3. A miss falls through to the next provider, up to three attempts. Every attempt appears in the attempts array of the response with its provider, outcome and duration.
  4. Only the provider that delivered is charged, and the reservation settles down to that amount. A run that ends with no email at all releases the hold in full.

That last point is the one that changes the arithmetic. In a credit model, a miss consumes credits. Here it does not.

What "best_value" costs against "cheapest"

Six providers back this capability at different prices and different coverage. Pinning the cheapest one maximizes savings per successful call and minimizes hit rate; letting the router weigh both usually finishes the list for less total spend, because a miss that falls through costs you two attempts on one row.

Read the routing_reason field on any run to see which trade it made, and attempts to see what it tried. If you want the decision to be yours instead, pin a provider by slug and allow_fallback: false turns the waterfall off entirely.

Cap the spend per row

routing.max_cost excludes any offer above your cap before the call, so an expensive provider never gets tried on a row you decided was not worth it:

{"routing": {"strategy": "best_value", "max_cost": "0.03"}}

This is the knob for tiered enrichment. Cheap cap on the long tail of a list, higher cap on the accounts that matter, and the same code path for both.

Verify in the same pass

An email that is found but not deliverable is a liability, not an asset. contact.verify has six providers behind it as well, with the same routing:

curl -X POST https://api.routergrowth.com/v1/run \
  -H "Authorization: Bearer $ROUTERGROWTH_API_KEY" \
  -d '{"capability": "contact.verify", "input": {"email": "alex.rivera@acme.com"}}'

Run it on every found address before the row is written. The combined cost of find plus verify is still cents per finished contact, and the alternative is discovering your bounce rate after the campaign.

Phones are a separate decision

contact.phone returns a mobile from a work email or a profile URL. It costs roughly an order of magnitude more than an email lookup, which is the correct signal: gate it behind qualification rather than running it across the list. Enrich the whole list with email, score it, then pull phones for the top tier only.

Where the money actually goes

Per 1,000 rows, at the starting prices and a realistic hit rate, the spend is dominated by the successful finds. The two things that inflate a bill are both avoidable:

  • Re-enriching rows you already have. Set Idempotency-Key to your own row id and a retry costs nothing. Cache company-level data by domain rather than per contact.
  • Paying for misses. You do not, here. But you will in a credit model, so check what your current tool does on a no-match before you compare prices.

Every run is on the ledger. /v1/wallet/transactions returns every reserve, settle and release as an entry you can read back, and /v1/runs lists the provider and charge per call. Cost per enriched row is a query, not an estimate.

Where it breaks

  • Coverage is not uniform. Hit rates are highest for mid-market and enterprise in North America and Western Europe. Small companies and less public regions are thinner everywhere, on every provider.
  • Three attempts is the cap. A row that misses on three providers returns no match. That is a real limit, not a bug: the fourth provider rarely has what the first three did not.
  • best_value uses measured success rates, which need volume to be meaningful. Early on it behaves closer to cheapest.
  • A verified email is not consent. Deliverability is a technical property. Whether you may send is a legal question with a different answer per jurisdiction.

Run it as an agent

routergrowth inspect -c contact.find
routergrowth run -c contact.find -i '{"first_name":"Alex","last_name":"Rivera","company_domain":"acme.com"}'
routergrowth run -c contact.verify -i '{"email":"alex.rivera@acme.com"}'

inspect returns the providers, the exact prices and the billing conditions, so an agent can quote a list before it enriches it.

What does each call cost?#

Prices below are the starting price per successful call. /v1/inspect returns the exact figure before the run and reserves it against your balance; failures and unbilled no-matches release the hold in full.

CapabilityWhat it returnsFromProviders
contact.find Find a professional email from a person and company. $0
contact.verify Verify deliverability of a professional email: valid, invalid, or unknown, with catch-all and mail-server details. $0.002
person.enrich Full professional profile from a public LinkedIn profile URL: name, headline, about, location and the complete role history. Email-based lookup follows with the enrichment providers. $0.004
contact.phone Mobile number for a professional contact from a work email or a LinkedIn profile URL; charged only when found. $0.11

One prepaid balance covers every row. Capabilities marked coming soon are listed but not yet executable.

FAQ#

Is this the same thing as a waterfall in an enrichment platform?

Functionally yes, structurally no. You get multi-provider fallback with a single request and a single price. What you do not get is a platform layer between you and the providers: no seat licence, no credit packs to pre-buy, no separate contract per source. The trade is that the provider order is a routing strategy rather than a drag-and-drop sequence you configure by hand.

What happens if no provider finds the email?

The run returns no match and the reservation is released in full. You are not charged. This is the unbilled_nomatch outcome, and it appears in the attempts array for every provider that was tried, so you can see the coverage failure rather than just the empty result.

Can I pin a specific provider instead of routing?

Yes. Set routing.provider to the provider slug and the router uses only that one. A pinned provider never falls back, so a miss is a miss. Use it when you have a compliance reason to prefer a source, or when you are benchmarking one provider's coverage on your own segment.

How do I stop paying twice for the same contact?

Set the Idempotency-Key header to your own stable row identifier. A repeated request with the same key returns the original run instead of executing a new one, which makes retries after a timeout or a crash free. Beyond that, cache: company firmographics change slowly and should be fetched per domain, not per person.

Does a higher max_cost get better coverage?

Sometimes. A low cap excludes the more expensive providers before the call, and on hard segments those are often the ones with the data. Run a sample of a few hundred rows at two different caps and compare hit rate against total spend. The answer varies by segment more than most people expect.

Run this today

Sign up, get $5 of credit, and the first call in this guide costs a fraction of a cent. One key, one balance, price shown before every call.

Get started →
waterfall enrichmentclay alternativeemail finder apicontact enrichmentenrichment credits
Give this to your agent
Claude CodeCodexHermesOpenClawCursor

Install the skill once and Claude Code, Codex, Cursor and the rest discover these capabilities, check the price, then run them.

Install the agent skill →

Reading this as an agent? This guide as markdown: /use-cases/waterfall-email-enrichment.md · every guide: /use-cases/llms.txt · RSS