The Clay Playbook: Enriching Leads With a Custom HTTP Call When There's No Native Integration
Key Takeaways
Why not just use Clay's native HTTP API column? Cost, mostly. Clay's own HTTP API enrichment is metered by credits, and Clay's Explorer plan caps out at a small monthly allowance — running it at real volume (thousands of enrichment calls a month) forces an upgrade to a much more expensive plan well before you'd hit a comparable cost running the same calls through a dedicated actor.
What does "no native integration" actually mean here? Clay ships pre-built enrichment columns for major providers (Clearbit, Apollo, etc.), but the moment you need to call an API Clay hasn't built a column for — an internal tool, a niche data provider, a webhook — you're writing a raw HTTP call yourself either way. The question is just which HTTP engine you route it through.
Does this require engineering resources to set up?
No — the workflow is: map Clay's row data into the request URL/body, set a lookupKey so the response matches back to the right row, and read the response into a new Clay column. No code beyond filling in a few input fields.
Where Clay's Native HTTP Column Runs Into a Wall
Clay is genuinely good at the pre-built integrations — pulling a Clearbit enrichment or an Apollo contact lookup is a drag-and-drop column. The friction shows up the moment you need a custom HTTP call: an internal API, a provider Clay hasn't built a column for, or a one-off endpoint you're testing. Clay's own HTTP API enrichment handles this, but it's metered by credits, and the free/Explorer tier's allowance (25 credits) is exhausted almost immediately at any real enrichment volume — pushing teams toward a plan upgrade costing well over $100/mo just to keep making calls Clay itself doesn't originate or own.
The HTTP Request actor is built specifically to sit in that gap: it sends the same GET/POST/PUT/PATCH/DELETE call to any endpoint, at Apify's own pay-per-use pricing instead of Clay's credit system — the actor's own documentation walks through this exact cost comparison, because it's the single most common reason people reach for it from inside Clay.
What the Request and Response Actually Look Like
// input
{
"method": "POST",
"url": "https://api.yourinternaltool.com/enrich",
"headers": { "Authorization": "Bearer sk_live_..." },
"data": { "domain": "candlebliss.com" },
"lookupKey": "row-42"
}
// output
{
"data": { "id": 12345, "name": "John Doe", "email": "john@example.com" },
"status": 200,
"lookupKey": "row-42"
}
lookupKey is the field that makes this usable inside a spreadsheet-driven tool like Clay: set it to the row's unique ID before the call, and the response carries it back through, so you can match each result to the correct row even when calls complete out of order or run concurrently.
Wiring It Into Clay
- Add a Run Actor enrichment column pointed at
parsebird/http-request-actor. - Map Clay's row fields into the actor's
urlanddatainputs — for example, a company domain column feeding into the request body shown above. - Set
lookupKeyto Clay's row ID column, so results reconcile correctly even under concurrent runs. - Read the response back into a new column from the actor's
dataoutput field.
From Clay's side, this looks identical to any other enrichment column — the only difference is what's actually executing the HTTP call underneath it, and at what cost per call.
Two Companion Actors for the Cleanup Step
Custom HTTP enrichment at volume produces messy output — inconsistent casing, nulls where a field wasn't found, duplicate rows if a source list wasn't deduped first. Two small, purpose-built actors handle exactly this without you writing cleanup scripts by hand:
- Data Cleaner — normalizes casing, formats phone numbers and emails, strips nulls, before the data ever reaches your CRM.
- Data Deduplicator — merges and deduplicates a dataset by any field combination, useful if your enrichment source list itself has overlap (the same pattern shows up when combining multiple job boards or Shopify lead sources).
Run enrichment first, dedupe and clean second — deduping before enrichment risks dropping a row that looked like a duplicate pre-enrichment but would have resolved to a distinct company post-enrichment.
A Concrete Example: Enriching a Lead List From Scratch
async function enrichLeadList(domains) {
const results = [];
for (const [i, domain] of domains.entries()) {
const response = await runActor("http-request-actor", {
method: "GET",
url: `https://api.yourenrichmentprovider.com/v1/company?domain=${domain}`,
headers: { Authorization: `Bearer ${process.env.PROVIDER_KEY}` },
lookupKey: `row-${i}`,
});
results.push(response);
}
return results;
}
This same pattern is what powers the enrichment step in a Shopify store lead pipeline — once you have a list of domains from a scraper, an HTTP request actor is how you call whatever proprietary or niche API you actually need to enrich them further, without waiting on Clay (or anyone else) to build that specific integration first.
FAQ
Do you need an Apify account separate from Clay to use this? Yes — you're running the actor through Apify (Clay calls it as a Run Actor column), so you need an Apify account and the actor's pay-per-use cost, separate from your Clay subscription.
Is this only useful inside Clay, or does it work standalone? Fully standalone — Clay is just the most common no-code context where the credit-cost problem shows up. The same actor works from n8n, Make, or a raw script for any workflow that needs a scriptable HTTP call without writing and hosting your own request-handling code.
Browse ParseBird's developer tools for the rest of the infrastructure-layer actors, including the data-cleaning and deduplication tools mentioned above.