docs
Dashboard

More / SDKs

SDKs.

Official TypeScript and Python clients for the REST API. Typed responses with the same field names as the API, and automatic retries on 429 and 5xx.

Install

TypeScriptPython
npm install @leadsetsai/sdk

The TypeScript SDK has no runtime dependencies and runs on Node.js 18+, Bun, Deno and edge runtimes. The Python SDK has a sync Leadsets and an async AsyncLeadsets client.

Create a client

Pass your API key, or leave it out and the client reads LEADSETS_API_KEY (or LEADSETS_KEY) from the environment. Never hard-code it. See Authentication.

TypeScriptPython
import { Leadsets } from "@leadsetsai/sdk";

const ls = new Leadsets(process.env.LEADSETS_KEY);

const res = await ls.people.search({
  function: ["revenue"],
  seniority: ["vp", "head"],
  country: "US",
  limit: 10,
});
console.log(res.items[0].match_reasons, res.usage);

Methods

One method per REST route. Arguments are the route's body parameters, with the same names in both languages.

MethodREST routeMCP tool
people.searchPOST /v1/people/search. Also searchAll / search_all and searchPages / search_pages.search_people
companies.searchPOST /v1/companies/search. Also searchAll / search_all and searchPages / search_pages.search_companies
people.enrichPOST /v1/people/enrichenrich_person
companies.enrichPOST /v1/companies/enrichenrich_company
emails.verifyPOST /v1/emails/verify. Takes one email, a list, or { emails }.verify_email
scrape.companyPOST /v1/scrape/companyscrape_company
scrape.jobsPOST /v1/scrape/jobsscrape_jobs
jobs.getGET /v1/jobs/{id}. Throws NotFoundError until async jobs ship.Not available yet
usage.getGET /v1/usageget_usage

Errors and retries

Every API error is thrown as a typed error with the API's status, type, message and request_id. Branch on the class:

ClassStatustype
InvalidRequestError400invalid_request (param names the field)
AuthenticationError401unauthorized
FreeRecordsExhaustedError402free_records_exhausted
ScrapeUnitsExhaustedError402scrape_units_exhausted
PermissionDeniedError403forbidden, with the upgrade URL when a plan change would allow the call
NotFoundError404not_found
IdempotencyConflictError409idempotency_conflict
RateLimitError429window_full, weekly_limit, too_many_concurrent
InternalServerError5xxinternal_error
ServiceUnavailableError503unavailable
APIConnectionErrornoneNo answer from the API.
APITimeoutErrornoneThe request timed out.

Both payment errors extend PaymentRequiredError, every HTTP error extends APIError, and everything extends LeadsetsError.

TypeScriptPython
import { APIError, FreeRecordsExhaustedError, NotFoundError, RateLimitError } from "@leadsetsai/sdk";

try {
  await ls.people.enrich({ email: "maya@northwind.io" });
} catch (err) {
  if (err instanceof NotFoundError) {
    // nothing to enrich; nothing was counted
  } else if (err instanceof FreeRecordsExhaustedError) {
    askToUpgrade();
  } else if (err instanceof RateLimitError) {
    console.log(err.type, err.meter, err.resetsAt, err.retryAfter);
  } else if (err instanceof APIError) {
    console.log(err.status, err.type, err.message, err.requestId);
  } else {
    throw err;
  }
}

Retries. The SDKs retry 429, 5xx, timeouts and network errors up to maxRetries / max_retries times (default 2). They wait for Retry-After when the API sends one, and otherwise back off exponentially with jitter. A Retry-After longer than maxRetryDelay / max_retry_delay (default 60 seconds), such as a full weekly window, is thrown at once so a run doesn't hang. 400, 401, 402, 403, 404 and 409 are never retried. Every error type is in Errors and retries.

Idempotency keys

The SDKs send an Idempotency-Key with every paid call (every POST) and reuse it across their own retries, so a retry never counts records twice. Pass your own key to make a call safe to repeat across processes, for example one key per CRM row. See Idempotency.

TypeScriptPython
await ls.people.enrich({ email: row.email }, { idempotencyKey: `crm-${row.id}` });

Pagination

searchAll / search_all follows next_cursor for you and yields one record at a time. Each record returned counts, so cap a run with maxItems / max_items; the last page asks only for what is left. searchPages / search_pages yields whole pages when you want to read usage as you go. See Pagination.

TypeScriptPython
for await (const company of ls.companies.searchAll({ industry: "saas" }, { maxItems: 250 })) {
  save(company);
}

Without an SDK

Every SDK method is one HTTPS request with a JSON body, so any HTTP client works: send Authorization: Bearer and Content-Type: application/json. Each reference page has a cURL example, starting with POST /v1/people/search. For agents, use MCP instead: no code at all.

Previous
← get_usage
Next
Pagination →

Was this page useful? Tell us