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

TL;DR: Full lead-list CRUD. Create a list, add leads by id (from a search or an owned list), read leads with phone and full_name, remove up to 100 at a time, or import a CSV with optional bulk enrichment. Lists carry an owner_name and a visibility of private / organization / shared.

Lead Lists

Lead lists are how you organize contacts in Cleanlist. The Public API exposes full CRUD plus CSV import.

EndpointMethodScope
CreatePOST /lead-listslists:write
ListGET /lead-listslists:read
Get oneGET /lead-lists/{list_id}lists:read
List leadsGET /lead-lists/{list_id}/leadslists:read
Add leadsPOST /lead-lists/{list_id}/leadslists:write
Remove leadsDELETE /lead-lists/{list_id}/leadslists:write
CSV importPOST /lead-lists/{list_id}/csv-importlists:write (+ enrich:write if dispatching)

Create

POST /lead-lists

Creates a private list in your workspace. Returns 201.

Request body

FieldTypeNotes
namestringRequired. 1–200 chars
descriptionstringOptional
folder_idstringOptional folder UUID you own
curl -X POST https://api.cleanlist.ai/api/v1/public/lead-lists \
  -H "Authorization: Bearer clapi_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"name": "Q3 Outbound — Fintech VPs"}'
{
  "list_id": "9c1f...",
  "name": "Q3 Outbound — Fintech VPs",
  "description": null,
  "lead_count": 0,
  "enriched_count": 0,
  "folder_id": null,
  "created_at": "2026-06-03T10:00:00Z",
  "timestamp_ms": 1717430400000
}

List

GET /lead-lists

Paginates the lists you can access — owned, organization-wide, and shared.

Query paramDefaultRange
limit501100
cursoropaque cursor from the previous page
folder_idfilter to one folder
{
  "lists": [
    {
      "list_id": "9c1f...",
      "name": "Q3 Outbound — Fintech VPs",
      "lead_count": 128,
      "folder_id": null,
      "created_at": "2026-06-03T10:00:00Z",
      "owner_name": "Victor Paraschiv",
      "visibility": "private"
    }
  ],
  "total": 14,
  "cursor": "50",
  "timestamp_ms": 1717430400000
}

visibility is one of private, organization, or shared. owner_name is the list creator's display name (may be null).

Get one

GET /lead-lists/{list_id}

{
  "list_id": "9c1f...",
  "name": "Q3 Outbound — Fintech VPs",
  "description": null,
  "lead_count": 128,
  "enriched_count": 96,
  "folder_id": null,
  "created_at": "2026-06-03T10:00:00Z",
  "timestamp_ms": 1717430400000
}

List leads

GET /lead-lists/{list_id}/leads

Paginates the leads in a list.

Query paramDefaultRange
limit1001500
cursoropaque cursor

Lead row schema

FieldTypeNotes
lead_idstringStable lead id
first_namestring
last_namestring
full_namestringPre-composed display name
emailstringPrimary email (null until enriched)
phonestringPrimary verified phone (null until phone-enriched)
titlestringJob title
companystringCompany name
{
  "list_id": "9c1f...",
  "leads": [
    {
      "lead_id": "lead_abc",
      "first_name": "Jane",
      "last_name": "Doe",
      "full_name": "Jane Doe",
      "email": "jane@acme.com",
      "phone": "+14155550123",
      "title": "VP Sales",
      "company": "Acme"
    }
  ],
  "total": 128,
  "cursor": "100",
  "task_id": "cl-task_def...",
  "timestamp_ms": 1717430400000
}

The response carries a task_id — a cohort handle for these leads. Pass it to POST /enrichment/by-task to enrich the page without re-listing every lead_id.

Add leads

POST /lead-lists/{list_id}/leads

Adds leads by explicit lead_ids (1–1000). Filter-based add is intentionally rejected — translate a search into lead_ids first.

curl -X POST https://api.cleanlist.ai/api/v1/public/lead-lists/9c1f.../leads \
  -H "Authorization: Bearer clapi_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"lead_ids": ["lead_abc", "lead_def"]}'
{ "list_id": "9c1f...", "added": 2, "skipped_duplicates": 0 }

Each lead_id must be reachable in your workspace — either it already belongs to a list your org owns, or it appeared in a recent search_people / list-leads response. Otherwise you get 404 not_found. Re-run the search if the cohort expired.

Remove leads

DELETE /lead-lists/{list_id}/leads

Removes leads from a list by lead_ids (max 100). This removes membership only — the lead stays in your workspace and in any other lists. Unknown ids are silently ignored.

curl -X DELETE https://api.cleanlist.ai/api/v1/public/lead-lists/9c1f.../leads \
  -H "Authorization: Bearer clapi_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"lead_ids": ["lead_abc"]}'
{ "list_id": "9c1f...", "removed": 1 }

CSV import

POST /lead-lists/{list_id}/csv-import

Imports a CSV into a list and optionally dispatches bulk enrichment in the same call. Returns 201. Requires lists:write; if dispatch_enrichment is true, also requires enrich:write.

The raw CSV body is capped at ~150 KB / ~600 rows — past that the endpoint returns 413. Chunk larger files upstream. Import itself is free; the enrichment dispatch costs per-lead (see Enrichment Types).

Request body

FieldTypeNotes
csv_content_base64stringRequired. Base64-encoded UTF-8 CSV
column_mappingobjectRequired. { canonical_field: csv_header }. Canonical fields: first_name, last_name, full_name, email, phone, linkedin_url, company_name, company_domain, job_title, city, state, country
dispatch_enrichmentbooleanDefault true. Kick off bulk enrichment after import
enrichment_typeenumRequired when dispatching: partial, phone_only, full
quote_idstringOptional signed quote (tool: "bulk_import_csv") for large dispatches
idempotency_keystringOptional. Replays the cached response for 24h (org-wide); no re-import, no re-charge

Response

{
  "list_id": "9c1f...",
  "rows_parsed": 500,
  "rows_imported": 488,
  "rows_skipped": 12,
  "skip_reasons": { "missing_required": 8, "duplicate_email": 4 },
  "sample_skip_rows": [3, 17, 42],
  "workflow_id": "bulk-enrich-9a1b...",
  "enrichment_status_url": "/api/v1/public/enrichment/status/bulk-enrich-9a1b...",
  "idempotency_replayed": false,
  "timestamp_ms": 1717430400000
}
B64=$(base64 -i leads.csv)
curl -X POST https://api.cleanlist.ai/api/v1/public/lead-lists/9c1f.../csv-import \
  -H "Authorization: Bearer clapi_your_api_key" -H "Content-Type: application/json" \
  -d "{
    \"csv_content_base64\": \"$B64\",
    \"column_mapping\": {\"first_name\": \"First\", \"last_name\": \"Last\", \"company_domain\": \"Domain\"},
    \"dispatch_enrichment\": true,
    \"enrichment_type\": \"partial\"
  }"

After a dispatching import, poll the returned enrichment_status_url — see Enrichment status.

Learn more