New: meet Clu, and send email, LinkedIn and call steps with Sequences. Watch the videos →
API Reference (v2)
Authentication
🔌

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: Every API v2 request uses Authorization: Bearer clapi_... (or a Clerk session JWT). On top of the token, each endpoint checks one OAuth scope (for example enrich:write); a missing scope returns 403 insufficient_scope. Create keys under Settings → API keys → Create API key. Up to 10 active keys per user per organization.

Authentication

Cleanlist API v2 accepts two credential types:

CredentialHeaderUsed by
API key (clapi_...)Authorization: Bearer clapi_...servers, scripts, your own agents
Clerk session JWTAuthorization: Bearer <jwt>the Cleanlist app, and the /api/v1/api-keys management endpoints
Authorization: Bearer clapi_your_api_key_here

The Cleanlist MCP for Claude doesn't use an API key: it signs in with your Cleanlist account through OAuth and carries the same scopes.

Who can create API keys

PlanAPI keys
Pro, EnterpriseYes
Legacy Pro, legacy Scale (shown as Enterprise)Yes
AppSumo Tiers 5, 6 and 7Yes
Free, Starter (current or legacy)No
AppSumo Tiers 1 to 4No
Any trial, including the Pro trialNo

On a plan without API access, the API keys page shows an upgrade card ("The Cleanlist API.") instead of the create form.

OAuth scopes

Authentication answers "who are you?"; scopes answer "what may you do?". Every /api/v2 endpoint checks one scope before it runs. If your credential doesn't carry it, the request fails with 403 and code insufficient_scope.

ScopeGrants
people:readPeople Finder, people filters, compile, find-at-companies, find-people status
people:writeReserved; no endpoint checks it today
companies:readCompany search, company filters, similar companies
companies:writeReserved; no endpoint checks it today
lists:readRead lead lists and their leads
lists:writeCreate, update and delete lists, add and remove leads, CSV import, find-people jobs
enrich:readPoll enrichment status
enrich:writeRun person, company, by-task and bulk enrichment
smart_agents:readList smart-agent runs and read results
smart_agents:writeRun a smart agent
sync:writeSync a list to a CRM or sequencer
export:readCSV signed URLs and JSON export
credits:readCredit balance and cost estimates
admin:api_keysList keys and read usage reports through the API
crm:readRead CRM records through Cleanlist (used by the Cleanlist MCP)

Scopes are granted per user, per organization. Every clapi_ key you create carries the scopes your user has in the organization the key was created in, and GET /api-keys reports those scopes on each key. You can't create a key with a narrower scope set than your user has. Users who were granted the full set before crm:read existed receive crm:read automatically.

The five legacy v1-only endpoints (/api/v1/public/auth/validate-key, /enrich/bulk, /enrich/status, /webhooks/deliveries, POST /folders) don't check scopes. Every v2 endpoint does.

A 403 insufficient_scope error includes the required and granted scopes in details:

{
  "error": {
    "code": "insufficient_scope",
    "problem": "This call requires the 'enrich:write' scope, which is not granted to the authenticating credential.",
    "fix": "Either re-issue the API key with the required scope, or sign in with an identity that has it. The full list of granted scopes is visible via /api/v2/whoami.",
    "retryable": false,
    "docs_url": "https://docs.cleanlist.ai/errors/insufficient-scope",
    "request_id": "req_...",
    "details": { "required_scope": "enrich:write", "granted_scopes": ["lists:read", "people:read"] }
  }
}

To see what your credential can do, call GET /whoami; it returns the granted scopes list.

Key format

PropertyValue
Prefixclapi_
OwnershipThe user who created it, in the organization it was created in
Secret body48 random URL-safe bytes
VisibilityShown once at creation; afterwards only the prefix and last 4 characters are displayed
Limit10 active keys per user per organization

The full key is only returned once, at creation. Store it in a secret manager, env var or vault. If you lose it, revoke it and create a new one.

Creating a key

The fastest path is the app:

  1. Go to Settings → API keys.
  2. Under Create API key, enter a Name (for example "CI integration") and, if you want, an Expiry.
  3. Click Create API key and copy the key shown under New API key.

Your keys are listed under Your API keys with their status and request count.

You can also create keys programmatically with your Clerk session token. These management endpoints live at /api/v1/api-keys (not under /public or /api/v2) and authenticate with the Clerk session JWT, not a clapi_ key.

Creating and listing keys need API access on your plan (see Who can create API keys). Without it, POST /api/v1/api-keys returns 403 with code PERMISSION_DENIED, the message "This feature requires the Pro plan or higher." and details.error: "feature_gated". Revoking keys and reading a key's request log are not gated.

POST /api/v1/api-keys

curl -X POST https://api.cleanlist.ai/api/v1/api-keys \
  -H "Authorization: Bearer <clerk_session_jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production server",
    "expires_at": "2027-01-01T00:00:00Z"
  }'

Request body

FieldTypeRequiredDescription
namestringNoA human-readable label
expires_atISO-8601 datetimeNoOptional expiration; omit for a non-expiring key

Response

{
  "api_key": "clapi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "created_at": "2026-04-06T15:00:00Z",
  "expires_at": "2027-01-01T00:00:00Z"
}

If you already have 10 active keys in this organization, creation returns 400 with "Maximum active API keys reached (10). Revoke an existing key before creating a new one."

Listing your keys

GET /api/v1/api-keys

Lists your active and inactive keys in the current organization (Clerk session JWT, needs API access on the plan). The full secret is never returned, only the first 10 characters (key_prefix) and last 4 (key_last4).

curl https://api.cleanlist.ai/api/v1/api-keys \
  -H "Authorization: Bearer <clerk_session_jwt>"
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Production server",
    "key_prefix": "clapi_AbCd",
    "key_last4": "wXyZ",
    "created_at": "2026-04-06T15:00:00Z",
    "expires_at": null,
    "last_used_at": "2026-04-06T15:42:11Z",
    "request_count": 1547,
    "is_active": true
  }
]

There is also a clapi_-authenticated GET /api/v2/api-keys (scope admin:api_keys) that returns the same per-key fields (key_id, key_prefix, key_last4, name, scopes, created_at, last_used_at, is_active).

Revoking keys

In the app, open Settings → API keys, find the key under Your API keys and click Revoke, or click Revoke all.

POST /api/v1/api-keys/{key_id}/revoke

Revoke a single key by its UUID:

curl -X POST https://api.cleanlist.ai/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000/revoke \
  -H "Authorization: Bearer <clerk_session_jwt>"

POST /api/v1/api-keys/revoke-all

Revoke every active key for the authenticated user (useful if you suspect a leak):

curl -X POST https://api.cleanlist.ai/api/v1/api-keys/revoke-all \
  -H "Authorization: Bearer <clerk_session_jwt>"
{ "success": true, "message": "3 API key(s) revoked successfully" }

Revoked keys can't be reactivated; create a new one.

Inspecting key usage

In the app, click View requests on a key under Your API keys.

GET /api/v1/api-keys/{key_id}/requests

Pull the most recent requests made with a specific key:

curl "https://api.cleanlist.ai/api/v1/api-keys/550e8400-e29b-41d4-a716-446655440000/requests?limit=100" \
  -H "Authorization: Bearer <clerk_session_jwt>"
Query paramDefaultRange
limit1001 to 500
[
  {
    "id": "log-uuid",
    "method": "POST",
    "path": "/api/v2/enrichment/bulk",
    "status_code": 200,
    "duration_ms": 184,
    "ip_address": "203.0.113.42",
    "user_agent": "python-requests/2.32.0",
    "created_at": "2026-04-06T15:42:11Z"
  }
]

Using a key

Once you have a clapi_ key, every API v2 call uses the same Bearer header:

curl https://api.cleanlist.ai/api/v2/credits/balance \
  -H "Authorization: Bearer clapi_your_api_key"
🧑‍💻

With an SDK: you don't build this header yourself. Pass your key to the client (Cleanlist(access_token="clapi_…"), or the CLEANLIST_API_KEY env var) and every call is authenticated. See the SDKs.

Verifying a key

To confirm a key is valid (and inspect its scopes and tier) without spending credits, call GET /whoami:

curl https://api.cleanlist.ai/api/v2/whoami \
  -H "Authorization: Bearer clapi_your_api_key"

A 401 means the key is missing, malformed, expired or revoked.

Security best practices

  • Never commit clapi_ keys to source control.
  • Store keys in a secret manager (AWS Secrets Manager, Doppler, 1Password, Vault and similar).
  • Use a Name and an Expiry to make keys easy to rotate and audit.
  • If a key leaks, revoke it at once (Revoke in the app, or POST /api/v1/api-keys/{key_id}/revoke).
  • Watch the request count and View requests log for unexpected use.
  • Use one key per environment (dev, staging, production), and never share keys across systems.

Common authentication errors

StatusCodeCauseFix
401AUTHENTICATION_INVALIDMissing, malformed, expired or revoked key ("Invalid or expired API key", or "Not authenticated" when the header is missing)Check the header format and the key's status under Your API keys
403insufficient_scopeCredential lacks the endpoint's scopeCheck /whoami; if a scope you need is missing, contact support@cleanlist.ai
403PERMISSION_DENIED (details.error: feature_gated)Plan has no API access (key creation or listing)Move to Pro or Enterprise under Settings → Plans & billing
403PERMISSION_DENIEDKey used while a trial is active ("Public API access is not included during the trial...")Use the app or the MCP during the trial, or start a paid plan
429RATE_LIMITEDRate limit hitBack off using Retry-After; see Errors

Authentication, plan and rate-limit errors use the generic error shape (code + message); see Errors & Rate Limits.

Learn more