New: API Reference docs are live — integrate Cleanlist enrichment into your apps. View API docs →
MCP API
Authentication

TL;DR: Every Public API request uses Authorization: Bearer clapi_... (or a Clerk JWT). On top of the token, each endpoint requires an OAuth scope (e.g. enrich:write); a missing scope returns 403 insufficient_scope. Generate keys in the portal or via /api/v1/api-keys. Up to 10 active keys per user, and key creation requires the API_ACCESS feature.

Authentication

The Cleanlist Public API accepts two credential types:

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

OAuth scopes

Authentication answers "who are you?"; scopes answer "what may you do?". Every /api/v1/public/* endpoint asserts a single scope before running. If the authenticating credential doesn't carry that scope, the request fails with 403 and code insufficient_scope.

ScopeGrants
people:readPeople search + people filters
companies:readCompany search, company filters, similar companies
lists:readRead lead lists and their leads
lists:writeCreate lists, add/remove leads, CSV import
enrich:readPoll enrichment status
enrich:writeRun person/company/by-task/bulk enrichment
credits:readCredit balance + cost estimates
export:readCSV signed URLs + JSON export
sync:writeSync to CRM / sequencer
smart_agents:readList smart-agent runs + results
smart_agents:writeRun a smart agent
admin:api_keysList keys + usage reports via the public API

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": "Re-issue the API key with the required scope, or sign in with an identity that has it. See /api/v1/public/whoami.",
    "retryable": false,
    "docs_url": "https://docs.cleanlist.ai/errors/insufficient-scope",
    "request_id": "req_...",
    "details": { "required_scope": "enrich:write", "granted_scopes": ["people:read", "lists:read"] }
  }
}

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

Key format

PropertyValue
Prefixclapi_
Scope of ownershipUser (and the user's organization)
Secret body48 random URL-safe bytes
VisibilityShown once at creation; afterward only the prefix and last 4 are displayed
Limit10 active keys per user (MAX_API_KEYS_PER_USER)

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 generate a new one.

Generating a key

The fastest path is the portal: Settings → API Keys → Generate Key, then copy the full key immediately.

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

Key creation is gated by the API_ACCESS feature. If your plan doesn't include it, POST /api/v1/api-keys returns a 403 feature-gate error. Listing keys is also gated; revoking is not.

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, creation returns 400 with a "Maximum active API keys reached" message — revoke one first.

Listing your keys

GET /api/v1/api-keys

Lists active and inactive keys (Clerk JWT, gated by API_ACCESS). 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 public, clapi_-authenticated GET /api/v1/public/api-keys (scope admin:api_keys) that returns a lighter shape (key_id, key_prefix, key_last4, is_active).

Revoking keys

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

Revoke a single key by its UUID (not feature-gated):

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 cannot be reactivated — generate a new one.

Inspecting key usage

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
limit1001500
[
  {
    "id": "log-uuid",
    "method": "POST",
    "path": "/api/v1/public/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 Public API call uses the same Bearer header:

curl https://api.cleanlist.ai/api/v1/public/credits/balance \
  -H "Authorization: Bearer clapi_your_api_key"

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/v1/public/whoami \
  -H "Authorization: Bearer clapi_your_api_key"

A 401 response 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, etc.).
  • Use name and expires_at to make keys easy to rotate and audit.
  • If a key leaks, immediately call POST /api/v1/api-keys/{key_id}/revoke.
  • Monitor request_count and last_used_at to detect unexpected usage.
  • Prefer one key per environment (dev / staging / production) — never share keys across systems.

Common authentication errors

StatusCodeCauseFix
401invalid_tokenMissing/malformed token, or revoked/expired keyConfirm header format and the key's status
403insufficient_scopeCredential lacks the endpoint's required scopeRe-issue the key with the scope; check /whoami
403feature gatePlan lacks API_ACCESS (key creation)Upgrade your plan in billing
429rate_limitedRate limit hitBack off using Retry-After; see Errors

Learn more