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

Enrichment API

Public enrichment is asynchronous. You submit contacts in bulk, receive identifiers, then poll or use webhooks for completion.

Start Bulk Enrichment

POST /api/v1/public/enrich/bulk

curl -X POST https://api.cleanlist.ai/api/v1/public/enrich/bulk \
  -H "Authorization: Bearer clapi_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "enrichment_type": "partial",
    "contacts": [
      {
        "linkedin_url": "https://linkedin.com/in/johndoe",
        "first_name": "John",
        "last_name": "Doe",
        "company_domain": "acme.com"
      },
      {
        "first_name": "Jane",
        "last_name": "Smith",
        "company_name": "Example Inc"
      }
    ]
  }'

Request Body

FieldTypeRequiredDescription
enrichment_typestringYesnone, partial, phone_only, or full
contactsarrayYesUp to 250 contacts per request

Contact Input Requirements

Each contact must include one of these two input patterns:

  • linkedin_url
  • first_name + last_name + (company_name or company_domain)

If contacts exceeds 250, the API returns 400 with a validation error.

LinkedIn-Only Payload Example

{
  "enrichment_type": "full",
  "contacts": [
    {
      "linkedin_url": "https://www.linkedin.com/in/levon-adamyan"
    }
  ]
}

Enrichment Types

TypeBehaviorTypical use
noneNormalize and prepare records onlyStage/import contacts without appending new contact data
partialEmail enrichmentFind and verify business emails
phone_onlyPhone enrichmentFind direct phone numbers
fullEmail + phone enrichmentMax contact coverage

Response

{
  "workflow_id": "public_bulk_3d1f9a67-cc44-4a06-b8d4-7fe7a8f31a01",
  "status": "processing",
  "message": "Bulk enrichment started",
  "task_ids": [
    "6aab5e77-f5c0-4233-8f8e-a0e1a9baf18c",
    "d2c1e5f2-2d83-47f2-a620-4d27906b9f65"
  ],
  "total_contacts": 2
}
⚠️

The API validates credit availability before processing. If insufficient, the request can return 402.

Poll Enrichment Status

GET /api/v1/public/enrich/status

Provide exactly one query parameter:

  • workflow_id for aggregate workflow progress
  • task_id for single task status

Workflow Status Example

curl "https://api.cleanlist.ai/api/v1/public/enrich/status?workflow_id=public_bulk_3d1f9a67-cc44-4a06-b8d4-7fe7a8f31a01" \
  -H "Authorization: Bearer clapi_your_api_key"

Task Status Example

curl "https://api.cleanlist.ai/api/v1/public/enrich/status?task_id=6aab5e77-f5c0-4233-8f8e-a0e1a9baf18c" \
  -H "Authorization: Bearer clapi_your_api_key"

Response (task):

{
  "task": {
    "task_id": "6aab5e77-f5c0-4233-8f8e-a0e1a9baf18c",
    "status": "completed",
    "message": "Enrichment completed"
  }
}

Webhooks

Register Webhook

POST /api/v1/public/webhooks

curl -X POST https://api.cleanlist.ai/api/v1/public/webhooks \
  -H "Authorization: Bearer clapi_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://example.com/cleanlist-webhooks",
    "name": "Production Enrichment Webhook",
    "event_type": "enrichment.completed"
  }'

List Delivery Attempts

GET /api/v1/public/webhooks/deliveries?workflow_id=<workflow_id>

curl "https://api.cleanlist.ai/api/v1/public/webhooks/deliveries?workflow_id=public_bulk_3d1f9a67-cc44-4a06-b8d4-7fe7a8f31a01" \
  -H "Authorization: Bearer clapi_your_api_key"

Polling Best Practice

Use backoff when polling status:

import time
import requests
 
 
def poll_workflow(workflow_id, api_key, max_wait=300):
    url = "https://api.cleanlist.ai/api/v1/public/enrich/status"
    headers = {"Authorization": f"Bearer {api_key}"}
 
    delay = 1
    elapsed = 0
 
    while elapsed < max_wait:
        response = requests.get(url, headers=headers, params={"workflow_id": workflow_id})
        data = response.json()
 
        workflow = data.get("workflow", {})
        if workflow.get("status") in ("completed", "failed"):
            return workflow
 
        time.sleep(delay)
        elapsed += delay
        delay = min(delay * 2, 10)
 
    raise TimeoutError(f"Workflow {workflow_id} did not complete within {max_wait}s")

Rate Limits

Public endpoints documented on this page are limited to 60 requests per minute per organization in a fixed 60-second window.

If you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header.

Example 429 response:

HTTP/1.1 429 Too Many Requests
Retry-After: 17
Content-Type: application/json
 
{
  "detail": "Rate limit exceeded. Public API is limited to 60 requests per minute."
}