How-to guide

How to make LeadOcean the first provider in a Clay waterfall (HTTP API column)

Add one HTTP API column that calls LeadOcean, then gate every paid provider so it runs only on rows LeadOcean left empty.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

You put LeadOcean in the first position so Clay's paid providers only run on the rows it could not fill. The link is one HTTP API column calling POST /v1/people/enrich. The gate is an "Only run if" condition on every paid column after it.

TL;DR

  • Takes about 30 minutes: one column, one header account, one run condition per paid provider, then a 50-row test.
  • Spends at most 50 LeadOcean records on the test. A person LeadOcean holds nothing for is a 404 and costs nothing.
  • Needs Clay Growth or higher for the HTTP API column (Clay pricing, September 2026). The free LeadOcean plan covers the test.

Prerequisites

  • A LeadOcean key from app.leadocean.io/register. Free plan: 1,000 records, one-off, no card. Export it as $LEADOCEAN_API_KEY.
  • A Clay workspace on Growth or Enterprise. The pricing page lists the HTTP API column on those two only (Clay pricing, September 2026).
  • A Clay table with a column of LinkedIn profile URLs, and at least one paid email or phone provider column you want to gate.
  • curl in a terminal for the test call.

Step 1: Size the audience for free

Count first, so you know how many rows you are about to send. A search with count=true in the query and limit: 1 costs no records (LeadOcean API reference, September 2026). The total comes back in meta.total.

bash
curl -X POST "https://api.leadocean.io/v1/people/search?count=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{
    "jobLevel": ["VP"],
    "jobFunction": ["Sales & Business Development"],
    "country": ["CA"],
    "emailStatus": ["verified", "catch_all_valid", "catch_all"],
    "limit": 1
  }'

Filters are arrays. data comes back empty on a count call, so read meta.total. It is capped at 100,000.

Step 2: Test one row from a terminal

Send the exact request Clay will send per row. Use a LinkedIn URL from your own table. One identifier per request: linkedin_url, email, phone or person_id.

bash
curl -X POST "https://api.leadocean.io/v1/people/enrich" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "content-type: application/json" \
  -d '{"linkedin_url":"https://www.linkedin.com/in/your-own-row","reveal_email":true}'

A hit costs one record. reveal_email costs no extra record. A miss returns 404 and costs nothing.

Step 3: Add the HTTP API column in Clay

Save the key once as a header account so it is not visible in the table. Clay shows a manually typed header in plain text to anyone with access to the column (Clay docs, September 2026).

  1. Click Add enrichment, pick HTTP API, open the Configure tab.
  2. In Select header account, click + Add account. Add the key x-api-key with your LeadOcean key as the value. Name it LeadOcean.
  3. Set the method and body as below. /LinkedIn URL is Clay's column reference.
text
Method:   POST
URL:      https://api.leadocean.io/v1/people/enrich
Account:  LeadOcean   (header x-api-key)
Headers:  content-type: application/json
Body:     {
            "linkedin_url": "/LinkedIn URL",
            "reveal_email": true
          }
Field paths to return:
          data.contact_data.email_status
          data.contact_data.has_email
          data.contact_data.has_phone

Clay uses dot notation for field paths. Name the column LeadOcean. Click Test on one row before you run the table.

Step 4: Gate every paid provider behind it

This is what makes LeadOcean the first hop. On each paid email or phone column, open Run settings and add a condition so it runs only when the LeadOcean column came back empty.

text
Condition on each paid provider column:
  {{LeadOcean email status}} is empty

Conditional runs work like an if statement: the enrichment runs when the condition is true and is skipped when it is false (Clay docs, September 2026). The HTTP API column accepts the same setting. Clay's docs do not say how a 404 HTTP API row is shown or whether an is empty condition matches it (Clay docs, October 2026). Test it on five rows that LeadOcean does not hold before you run the full table.

Step 5: Run 50 rows and read the result

Run 50 rows, not the table. Then compare LeadOcean's own usage number with what Clay filled. GET /v1/usage reports records spent (LeadOcean API reference, September 2026).

bash
curl "https://api.leadocean.io/v1/usage" \
  -H "x-api-key: $LEADOCEAN_API_KEY"

In Clay, filter the table on rows where the LeadOcean column is filled. Those rows never triggered a paid provider. The rest did.

Your fill rate is that share of the 50. It depends on your list, so use your own number. Do not size a full run from anyone else's.

What you get

A table where each row carries a LeadOcean email status, and paid provider cells stay empty on rows LeadOcean filled. A free count call gives you the audience size before you start.

Free leadocean_count_leads MCP call, run 2026-09-30 with no filters (default: verified, catch_all_valid and catch_all addresses only):

json
{
  "total": 124786586,
  "totalIsExact": true,
  "credits": 0,
  "defaults": "Counted only addresses worth sending to (emailStatus: verified or catch_all_valid or catch_all)"
}

That is the whole mailable people set, not your audience. Step 1 narrows it with your filters.

Response shape from the API reference for each row Clay receives, placeholder values only:

json
{
  "success": true,
  "data": {
    "contact_data": {
      "email_status": "verified",
      "email_type": "work",
      "has_email": true,
      "has_phone": true,
      "contact_emails": [{ "email": "name@example.com", "type": "work", "status": "verified" }],
      "contact_phones": [{ "phone": "+14155550133", "type": "mobile" }],
      "contact_current_experiences": []
    }
  },
  "meta": { "source": "leadocean", "credits": 1 }
}

Troubleshooting

ErrorCauseFix
401 on every rowMissing or wrong x-api-key headerRe-save the header account. The name is x-api-key, not Authorization.
429 on a big runMore than 100 requests a second on one keyLower the rate limit setting on the Clay column and retry.
402 partway throughThe free plan's 1,000 records are spentStop the run, or move to Pro at $499 a month.
404 on some rowsLeadOcean holds nothing for that personExpected and free. The gated paid providers run on those rows.
Empty results from a searchA filter value is not in the catalogueResolve it with GET /v1/enums/{name}, for example email_status, and use the exact string.
Export refusedThe job is over the ceiling of 50,000 rows a request or 500,000 a daySplit the export into several requests and spread them over days.

FAQ

Why put LeadOcean first and not last?

Because a LeadOcean miss costs nothing, you can try it on every row. Paid providers then run only on the gap. Put the provider with the cheapest miss first.

Do I need the Clay Growth plan?

Yes, for the HTTP API column. Clay lists it on Growth and Enterprise only (Clay pricing, September 2026). On Launch, pull a CSV from LeadOcean Exports and import it instead.

What does a miss cost in Clay?

Clay lists HTTP API calls as using an Action and does not say whether a call that returns 404 is charged (Clay docs, October 2026). LeadOcean charges nothing for a miss.

Can I add phone numbers as a second hop?

Yes. Filter with hasPhone before you spend, then call GET /v2/people/phone. It costs one record per call, including a call that finds nothing. See the phone waterfall skill.

Related: what a data waterfall hop is, LeadOcean, Clay and Salesforce together, pricing. Back to the blog.

Put LeadOcean first in your Clay waterfall. Start free.

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →