A Clay and BlitzAPI Enrichment Waterfall, Step by Step

Three independent agency guides cover Clay waterfalls as drag-and-drop columns. None show the request, the response, or an empty result. Here is the scripted version.

Anshul
Anshul Bhatia
Founder
September 2, 2026 · 10 min read

Finding the person, then the email, then the phone number, all without opening a Clay table. That is a Clay waterfall enrichment workflow run from scripts and HTTP calls instead of drag-and-drop columns, and it is how Lead Line Partners runs it for scored, per-task lookups that do not justify standing up a table. Find the person first, through the Clay CLI or BlitzAPI's employee finder. Enrich the email next, through BlitzAPI's waterfall endpoint keyed on a LinkedIn URL. And enrich the phone number the same way. But log a miss instead of guessing one. Five moves. That's the walkthrough below.

Why script it instead of building a Clay table

A Clay table earns its keep when a waterfall runs the same way every week: same providers, same order, same list shape, and someone who is not an engineer needs to watch it run and glance at rows as they fill in. That is most of Clay's audience, and our own Clay review covers that use case in more depth.

But a one-off lookup for a scored account, mid-research, does not need any of that.

Opening Clay's UI, building a table, and wiring three enrichment steps together takes longer than writing forty lines of Python that call an endpoint directly. So we script it. We run these lookups from Claude Code more than from a spreadsheet now, and this walkthrough is that path: find the person, hit the email endpoint, hit the phone endpoint, and log what came back.

Find the person first

Clay CLI, query mode

Clay's CLI supports Clay CLI people search in query mode: pass filters like title, seniority, and company domain, and it returns matches straight from Clay's own database, the same data a table-based people search would surface. Just callable from a script instead of a UI.

The example below is illustrative pseudo-code, not literal current syntax. Clay's CLI syntax moves, and a copy-pasted snippet here would go stale faster than the mechanism it demonstrates.

# illustrative only, not literal Clay CLI syntax
clay search people \
  --title "VP RevOps" \
  --company-domain "example.com" \
  --mode query \
  --token "$CLAY_API_KEY"

The output is a list of candidate matches, one LinkedIn URL per person. And that URL is the one field the rest of this waterfall keys on.

BlitzAPI employee finder, by company LinkedIn URL

When the starting point is a company rather than a person, an employee finder API takes it from there. POST /v2/search/employee-finder takes a company LinkedIn URL and returns employee LinkedIn profiles at that company, paginated up to 200 pages and 10,000 results, per BlitzAPI's documentation (accessed September 2026).

It returns no email, and no phone number either. Just who works there, and their LinkedIn profile.

curl -X POST https://api.blitz-api.ai/v2/search/employee-finder \
  -H "Authorization: Bearer $BLITZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "company_linkedin_url": "https://www.linkedin.com/company/example-co",
    "page": 1
  }'

So filter the results by title before sending anyone into the email step below. The employee finder does not know your ICP. It just lists names.

Email enrichment through the waterfall endpoint

Once a candidate has a LinkedIn URL, whether it came from Clay's people search or BlitzAPI's employee finder, the email enrichment API step is one call, repeated for each candidate. BlitzAPI's waterfall endpoint for this is POST /v2/enrichment/email, keyed on person_linkedin_url. That single field is the whole waterfall's connective tissue: Clay does not need to know how BlitzAPI found the person, and BlitzAPI does not need to know Clay exists. And the LinkedIn URL is the interface between them.

curl -X POST https://api.blitz-api.ai/v2/enrichment/email \
  -H "Authorization: Bearer $BLITZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "person_linkedin_url": "https://www.linkedin.com/in/example-person"
  }'

A found match comes back shaped like this, per BlitzAPI's own documentation (accessed September 2026):

{
  "found": true,
  "email": "person@example.com",
  "all_emails": [
    {
      "email": "person@example.com",
      "job_order_in_profile": 1,
      "company_linkedin_url": "https://www.linkedin.com/company/example-co",
      "email_domain": "example.com"
    }
  ],
  "fair_usage": {
    "records_used": 179,
    "records_remaining": 4821,
    "next_reset_at": "2026-10-01T00:00:00Z",
    "rate_limit": {
      "requests_per_second": 5,
      "remaining_this_second": 4
    },
    "request_id": "a1b2c3d4-0000-0000-0000-000000000000"
  }
}

found is the field the rest of your script hinges on. email is the single best-match address, null on a miss. all_emails is not a flat list of strings: each candidate comes back as its own object, carrying email, job_order_in_profile, company_linkedin_url, and email_domain, which matters when a company runs more than one address format at once. fair_usage tracks account-level consumption, and it nests the per-second rate limit inside it (rate_limit.requests_per_second) rather than keeping the two separate, alongside a request_id for tracing a specific call.

A script wrapping this is short:

resp = post_json(f"{BASE}/v2/enrichment/email", {"person_linkedin_url": url})
if resp["found"]:
    save_email(person_id, resp["email"])
else:
    log_miss(person_id, "email")

Our own BlitzAPI review covers where this endpoint sits inside the broader enrichment stack, beyond this one call.

Phone enrichment, same shape

The phone endpoint follows the same request and response family: POST /v2/enrichment/phone, keyed on the same person_linkedin_url, returning the same found boolean.

We have not pulled BlitzAPI's phone-enrichment documentation as thoroughly as its email endpoint's. So treat the fields below as structurally consistent rather than as a fully verified schema, and check BlitzAPI's own docs before building against it.

curl -X POST https://api.blitz-api.ai/v2/enrichment/phone \
  -H "Authorization: Bearer $BLITZ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "person_linkedin_url": "https://www.linkedin.com/in/example-person"
  }'

What we can say with confidence: the shape mirrors the email endpoint, a found boolean gates everything downstream, and a miss deserves the same discipline as an empty email lookup. Coverage specifics, which countries, which line types, are not something we have confirmed against BlitzAPI's own phone documentation as of this writing. So none of that is asserted here. Check current docs before planning volume around geographic coverage.

Rate limits: five requests a second

BlitzAPI limits each endpoint to five requests per second, applied independently per endpoint and standard across plans, per its documentation (accessed September 2026). That is a real constraint the moment enrichment leaves a tool's internal queue and becomes a script making raw HTTP calls. Clay throttles its own waterfall internally. But a script calling BlitzAPI directly does not get that for free.

None of the UI-only Clay waterfall guides we have come across mention this, because a Clay table never exposes it. So the limit only shows up once you are the one making the calls.

import time

RATE_LIMIT = 5  # requests per second, per BlitzAPI endpoint
last_call = 0

def call_endpoint(fn, *args):
    global last_call
    elapsed = time.time() - last_call
    if elapsed < 1 / RATE_LIMIT:
        time.sleep(1 / RATE_LIMIT - elapsed)
    last_call = time.time()
    return fn(*args)

Pull the employee finder first. Queue every LinkedIn URL it returns. Then pace the email and phone calls against that same limiter. Skip that step and a burst of calls in the first second gets you a stack of rejected requests, not a stack of enriched rows.

Honest handling of found: false

A found: false response means what it says: BlitzAPI's waterfall checked its sources and came up empty on that person, at that moment. It is not a signal to try harder by guessing.

The temptation is obvious. You know the company's domain. You know most of their colleagues use firstname.lastname@company.com. Pattern-guessing the address and moving on feels like finishing the job.

But it is not.

A guessed email is not enrichment. It is an unverified string that produces a bounce the moment someone sends to it, and a high bounce rate hurts deliverability more than a smaller, honest list ever does.

So log the miss. Move to the next candidate. Or accept that this row stays empty, and mean it. That is the same no-fabricated-claims discipline Lead Line Partners applies to prospect research, aimed here at the data itself instead of at the words in an email. A missing row costs nothing beyond an empty cell. A wrong one costs a bounce, hurts deliverability, and hides that cost behind a field that looks filled in.

Where a Clay table waterfall with FullEnrich still wins

None of this makes the scripted path right in every case. Four situations still point back to a Clay table, FullEnrich included in the waterfall.

Bulk lists are the first case. A one-off script makes sense for a scored subset of accounts. It stops making sense at ten thousand rows a client hands over at once, where Clay's own queueing, retry logic, and credit tracking already do the coordination work you would otherwise build yourself.

Non-engineering operators are the second. Not every operator running enrichment can write a script, or wants to maintain one when BlitzAPI or Clay ships a breaking change. A table with configured steps is the right interface for a team whose job is running campaigns, not maintaining code.

Visual QA per row is the third. Some enrichment tasks need a person glancing at each row as it fills in, catching a mismatched company before it goes further into a sequence. A spreadsheet view does that naturally. A JSON response in a terminal does not.

And there's a fourth case most teams underrate: standing, recurring enrichment that runs the same way against a growing list, week after week. That kind of work sits closer to infrastructure than to a one-off lookup, and infrastructure benefits from a table's built-in monitoring and audit trail.

FullEnrich fits into that kind of waterfall on its own merits, for a given task. It is never the fallback source by default, in Clay or anywhere else. What earns it a spot is the same question that decides every step in a waterfall: does it produce enriched rows the earlier steps missed, at a cost the task can carry. So the choice comes down to the task in front of you, script or table. Not a blanket rule.

Credit cost is a tuning parameter, not a fixed number

Every provider in a waterfall costs something per row, whether it is a BlitzAPI call or a Clay-native step, and the instinct is to ask which one is cheapest. But that is the wrong question on its own. The right one is which order, which providers, and which fallback depth get you the enriched rows you need, at a cost the task can carry. That is a design choice made per waterfall, not a number printed on a pricing page.

BlitzAPI's Unlimited Leads plan runs $399 a month as of September 2026, and it does not include email or phone enrichment at all, per BlitzAPI's own pricing page. But that figure tells you almost nothing about what a given waterfall will cost to run, because the plan it prices and the enrichment steps in this walkthrough are two different things.

Whether the Clay API lowers waterfall cost goes deeper on the cost question specifically. Here, the point is narrower: credit cost is one input you tune per task, alongside provider order and fallback depth. It is not a fixed line item you accept as given.

If the command-line and API side of Clay is new territory before any of this, the API and CLI side of Clay, not just the table is worth reading first.

Frequently asked questions

What is a waterfall enrichment workflow in Clay?

A waterfall enrichment workflow runs a list of prospects through more than one data provider in sequence, using each provider's output only when the one before it came back empty. In Clay's table UI, that means chaining enrichment columns with conditional logic. The mechanism here is the same idea, run through direct API calls instead of table columns.

Can you run a Clay-style enrichment waterfall without Clay's table UI?

Yes. A waterfall is just an ordering of provider calls with fallback logic, and that logic can live in a script as easily as in a table. This walkthrough runs the person-finding, email, and phone steps through the Clay CLI and BlitzAPI's HTTP endpoints directly, applying the same found-or-not-found logic a Clay table waterfall would apply.

What does found: false mean in an enrichment API response?

It means the provider checked its own data sources and did not turn up a verified match for that person. In BlitzAPI's email endpoint, found: false returns a null email rather than a guess. So the honest response is to log the miss, not to pattern-guess an address from a known company domain.

How many requests per second can you send to an enrichment API like BlitzAPI?

BlitzAPI limits each endpoint to five requests per second, applied independently per endpoint, standard across its plans as of September 2026. That number is specific to BlitzAPI. Other enrichment vendors set their own limits, so check current documentation before a script assumes any particular ceiling holds.

When should you use a Clay table with FullEnrich instead of scripting the waterfall yourself?

Reach for the table when the list is large, the operator running it is not writing scripts, the task needs a person glancing at each row, or the enrichment runs on a standing weekly cadence rather than as a one-off lookup. So those four situations favor Clay's queueing and monitoring over a script you would otherwise maintain yourself.

Supporting

  1. BlitzAPI docs: Employee Finder, accessed September 2026
  2. BlitzAPI docs: Find Work Email, accessed September 2026
  3. BlitzAPI docs: pricing and rate limits, accessed September 2026
Written by
Anshul

Anshul Bhatia

Founder
IIT Kharagpur. Builds GTM systems for B2B SaaS.

Anshul builds the outbound systems behind Lead Line Partners. Clay workflows, AI enrichment, and research-first sequencing for teams that want more with less.

More posts
GTM ToolkitComparison · 6 min read

Orum vs Nooks: Which Parallel Dialer Fits a Small Outbound Team

Orum and Nooks both get shortlisted as the AI parallel dialer to buy. Neither publishes a price. Here is what the dialing mode, the AI layer, and the reliability tradeoffs look like instead.

By Anshul Bhatia
GTM ToolkitListicle · 9 min read

Cold Email Deliverability Terms: CASL, BIMI, and Block Lists

Our deliverability FAQ covers SPF, DKIM, and warm-up. This covers what it left out: CASL, BIMI, block lists, seed testing, bounce classes, and complaint rate.

By Anshul Bhatia
GTM ToolkitComparison · 8 min read

Apollo vs Nooks vs Aircall: Which Parallel Dialer Fits Your Team

Apollo, Nooks, and Aircall all get called a parallel dialer. Only one of them runs a power dialer instead, and that decides more about fit than any feature list.

By Anshul Bhatia

Ready to engineer your GTM motion?

Tell us how your motion runs today. We'll show you what we'd engineer.

Contact us