/

GTM systems

Clay API (2026): The Complete Guide to Routines and CLI

Clay API (2026): The Complete Guide to Routines and CLI.
Picture of Sparsh Gupta, Founder of Automation Jinn

Sparsh Gupta, Founder of Automation Jinn, Clay Expert

Sparsh Gupta, Founder of Automation Jinn, Clay Expert

11 min read

11 min read

Building on Clay's API?

Book a discovery call

""

Official Attio Expert Partner

""

Own your GTM stack

""

Built to drive revenue

""

Production-ready, not prototypes

""

Proven, hands-on experience

Yes, Clay has a real API. Clay shipped a public API, a CLI, and an agent plugin on 9 July 2026, and made them available on every plan including legacy ones. The developer platform exposes three primitives: Searches, Routines, and Tables. Routines are the one that changes how you should architect Clay.

Almost every guide ranking for this topic was written before that launch, so they describe workarounds for a problem that no longer exists. This one covers what actually shipped: how routines run, what the CLI does, where the limits sit, and when building on the API beats building another table.

Two different things are both called "the Clay API"

This is the single biggest source of confusion right now, and it is worth sixty seconds before anything else. Clay has two features with almost the same name, built for opposite directions of data flow.


HTTP API (in-table)

Public API (developer platform)

What it is

An enrichment column inside a Clay table

A REST API you call from your own code

Direction

Clay calls out to someone else's API

Your systems call in to Clay

Where you configure it

The Clay UI

Your backend, terminal, or agent

Plan

Growth and above

All plans, including legacy

Launched

Long-standing feature

9 July 2026

The HTTP API column is how you pull data from a vendor Clay does not natively integrate with, or push a record into your CRM at the end of a table. It has been there for years. It is what the older articles on this topic are describing.

The Public API is the new thing. It lives at api.clay.com/public/v0, authenticates with a clay-api-key header, and lets a queue worker, a backend job, or a coding agent run Clay logic with no Clay tab open anywhere. If you have been told Clay has no API, that was true until July and is not true now.

The three primitives

Clay's developer surface is deliberately small. Three primitives cover everything, and the docs are clear about which one to reach for.

Primitive

Use it for

Availability

Searches

Find companies and people in Clay's GTM database

All plans, with caps

Routines

Run Clay logic: enrichment, research, scoring, routing

All plans

Tables

Read structured data from a table you already know

Enterprise only

Most integrations only need the first two. You search for the right records, then run a routine over them, then send the structured output wherever it needs to go. Tables is a read path for teams who have already made Clay the system of record for some slice of GTM data, and it is gated behind Enterprise.

Routines: the part that actually matters

A routine is packaged Clay logic you can call from outside Clay. The provider waterfall, the prompts, the fallback order, the output schema, all of it lives in one definition that your systems invoke by ID. There are three types.

Routine type

Where you build it

Status

Clay-managed functions

Clay provides them

Available

Custom functions

Clay UI

Available

Workflows

Plugin or CLI

Alpha

Clay-managed functions are the pre-built jobs: work email, phone number, job title, company domain, employee count, revenue, tech stack, job openings, funding, news. If Clay already does the enrichment you need, you call it directly without building anything.

Custom functions are your team's own logic, built once in the Clay UI and then exposed to everything else. This is where a scoring model, an inbound routing rule, or a five-provider email waterfall should live. To expose one, open Functions in Clay, select it, open Details, enable API or MCP, and copy the t_... ID. You then call it as function:t_abc123.

Workflows are the interesting and immature option, which I will come back to.

The architectural argument for routines is durability, and it is the reason I now push clients toward this path by default. A table is a document. Someone built it, someone owns it, and when that person changes role the table quietly rots while still running. A routine is a definition your whole stack calls by ID. When a provider deprecates a field, you fix one routine rather than auditing forty tables to find the three still running stale logic.

Want your Clay logic running as routines your backend can call, not tables someone has to remember to open?

Book a discovery call

Want your Clay logic running as routines your backend can call, not tables someone has to remember to open?

Book a discovery call


Running a routine

The call is deliberately boring, which is the point. Start a run, get a routine_run_id, then fetch results.

ROUTINE_RUN_ID=$(curl --request POST \
  --url "https://api.clay.com/public/v0/routines/function:t_abc123/run" \
  --header "Content-Type: application/json" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY" \
  --data '{
    "items": [
      { "id": "row-1", "inputs": { "domain": "clay.com" } }
    ]
  }' | jq -r '.routine_run_id')

curl --request GET \
  --url "https://api.clay.com/public/v0/routines/run/$ROUTINE_RUN_ID/results" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY"
ROUTINE_RUN_ID=$(curl --request POST \
  --url "https://api.clay.com/public/v0/routines/function:t_abc123/run" \
  --header "Content-Type: application/json" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY" \
  --data '{
    "items": [
      { "id": "row-1", "inputs": { "domain": "clay.com" } }
    ]
  }' | jq -r '.routine_run_id')

curl --request GET \
  --url "https://api.clay.com/public/v0/routines/run/$ROUTINE_RUN_ID/results" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY"
ROUTINE_RUN_ID=$(curl --request POST \
  --url "https://api.clay.com/public/v0/routines/function:t_abc123/run" \
  --header "Content-Type: application/json" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY" \
  --data '{
    "items": [
      { "id": "row-1", "inputs": { "domain": "clay.com" } }
    ]
  }' | jq -r '.routine_run_id')

curl --request GET \
  --url "https://api.clay.com/public/v0/routines/run/$ROUTINE_RUN_ID/results" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY"
ROUTINE_RUN_ID=$(curl --request POST \
  --url "https://api.clay.com/public/v0/routines/function:t_abc123/run" \
  --header "Content-Type: application/json" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY" \
  --data '{
    "items": [
      { "id": "row-1", "inputs": { "domain": "clay.com" } }
    ]
  }' | jq -r '.routine_run_id')

curl --request GET \
  --url "https://api.clay.com/public/v0/routines/run/$ROUTINE_RUN_ID/results" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY"
ROUTINE_RUN_ID=$(curl --request POST \
  --url "https://api.clay.com/public/v0/routines/function:t_abc123/run" \
  --header "Content-Type: application/json" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY" \
  --data '{
    "items": [
      { "id": "row-1", "inputs": { "domain": "clay.com" } }
    ]
  }' | jq -r '.routine_run_id')

curl --request GET \
  --url "https://api.clay.com/public/v0/routines/run/$ROUTINE_RUN_ID/results" \
  --header "clay-api-key: $CLAY_PUBLIC_API_KEY"

Inline runs take 1 to 100 items. Runs are asynchronous, so a 202 means the job is still going and you should poll the results endpoint until it returns a terminal 200.

Do not build a polling loop if you can avoid it. Both run and run-batch/start accept an optional webhook_id, and Clay will notify that endpoint when the run finishes. Register one with clay webhooks create, store the signing secret it returns once and never shows again, and verify the X-Clay-Signature header before trusting a delivery. Clay is explicit that webhook delivery is not guaranteed, so treat webhooks as the fast path and keep polling as the fallback rather than deleting your poller entirely.

Batch runs and the 50,000-row ceiling

This is the strongest practical reason to move off tables, and it is a hard number rather than a preference. Clay tables cap at 50,000 rows on every plan, including Enterprise. When you hit it, Clay stops importing and shows no error.

If your CRM has 120,000 contacts, the table path means splitting them across three tables and keeping all three in sync forever. The batch path does not care. You request an upload URL, push a JSONL file to it, and start the run against a file ID.

clay routines runs start function:t_abc123 --bulk
clay routines runs start function:t_abc123 --bulk
clay routines runs start function:t_abc123 --bulk
clay routines runs start function:t_abc123 --bulk
clay routines runs start function:t_abc123 --bulk

Each line of the JSONL is one item:

{ "id": "row-1", "inputs": { "domain": "clay.com" } }
{ "id": "row-1", "inputs": { "domain": "clay.com" } }
{ "id": "row-1", "inputs": { "domain": "clay.com" } }
{ "id": "row-1", "inputs": { "domain": "clay.com" } }
{ "id": "row-1", "inputs": { "domain": "clay.com" } }

The CLI is wrapping three API calls: POST /routines/{id}/run-batch/upload-url to get a presigned URL, a PUT of the file to that URL as application/x-ndjson, then POST /routines/{id}/run-batch/start with the returned file_id. Poll run-batch/{run_id}/results for progress, or attach a webhook and skip the polling.

If you have ever split a database across Clay tables to get under the row cap, this alone justifies the migration.

Searches, and the limits Clay does publish but nobody quotes

Searches query Clay's proprietary company and people database. Use advanced search, which takes a query language supporting cross-entity criteria and nested Boolean logic, rather than the older filters mode.






Fetch the query reference from /search/query-mode/reference before writing a query. It is a markdown document describing the queryable fields and grammar, which means your coding agent can read it and write valid queries without you learning the syntax.

The caps matter more than the syntax, and this is the table I have not seen anywhere outside Clay's own docs:

Plan

Per request

Per period

Resets

Free

50

100 per month

1st of the month

Trial

50

10,000 for the trial

Not applicable

Paid

500

1,000,000 per year

1 January

Enterprise

500

10,000,000 per year

1 January

Exceeding a limit returns HTTP 402 naming the limit you hit, not a 429. That distinction will save you an afternoon: 429 means slow down and retry, 402 means you are out of budget and retrying will not help. Malformed input still returns 400.

A million search results a year sounds enormous until you point an always-on TAM job at it. Budget it like a metered resource, because it is one.

The CLI and the agent plugin

The clay CLI is JSON-first by design. Every command writes machine-readable JSON to stdout on success and a structured error envelope to stderr on failure, with no spinners, colours, or progress bars. That is not an aesthetic choice, it is what makes the CLI safe for an agent to drive.

Exit codes carry the meaning, so scripts and agents branch on $? instead of parsing English:

Exit

Meaning

0

Success

2

Validation error, input was rejected

3

Auth required or invalid, run clay login

4

Rate limited, back off using details.retryAfter

5

Network error or timeout

6

Not found

Authentication is OAuth through clay login, which opens a browser. On a container or an SSH session use clay login --device, which prints a link and a short code you approve from any browser. clay whoami is the canonical check that auth is working.

The agent plugin bundles the CLI with a local MCP server and a set of skills, and installs into Claude Code, Codex, or Cursor. There is a real constraint here worth knowing before you promise it to anyone: the plugin needs a host that can both call MCP tools and run a shell. Claude Desktop, claude.ai, and ChatGPT can call MCP tools but have no shell tied to the session, so they cannot run the CLI. The plugin is for coding agents, not chat apps.

One operational detail that catches people: the MCP server resolves its session once at startup and stays pinned to that workspace. If you sign in after the agent launched, or switch workspaces, restart the agent or it will keep using the old credential.

Functions or Workflows

Both are routines, both run programmatically, both support batch runs. The difference is where the logic is built and how mature the surface is.

You want

Choose

Clay-provided enrichment or research

Clay-managed functions

Reusable logic your team already built in the UI

Custom functions

To create and edit logic from an agent or terminal

Workflows (Alpha)

Step-by-step run inspection and snapshots

Workflows (Alpha)

To avoid the 50,000-row limit entirely

Workflows (Alpha)

Workflows run a trigger into connected nodes: agent nodes for reasoning and classification, enrich nodes for Clay actions, code nodes for deterministic transformation and filtering. Triggers come from a CSV, a webhook, an Audience, a Clay table, or a manual test run. Agents can read and edit the graph, validate it, run test actions, inspect failed steps, pause and resume runs, and restore snapshots.

That is a real step up from anything else in the GTM tooling category, and it is also Alpha. Clay says plainly to expect the surface to evolve. My position for client work in August 2026 is to put production paths on custom functions, which are stable and batchable, and to use Workflows where the inspection and snapshot loop earns its keep: complex multi-step flows you are still iterating on, and large runs where the row cap actually binds. Revisit that split each quarter, because Alpha does not stay Alpha.

What it costs

The API and CLI are available on every plan, including legacy ones, which is unusually generous and worth taking advantage of early. What you pay for is the work the routines do. Clay meters two things: Data Credits for the data you buy, and Actions for the operations it performs. Running a routine from a script consumes exactly what running the same enrichment in a table would.

Current self-serve pricing is Launch at $185 a month and Growth at $495 a month on Clay's pricing page, both with unlimited seats. The plan gate that catches teams is that the in-table HTTP API column and native CRM sync sit on Growth, while the Public API does not. I have written up the full plan maths, credit costs per record, and the three mistakes that double the invoice in Clay pricing 2026.

Verify current pricing with Clay before you buy. The March 2026 restructure retired the old Starter, Explorer, and Pro tiers, and a good deal of third-party pricing content still quotes them.

Honest limitations

The developer platform is four weeks old at the time of writing, and it shows in specific, mostly forgivable ways.

Tables is Enterprise only, and there is no list-tables endpoint. Your integration has to already know the table ID, which you pull out of the Clay URL after /tables/. If you wanted to build a dashboard on Clay data from a Growth plan, you cannot yet.

Workflows are Alpha. Powerful, agent-native, and explicitly subject to change. Do not put a revenue-critical path on them this quarter without a fallback.

Error bodies have no stable codes. You get a human-readable message and the HTTP status, and Clay tells you to branch on the status. That is workable but it means your error handling is coarser than you would want against a mature API.

The plugin is open beta on Mac and Linux. Windows users are waiting.

Webhook delivery is not guaranteed. Keep the poller.

None of these are reasons to stay on tables. They are reasons to scope the first build sensibly: custom functions and Searches in production, Workflows where you are still experimenting, and a plan to revisit Tables when you are on Enterprise or when Clay opens it up.

How I would architect this

If you are moving an existing Clay setup onto the API, the order matters more than the code.

Start by finding the logic you have rebuilt more than twice. Almost every team has an email waterfall or a fit-scoring rule copy-pasted across several tables, drifting slightly in each one. That is your first custom function, because consolidating it pays for itself immediately and proves the pattern to the rest of the team.

Then move the trigger to where the event actually happens. The reason to build on routines is not that the API is elegant, it is that a trial signup, a form fill, a support ticket, or a nightly CRM sweep can each fire Clay logic at the moment it matters instead of waiting for someone to open a table and click run. Pair each of those with a webhook so your system reacts on completion.

Keep the Clay UI for what it is good at. Discovery, testing a new play, eyeballing whether an enrichment returns anything useful. Build it in the UI, prove it, then promote it to a function. Teams that try to do everything from the terminal on day one lose the fastest feedback loop Clay has.

Finally, treat search volume and credits as budgets with owners, not as invisible resources. A routine your backend calls on every signup will consume more predictably than a table someone runs by hand, which is a feature, but only if somebody is watching the meter.

If you want the broader picture of what Clay does and where it stops being worth paying for, that is in the complete Clay guide. If you are still building enrichment in tables, Clay waterfall enrichment covers the logic you will eventually want to promote into a function.

Frequently asked questions

Does Clay have an API?

Yes. Clay launched its public developer API, a CLI, and an agent plugin on 9 July 2026, available on all plans including legacy ones. It exposes Searches, Routines, and Tables. Guides published before that date describing webhook workarounds or third-party proxies are out of date.

Where can I find my Clay API key?

Create one in Clay under Settings, then Account, then API keys. You can also ask your coding agent to generate one after installing the agent plugin. Pass it in the clay-api-key header on every request, and keep it server-side rather than in browser or mobile code.

What is the difference between the Clay HTTP API and the Clay Public API?

The HTTP API is an enrichment column inside a Clay table that calls out to someone else's endpoint, and it sits on the Growth plan. The Public API is a REST API your own systems call in to, launched in July 2026 and available on every plan.

What does the Clay API cost?

The API and CLI carry no separate fee on any plan. You pay for the work routines do, metered as Data Credits for purchased data and Actions for operations, at the same rates as the UI. Self-serve plans start at $185 a month for Launch and $495 for Growth.

Can I use the Clay API with ChatGPT or Claude Desktop?

Not the agent plugin. It needs a host that can run the clay CLI as a local process, which means Claude Code, Codex, or Cursor. Chat apps can call MCP tools but have no shell tied to the session. Clay's separate MCP for reps does work inside those chat tools.

What is a Clay routine?

A routine is Clay logic packaged so it can run from outside the Clay UI. There are three types: Clay-managed functions for common enrichment jobs, custom functions your team builds in the UI, and Workflows, an Alpha type built entirely from the CLI or a coding agent.

Sparsh Gupta, Founder of Automation Jinn and a Clay expert, helps seed to Series B B2B teams turn Clay from a pile of tables into GTM infrastructure their backend, CRM, and agents can call. If you want your enrichment and scoring logic running as routines instead of tables someone has to remember to open, book a discovery call.

Build Clay into your stack, not around it.

Book a discovery call

Build Clay into your stack, not around it.

Book a discovery call