GTM · Data
Clay API
Clay's whole programmatic surface in one reference: which of the three surfaces (REST, MCP, CLI) can actually do the thing, the non-standard auth header, what is UI-only — and the two rules that stop you spending someone's credits by accident.
This skill ships 5 files. The references are where the method lives — SKILL.md on its own will point at files you do not have, so take the archive rather than the markdown.
SKILL.mdreferences/mcp-tools.mdreferences/platform.mdreferences/rest-api.mdscripts/clay_api.sh
Prefer just the instructions? Download SKILL.md alone.
Use it in your assistant
Claude Code — drop the file in your skills folder and it loads on the next session. Use ~/.claude/skills for every project, or .claude/skills inside a repo to keep it to that project.
mkdir -p ~/.claude/skills
curl -L https://growsteady.io/skills/clay-api/archive | tar xz -C ~/.claude/skillsClaude apps (web and desktop) — Settings → Capabilities → Skills → add a skill. Extract the archive and upload the whole clay-api folder, references included (zip it if an archive is asked for).
No install— paste the file into a Claude Project's custom instructions with “Copy as prompt”. Same behaviour, scoped to that project. Note that a paste carries the instructions only: this skill's references do not come with it, so use a real install if you want the full method.
Clay is a GTM data platform. Most of what people know Clay for — building tables, composing waterfalls, writing Claygent prompts — happens in a UI that has no API. What is programmable is narrower than the marketing suggests, and it's split across three surfaces that share a name and almost nothing else.
Getting the surface wrong is the most common way to waste a session here. Start by picking one.
Onboarding — start here
1. What this skill does
It tells you exactly which of Clay's three programmatic surfaces can do the thing you want (REST API, in-app MCP tools, or the clay CLI), gives you the endpoint-level detail for each, and stops you from spending the user's credits by accident. It exists because Clay's auth header is non-standard, most of the product has no API at all, and several operations bill silently with no undo.
2. What it can't do — read this before promising anything
The single most common failure here is not a wrong API call, it's confidently hunting for an endpoint that has never existed. These are UI-only. There is no programmatic path, and table building is explicitly not on Clay's roadmap:
| The user asks for | Reality |
|---|---|
| "Create a Clay table" / "add an enrichment column" | UI only. You can invoke a table someone built; you cannot build one. |
| "Build me a waterfall" | UI only. |
| "Write rows into this table" | No REST write path. Only an inbound Monitor webhook URL a human creates in the UI. |
| "List my tables / routines / functions" | No discovery endpoints anywhere. Table ids come from the app URL. |
| "Register a webhook" | CLI only (clay webhooks create). Signing secret is shown once, unrecoverably. |
| "How many credits have I used" | No billing endpoint. |
| "Search jobs" / "just give me a count" | Exists in the UI Search product, explicitly not via API. |
| Add a user, change a seat, workspace admin | No admin API at all. |
| Read a Clay table over REST | Enterprise only, and each table must be individually toggled on. |
Say this early. A user forgives "that's UI-only, here's what I can do instead"; they don't forgive twenty minutes of you searching for a phantom endpoint.
This skill is also not a general GTM-strategy skill — for defining an ICP or choosing a motion, that's blitz-gtm-brainstorm. It is a reference for calling Clay correctly, not for deciding what to call it about.
3. Setup
Get a key from Settings → Account → API keys (the tab is literally named api-keys-beta). Store it in local.env — never in a script:
echo 'CLAY_API_KEY=your_key_here' >> local.envset -a && . ./local.env && set +aThe auth header is `clay-api-key` — lowercase, no Bearer, not Authorization. Every model that pattern-matches normal REST conventions gets this wrong.
The MCP tools are a separate surface with separate auth (OAuth, already connected in this workspace) and need no key. The CLI needs clay login.
4. Verify it works — free
GET /me costs zero credits and returns the user plus the workspace behind the key. Run it first, every time: it catches a wrong key or, worse, a right key pointed at the wrong workspace before you spend anything.
curl -sS https://api.clay.com/public/v0/me -H "clay-api-key: $CLAY_API_KEY"A 401 means the header name or key is wrong. A 200 naming a workspace you didn't expect means stop and ask.
5. How to invoke the skill
It triggers on intent — no slash command. Any mention of Clay, clay.com, a Clay table/workbook/routine/function/waterfall, CLAY_API_KEY, or asks like "enrich these domains through Clay", "search Clay's database for…", "run this Clay function on my list". Load it before writing the first call, not after the first 400.
6. Cost — the part that matters most
Clay bills real credits, there is no undo, and nothing is idempotent. A retried POST is a second charge.
Before any run at volume: check the balance, compute the call count, and tell the user what it will cost. Two mistakes worth memorizing because each is expensive by an order of magnitude:
- Omitting
entityIdsonadd-company-data-points/add-contact-data-pointsfans the enrichment across every entity in the task. run_subroutine_directbills per input object, capped at 1000 — a carelessly built array is 1000 billed runs.
And never enrich just to be helpful. If asked "tell me about Stripe", search and report; searching is usually free, attaching data points is the billable part.
Full detail in The two rules that protect the user's money.
7. Read next
Pick your surface in Pick the surface first, then read only that reference file:
references/rest-api.md— before writing any HTTP callreferences/mcp-tools.md— before invoking any Clay MCP toolreferences/platform.md— when the question is "what will this cost"scripts/clay_api.sh— wraps identity check, search-and-page, and run-routine-and-poll; read it before rewriting polling logic
When any source disagrees with another, https://developers.clay.com/openapi.json wins — including against this skill.
Pick the surface first
| If the user wants to… | Use | Auth |
|---|---|---|
| Have you find/enrich companies or contacts right now, conversationally | In-app MCP tools | OAuth, already connected |
| Write code, a script, or a service that calls Clay | Public REST API | clay-api-key header |
| Register a webhook, build a Workflow, or list routine runs | `clay` CLI | OAuth via clay login |
| Create a table, add a column, write rows, build a waterfall | Nothing — this is UI-only | — |
That last row is not a limitation to work around; it's the shape of the product. Clay has stated table building is not on the roadmap. When a user asks you to "create a Clay table and add an enrichment column," the honest answer is that they must do it in the UI, and then you can invoke it. Say so early rather than hunting for an endpoint that doesn't exist.
Read the reference file for whichever surface you picked:
- `references/rest-api.md` — all 13 REST endpoints with full request/response field tables, auth, both webhook directions, pagination, quotas, error model, and an exhaustive list of what is UI-only. Read this before writing any HTTP call.
- `references/mcp-tools.md` — all 13 in-app MCP tools with complete parameter schemas, which ones spend credits, and the search→context retrieval pattern. Read this before invoking any Clay MCP tool.
- `references/platform.md` — object model, waterfall mechanics, credit costs per provider, and platform limits. Read this when the question is "what will this cost" or "why did my enrichment behave that way."
The two rules that protect the user's money
Clay bills real credits. Both surfaces will happily spend them on a call you made speculatively, and there is no undo.
1. Never enrich to be helpful. If the user asks "tell me about Stripe," search and report — do not attach data points. Data points are the billable part; the search itself is usually free. Enrich only what the user asked for, for the entities they named. The MCP schemas carry an explicit ⚠️ CRITICAL warning about this, which tells you how often it goes wrong.
2. Nothing is idempotent. The REST API has no Idempotency-Key, no dedupe token, no conditional requests. items[].id looks like a dedupe key but is only a correlation id for matching results back to inputs. Re-POSTing a routine run starts a second run and charges again. So: GETs are safe to retry; POSTs are not. On a network failure where you never saw a routine_run_id come back, reconcile through the results endpoint before retrying — don't blind-retry.
Two specific high-cost mistakes worth memorizing:
- Omitting
entityIdsonadd-company-data-points/add-contact-data-pointsfans the enrichment across every entity in the task, not the one you meant. run_subroutine_directbills per input object, capped at 1000. A carelessly built array is 1000 billed runs.
When a call will spend a non-trivial amount, say what it will cost before making it. Users forgive a clarifying question; they don't forgive a surprise invoice.
On quoting costs. Clay's docs contradict themselves on the two questions people ask most, and both are in references/platform.md with sources. The pricing page says a miss is never charged; the magellan-data page says credits are consumed regardless of whether data is found. Waterfall billing is described both as "you only pay for the winner" and as a refund mechanism, never reconciled — and validation steps bill separately, so a failing row is not free. Quote the ambiguity rather than picking a side; a confident wrong number here becomes someone's budget.
Two factual corrections worth carrying, because both are commonly gotten wrong: Clay's column-reference syntax is /Column Name, not {{...}}. And "Playbooks" is not a Clay object — the word appears nowhere in the docs. If a user says it, they mean Workflows, Claybooks, or Signals; ask which.
REST API essentials
Base URL https://api.clay.com/public/v0. Everything else is in references/rest-api.md.
The auth header is `clay-api-key` — lowercase, no Bearer, not Authorization. This trips up every model that pattern-matches to normal REST conventions. Get the key from Settings → Account → API keys (beta).
curl https://api.clay.com/public/v0/me -H "clay-api-key: $CLAY_API_KEY"GET /me costs nothing and returns the user + workspace behind the key. Run it first — it catches a wrong key or a wrong workspace before you spend anything.
The 13 endpoints in four groups:
- `me` (1) — identity check.
- `search` (6) — search Clay's GTM database. Two modes: filters mode (JSON object) and query mode (a query string, beta, and what Clay recommends for new work). Both are two-step: create a search to get a
search_id, then repeatedly POST to/runto page through it. - `routines` (5) — invoke a Clay-managed function, a custom function, or a Workflow. Inline for ≤100 items; a three-step presigned-upload JSONL flow for batch.
- `tables` (1) — read-only structured query. Enterprise only, and each table must be individually toggled on.
Three behaviors that will bite you if you assume normal REST:
`202` is not an error. Routine runs are async: POST /routines/{id}/run returns 202 with a routine_run_id, and the results endpoint keeps returning 202 while it works. 200 means terminal. Treat unrecognized terminal status values as an unhandled terminal outcome — Clay says the status set is explicitly open, so don't write an exhaustive match.
Searches are stateful iterators, not cursors. /search/.../run consumes results as it goes and returns has_more, not a cursor. There is no way to re-read a page. If you need the data twice, keep it. Routines and tables use real cursors and behave normally.
Two different pagination models and two different quota models coexist. Rate limiting returns 429 with Retry-After; search result quotas return 402. They're unrelated — a 402 means the user is out of monthly search results, and backing off won't help.
Before building a filters-mode search, call GET /search/filters-mode/fields?source_type=people|companies. The OpenAPI spec deliberately leaves filters untyped because that endpoint is the discovery mechanism — it returns valid field names, allowed values, and worked examples. Guessing filter keys produces 400s.
scripts/clay_api.sh wraps the common flows (identity check, search-and-page, run-routine-and-poll) so you don't rewrite polling logic each time. Read it before writing your own.
MCP tools essentials
Everything is in references/mcp-tools.md; this is the part you need in working memory.
Search returns a skeleton, not the data. find-and-enrich-* responses carry base fields only. Emails, work history, and every data point live behind `get-task-context`. Calling it is mandatory before you tell a user something wasn't found — most "Clay couldn't find it" reports are actually an agent that stopped one call early.
Identify companies by domain or LinkedIn company URL, never by name. companyIdentifier rejects plain names. Person LinkedIn URLs are invalid where a company one is expected.
Own data vs. prospecting are different tools. query-objects and ask-question-about-accounts read the user's own CRM/Audiences data; find-and-enrich-* goes to Clay's external database. For an ambiguous "tell me about Acme," try query-objects first — if Acme is already an account, the user almost certainly meant theirs.
Functions are workspace-specific. run_subroutine needs a real subroutine_id from list_subroutines. If that returns empty, no Functions are MCP-enabled in this workspace and the subroutine tools are uncallable — tell the user to toggle Enable for MCP on a Function in Clay rather than trying to synthesize an id.
Hard caps: 100 search results (5 pages of 20); ask-question-about-accounts takes max 10 account ids; query-objects limit maxes at 100.
When Clay can't do it
Reach for the honest answer quickly in these cases, because each one has a plausible-looking dead end that can absorb a lot of effort:
- Creating or writing to tables — no REST write path. The only programmatic write into a Clay table is an inbound Monitor webhook URL that a human creates in the UI.
- Registering webhooks — CLI only (
clay webhooks create). No REST endpoint. The signing secret is shown exactly once and is unrecoverable. - Listing tables, routines, functions, or webhooks — no discovery endpoints anywhere. Table ids come from the app URL.
- User, seat, or workspace administration — no admin API at all.
- Credit or usage reporting — no billing endpoint. Credit budgets over the developer platform aren't available in open beta.
- Jobs search and count-only search — exist in the UI Search product, explicitly unavailable via API.
Trust the spec over the prose
Clay's own docs contradict each other in places, and the wider internet is worse — a lot of still-ranking content asserts "Clay has no public API," which was true in 2025 and is false now. When sources disagree, https://developers.clay.com/openapi.json wins.
A live example: Clay University says paid plans get 10,000 search results per request. The developer docs say 500, and the spec hard-caps limit at 500. 500 is correct.
Everything here is beta — info.version is "0", the base path is /public/v0, API keys are in a tab literally named api-keys-beta, and query-mode search is labelled beta. If a call fails in a way this skill doesn't explain, re-fetch the spec before assuming you got the syntax wrong.
