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.
| Endpoint | Method | Scope |
|---|---|---|
| Create | POST /lead-lists | lists:write |
| List | GET /lead-lists | lists:read |
| Get one | GET /lead-lists/{list_id} | lists:read |
| List leads | GET /lead-lists/{list_id}/leads | lists:read |
| Add leads | POST /lead-lists/{list_id}/leads | lists:write |
| Remove leads | DELETE /lead-lists/{list_id}/leads | lists:write |
| CSV import | POST /lead-lists/{list_id}/csv-import | lists:write (+ enrich:write if dispatching) |
Create
POST /lead-lists
Creates a private list in your workspace. Returns 201.
Request body
| Field | Type | Notes |
|---|---|---|
name | string | Required. 1–200 chars |
description | string | Optional |
folder_id | string | Optional 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 param | Default | Range |
|---|---|---|
limit | 50 | 1–100 |
cursor | — | opaque cursor from the previous page |
folder_id | — | filter 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 param | Default | Range |
|---|---|---|
limit | 100 | 1–500 |
cursor | — | opaque cursor |
Lead row schema
| Field | Type | Notes |
|---|---|---|
lead_id | string | Stable lead id |
first_name | string | |
last_name | string | |
full_name | string | Pre-composed display name |
email | string | Primary email (null until enriched) |
phone | string | Primary verified phone (null until phone-enriched) |
title | string | Job title |
company | string | Company 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
| Field | Type | Notes |
|---|---|---|
csv_content_base64 | string | Required. Base64-encoded UTF-8 CSV |
column_mapping | object | Required. { 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_enrichment | boolean | Default true. Kick off bulk enrichment after import |
enrichment_type | enum | Required when dispatching: partial, phone_only, full |
quote_id | string | Optional signed quote (tool: "bulk_import_csv") for large dispatches |
idempotency_key | string | Optional. 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.