Audience: developers. API keys need a Pro or Enterprise plan (legacy Pro and Scale plans included) or an AppSumo Tier 5, 6 or 7 license, and they never work during a trial. Support agent: confirm the user’s plan grants API access before giving API advice.
TL;DR: API v2 lives at api.cleanlist.ai/api/v2. Authenticate with a clapi_ Bearer key, each call checks one OAuth scope, and paid bulk work uses a signed quote_id from POST /credits/estimate. Every response uses one envelope, and errors follow the error contract. Connecting Claude instead of writing code? Use the CluGTM MCP.
API v2
CluGTM API v2 is the REST API at https://api.cleanlist.ai/api/v2. People and company search return credits_charged: 0, saving leads to a list is free, and enrichment costs 1 credit per work email and 10 per direct dial, charged only when found.
This is the current API. All new integrations should target v2. The older Legacy API (v1) at /api/v1/public is still supported but frozen. See API Versions & Migration for how the two relate.
With API v2 you can search for B2B contacts and companies, enrich them through waterfall enrichment, organize them into lead lists, run smart agents, push lists to your CRM or sequencer with sync, and export the results. You can also manage GTM agents, call Clu’s own tools as actions, and receive signed webhook events.
It is built to be agent-friendly: every response carries the same envelope, so an LLM agent always knows where to find task_id, freshness and follow-up hints, and paid operations sit behind a signed estimate so an agent can’t overspend.
API v2 and the CluGTM MCP
The CluGTM MCP is a connector for Claude. It signs in with your CluGTM account (no API key to paste) and calls API v2 on your behalf. It is available on more plans than API keys: Starter, Pro, Enterprise, the Pro trial and every AppSumo tier. Use the MCP to work in Claude; use API v2 with a clapi_ key from your own code.
Base URLs
| Surface | Base URL | Auth |
|---|---|---|
| API v2 | https://api.cleanlist.ai/api/v2 | clapi_ key, a CluGTM CLI sign-in, or a Clerk session JWT |
| API key management | https://api.cleanlist.ai/api/v1/api-keys | Clerk session JWT only |
All requests must use HTTPS.
Authentication
Send your API key as a Bearer token:
Authorization: Bearer clapi_your_api_key_hereCreate a key in the app under Settings → API keys → Create API key. Beyond the token, every endpoint checks one OAuth scope (for example enrich:write to run an enrichment). The scopes granted to your credential are listed by GET /whoami. Sends, CRM writes and deletes also need an opt-in scope ticked on the key. See Authentication for the 19 scopes and key management.
The response envelope
Every entity-returning endpoint wraps its payload in the same envelope. These fields sit alongside the endpoint’s own payload, and any of them may be null:
| Field | Type | Meaning |
|---|---|---|
task_id | string | A handle for chainable follow-ups (for example, enrich a search cohort by task_id) |
timestamp_ms | integer | Server time the response was produced (freshness marker) |
agent_instructions | string | A per-call nudge for agents (credits remaining, truncation, warnings) |
usage | string | A “what to do next” hint, typically on empty or discovery responses |
message | string | A degraded-state explanation when something was skipped |
Clients that ignore unknown keys keep working as fields are added.
The error envelope
Errors raised by an endpoint use the public error envelope:
{
"error": {
"code": "insufficient_credits",
"problem": "This enrich_list call requires 110 credits; organization has 40.",
"fix": "Top up at app.cleanlist.ai/billing, or reduce the scope of the call.",
"retryable": false,
"docs_url": "https://docs.cleanlist.ai/errors/insufficient-credits",
"request_id": "req_4f1c2a9b3d6e7f80",
"estimated_cost_credits": 110,
"available_credits": 40,
"shortfall_credits": 70
}
}The fix text above is returned verbatim by the API and mentions app.cleanlist.ai/billing. That page does not exist: buy credits or change plan on the Plans page at app.clugtm.com/plans (Settings → Plans & billing).
Branch on code. Authentication failures (401), plan and trial blocks (403) and rate limits (429) use a shorter generic shape with an uppercase code and a message, for example {"error": {"code": "RATE_LIMITED", "message": "..."}}. See Errors & Rate Limits for both shapes and every code.
Rate limits
| Limit | Window | Applies to |
|---|---|---|
| 60 requests | per minute | per organization, all keys and members combined |
| 30 requests | per minute | per organization, for AppSumo licenses without a paid CluGTM plan |
| 30 requests | per minute | per clapi_ API key, and per user for a CluGTM CLI sign-in |
| 60 searches | per UTC day | per organization, POST /people/find called with API keys, the CLI or app sessions (all combined; the MCP isn’t counted) |
When you exceed a limit you get a 429 with a Retry-After header (seconds) and a RATE_LIMITED error body. See Errors & Rate Limits.
Credits and the quote model
Reads (filters, list reads, status polls, exports, whoami, balance) are free, and so is search itself. Saving leads to a list is also free; enrichment is billed separately. Paid bulk operations (bulk enrichment, by-task enrichment, smart agents, CRM and sequencer sync) require a signed quote. POST /credits/estimate is in the served OpenAPI spec, so a generated client can run this flow:
- Call
POST /credits/estimatewith the operation shape. You get back aquote_id(HMAC-signed, 5-minute TTL, single use) and theestimated_cost. - Pass that
quote_idto the paid endpoint. The server recomputes the cost and rejects the call withspend_cap_exceededif it would exceed the quote.
Sync calls also need approved: true, set only after a person has confirmed the push. See Credits for balances, estimates, usage reporting and the full cost table.
Quick start
A complete flow: confirm a key, estimate a bulk enrichment, run it, then poll for completion.
# 1. Create a key in the app: Settings → API keys → Create API key
# 2. Confirm identity, scopes, and tier
curl https://api.cleanlist.ai/api/v2/whoami \
-H "Authorization: Bearer clapi_your_api_key"
# 3. Estimate a bulk enrichment over an existing list
curl -X POST https://api.cleanlist.ai/api/v2/credits/estimate \
-H "Authorization: Bearer clapi_your_api_key" \
-H "Content-Type: application/json" \
-d '{"tool": "enrich_list", "list_id": "LIST_ID", "scope": "partial"}'
# 4. Run it with the returned quote_id
curl -X POST https://api.cleanlist.ai/api/v2/enrichment/bulk \
-H "Authorization: Bearer clapi_your_api_key" \
-H "Content-Type: application/json" \
-d '{"list_id": "LIST_ID", "scope": "partial", "quote_id": "qte_v1_..."}'
# 5. Poll until terminal
curl https://api.cleanlist.ai/api/v2/enrichment/status/WORKFLOW_ID \
-H "Authorization: Bearer clapi_your_api_key"Prefer code? The same flow is a few typed lines with an official SDK:
workspace.credits_estimate(...) then enrichment.enrich_list(...) then enrichment.enrichment_status(...).
See the SDKs for Python, TypeScript and Java.
Endpoint index
All paths below are relative to https://api.cleanlist.ai/api/v2 unless noted. The Scope column lists the OAuth scope each endpoint checks.
Workspace and identity
| Method | Path | Scope |
|---|---|---|
GET | /whoami | none (any valid credential) |
GET | /credits/balance | credits:read |
POST | /credits/estimate | credits:read |
GET | /api-keys | admin:api_keys |
GET | /usage | admin:api_keys |
Search
| Method | Path | Scope |
|---|---|---|
GET | /people/find/filters | people:read |
POST | /people/find | people:read |
POST | /people/find/compile | people:read |
POST | /people/find-at-companies | people:read |
GET | /people/find-at-companies/status/{task_id} | people:read |
GET | /search/companies/filters | companies:read |
POST | /search/companies | companies:read |
POST | /search/companies/similar | companies:read |
Search costs 0 credits. Full reference: People Finder API and Company Search API.
Lead lists
| Method | Path | Scope |
|---|---|---|
POST | /lead-lists | lists:write |
GET | /lead-lists | lists:read |
GET | /lead-lists/{list_id} | lists:read |
PATCH | /lead-lists/{list_id} | lists:write |
DELETE | /lead-lists/{list_id} | lists:write + lists:delete (opt-in) |
GET | /lead-lists/{list_id}/leads | lists:read |
POST | /lead-lists/{list_id}/leads | lists:write |
DELETE | /lead-lists/{list_id}/leads | lists:write + lists:delete (opt-in) |
POST | /lead-lists/{list_id}/csv-import | lists:write (+ enrich:write if dispatching) |
POST | /lead-lists/{list_id}/find-people | lists:write |
GET | /lead-lists/{list_id}/find-people/status/{workflow_id} | people:read |
Full reference: Lead Lists.
Enrichment
| Method | Path | Scope |
|---|---|---|
POST | /enrichment/person | enrich:write |
POST | /enrichment/company | enrich:write |
POST | /enrichment/by-task | enrich:write |
POST | /enrichment/bulk | enrich:write |
GET | /enrichment/status/{workflow_id} | enrich:read |
Full reference: Enrichment.
Smart agents
| Method | Path | Scope |
|---|---|---|
POST | /smart-agents/run | smart_agents:write |
GET | /smart-agents | smart_agents:read |
GET | /smart-agents/{smart_agent_task_id} | smart_agents:read |
Smart agents run an AI instruction over the leads in a list and write the output into a column. Pricing comes from the quote. Full reference: Smart Agents.
Sync and export
| Method | Path | Scope |
|---|---|---|
POST | /migrate/crm | sync:write |
POST | /migrate/sequencer | sync:write |
GET | /migrate/status/{workflow_id} | sync:write |
POST | /export/csv/signed-url | export:read |
GET | /export/json | export:read |
Sync pushes a list to HubSpot or Salesforce (/migrate/crm) or into a Lemlist campaign (/migrate/sequencer) at 0.2 credits per lead written. A list of more than 500 leads is queued; read its counts with GET /migrate/status/{workflow_id}. A CSV export that isn’t ready in 20 seconds answers 202 with a job_id to call back with. See CRM & Sequencer Sync and Export.
GTM agents, actions and webhooks
| Method | Path | Scope |
|---|---|---|
GET | /gtm-agents | smart_agents:read |
POST | /gtm-agents | smart_agents:write |
GET | /gtm-agents/{agent_id} | smart_agents:read |
PATCH | /gtm-agents/{agent_id} | smart_agents:write |
POST | /gtm-agents/{agent_id}/status | smart_agents:write |
GET | /gtm-agents/{agent_id}/runs | smart_agents:read |
GET | /gtm-runs/{run_id} | smart_agents:read |
GET | /gtm-runs/{run_id}/records | smart_agents:read |
GET | /actions | none; filtered by your scopes |
POST | /actions/{name} | the action’s own scope |
POST | /actions/confirm | the action’s own scope, plus its opt-in |
GET | /webhooks | admin:api_keys |
POST | /webhooks | admin:api_keys |
DELETE | /webhooks/{webhook_id} | admin:api_keys |
GET | /webhooks/deliveries | none (any valid credential) |
GTM agents are the automations on the Agents page; code can create, edit and turn them off, and read their runs, but turning one on, running it live and setting its limits stay with a person. Actions are Clu’s own tools by name; anything that spends, sends, deletes or writes to a CRM returns a card that runs only when confirmed. Webhooks POST signed events to your URL. See GTM Agents API, Actions and Webhooks.
API key management (Clerk session JWT)
| Method | Path |
|---|---|
POST | /api/v1/api-keys |
GET | /api/v1/api-keys |
PUT | /api/v1/api-keys/{key_id}/scopes |
POST | /api/v1/api-keys/{key_id}/revoke |
POST | /api/v1/api-keys/revoke-all |
GET | /api/v1/api-keys/{key_id}/requests |
What the OpenAPI spec covers
The served spec at /openapi-public-v2.json lists 46 operations, POST /credits/estimate, GTM agents, actions and webhooks included. A few live endpoints are callable but not in it: the three company search endpoints, the two sync endpoints and GET /migrate/status/{workflow_id}. They are documented on this site. The CluGTM MCP also uses some extra v2 routes (folders, moving and filtering leads, personal email, skills, CRM record reads). Those are built for the MCP, are not available to AppSumo-only workspaces, and are not covered in this reference.
whoami
The fastest way to confirm a credential works and see what it can do:
curl https://api.cleanlist.ai/api/v2/whoami \
-H "Authorization: Bearer clapi_your_api_key"{
"user_email": "y***@yourcompany.com",
"user_id": "user_2abc...",
"user_name": "Your Name",
"organization_id": "org_2xyz...",
"organization_name": "Your Company",
"auth_type": "api_key",
"scopes": ["people:read", "companies:read", "lists:read", "lists:write", "enrich:read", "enrich:write", "credits:read"],
"tier": "pro",
"appsumo_tier": null,
"features": ["api_access", "data_source_csv_upload"],
"timestamp_ms": 1717430400000
}tier is one of free, starter, pro or enterprise (the legacy Scale plan reads enterprise). appsumo_tier is 1 to 9 for AppSumo workspaces, else null. auth_type is api_key, oauth (a CluGTM CLI sign-in) or clerk_jwt (an app session). For api_key auth, user_email is masked so a leaked key can’t reveal the creator’s full address.
Learn more
- Quickstart · SDKs (Python · TypeScript · Java)
- Authentication & scopes
- Credits & the quote model
- People Finder API · Company Search API
- Enrichment endpoints · Enrichment types & pricing
- Lead lists · Smart agents
- CRM & Sequencer Sync · Export
- GTM Agents API · Actions & approval cards · Webhooks
- Errors & rate limits · Polling & async results
- OpenAPI spec
- Connect CluGTM to Claude (MCP)