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: pip install cleanlist-ai, then Cleanlist(access_token="clapi_…"), then call typed methods like cl.workspace.whoami() and cl.enrichment.enrich_person(...). Sync and async clients included.
SDKs
Official, typed client libraries for API v2, generated from the v2 OpenAPI schema. Available for Python, TypeScript/JavaScript and Java:
| Language | Package | Source |
|---|---|---|
| Python | cleanlist-ai (opens in a new tab) (sync + async) | python-client (opens in a new tab) |
| TypeScript / JS | @cleanlist-ai/sdk (opens in a new tab) (Node + browser) | typescript-client (opens in a new tab) |
| Java | ai.cleanlist:cleanlist-java (opens in a new tab) (OkHttp/Gson) | java-client (opens in a new tab) |
This page walks through the Python SDK end to end; TypeScript and Java mirror it with the same Cleanlist client and method names (see each repo's README).
Prefer raw HTTP? Every page in this section shows cURL. The SDKs are optional sugar over the same https://api.cleanlist.ai/api/v2 surface.
What the SDKs cover: workspace (identity, credits, estimates, API keys, usage), lead lists, enrichment, smart agents and export. People Finder, company search and CRM and sequencer sync are not in the SDKs yet; call them over HTTP with the same key.
Install
pip install cleanlist-aiPython 3.8+. Pulls in both the sync (urllib3) and async (aiohttp) runtimes, so both clients work out of the box.
Authenticate
Create a key in the app under Settings → API keys → Create API key (it starts with clapi_ and is shown once), then either pass it explicitly or export it and let the SDK pick it up:
export CLEANLIST_API_KEY="clapi_your_actual_key"from cleanlist_ai import Cleanlist
cl = Cleanlist() # reads CLEANLIST_API_KEYYour first calls
whoami and credits/balance are free, a good end-to-end sanity check:
from cleanlist_ai import Cleanlist
with Cleanlist() as cl:
me = cl.workspace.whoami()
print(f"Org: {me.organization_name} | tier: {me.tier}")
print(f"Credits: {cl.workspace.credits_balance().credits}")The client exposes five resource groups, mirroring the API sections:
| Group | Accessor | Covers |
|---|---|---|
| Workspace | cl.workspace | identity, credits, estimates, API keys, usage |
| Lead Lists | cl.lead_lists | create and manage lists and leads |
| Enrichment | cl.enrichment | person, company, by-task and bulk enrichment + status |
| Smart Agents | cl.smart_agents | run AI columns and read results |
| Export | cl.export | CSV (signed URL) and JSON export |
Enrich a person and poll
Enrichment is asynchronous: the call starts a workflow and returns a workflow_id; poll enrichment_status until it settles.
import time
from cleanlist_ai import Cleanlist
from cleanlist_ai.models import CreateListRequest, EnrichPersonRequest
TERMINAL = {"completed", "completed_with_errors", "failed", "canceled", "terminated"}
with Cleanlist() as cl:
lst = cl.lead_lists.create_list(CreateListRequest(name="SDK demo"))
job = cl.enrichment.enrich_person(
EnrichPersonRequest(
lead_list_id=lst.list_id,
first_name="Ada",
last_name="Lovelace",
company_name="Analytical Engines",
)
)
print("workflow:", job.workflow_id, "| reserved:", job.credits_reserved)
while True:
status = cl.enrichment.enrichment_status(job.workflow_id)
if status.status in TERMINAL:
break
time.sleep(3)
print("charged:", status.credits_charged, "refunded:", status.credits_refunded)Bulk enrichment (estimate, quote, run)
Bulk operations must be priced with a quote first. Call credits_estimate to get a signed, single-use quote_id, then pass it to enrich_list:
from cleanlist_ai.models import EstimateCostRequest, EnrichListRequest
quote = cl.workspace.credits_estimate(
EstimateCostRequest(tool="enrich_list", list_id=lst.list_id, scope="full")
)
print(f"cost={quote.estimated_cost} sufficient={quote.sufficient}")
if quote.sufficient:
run = cl.enrichment.enrich_list(
EnrichListRequest(list_id=lst.list_id, scope="full", quote_id=quote.quote_id)
)
print("bulk workflow:", run.workflow_id)Scopes: partial (email + LinkedIn + title + company, 1 credit) · phone-only (10 credits) · full (email and phone, 11 credits). Pricing is pay-for-results: the reservation is a cap and the unused part is refunded.
Async
The async client lives under cleanlist_ai.aio with identical method names. Use it as an async context manager and await each call; asyncio.gather fires independent requests concurrently:
import asyncio
from cleanlist_ai.aio import Cleanlist
async def main():
async with Cleanlist() as cl:
me, balance = await asyncio.gather(
cl.workspace.whoami(),
cl.workspace.credits_balance(),
)
print(me.organization_name, balance.credits)
asyncio.run(main())Error handling
Non-2xx responses raise a typed ApiException subclass carrying status, reason and the parsed body:
from cleanlist_ai import ApiException
from cleanlist_ai.exceptions import NotFoundException, UnauthorizedException
try:
cl.lead_lists.get_list("does-not-exist")
except NotFoundException:
print("no such list")
except UnauthorizedException:
print("bad or missing API key")
except ApiException as e:
print(f"API error {e.status}: {e.body}")The body holds the error envelope; branch on its code. See Errors & Rate Limits for every code.
Other languages
Official SDKs with the same Cleanlist facade and method names:
- TypeScript / JavaScript:
@cleanlist-ai/sdk(opens in a new tab) (npm install @cleanlist-ai/sdk; Node + browser, ESM + CJS) - Java:
ai.cleanlist:cleanlist-java(opens in a new tab) (Maven or Gradle; OkHttp + Gson)
Need another language? Generate a client from the OpenAPI spec with openapi-generator (opens in a new tab). The served spec leaves out POST /credits/estimate, company search and sync, so add those calls by hand.
Next steps
- Runnable example scripts (opens in a new tab) (person and bulk enrichment, smart agents, export, async).
- Full endpoint and model reference in the SDK README (opens in a new tab).
- Try requests live in the interactive API docs (opens in a new tab) (Swagger UI, opens on v2).
- The underlying OpenAPI spec if you'd rather generate a client in another language.