← all tools
Enrichment

Lusha

Verified emails and direct dials for people you already know you want.

Cost per call2.8–13.9 cr
Your own keyoptional
CategoryEnrichment

Lusha turns a name and a company into a way to reach them. Its strength is the direct dial, the mobile number that actually gets answered. Searching is cheap, revealing costs credits, so hyreflow shortlists before it spends.

Lusha works on a filtered list. Something upstream decides who matters, Lusha works out how to reach them.

things you can ask for

  • Get a direct dial for this hiring manager, then log the call through Aircall
  • Enrich this list of 50 with verified emails and validate them with Enrichley
  • Find the head of talent at these twelve companies from TheirStack
  • Take this longlist csv and add mobiles for the ten I want to call first

works well with Lusha

  • TheirStack

    start from companies with live roles instead of a cold list

  • Enrichley

    validate before the sequencer, not after the bounce

  • Aircall

    dial the direct line and log the call automatically

  • Bullhorn

    file the contact detail on the record

Skills that use Lusha

A skill is a whole pipeline your agent already knows. These ones call Lusha as part of the run.

signal5 steps

Hiring sprint → competitor poach

TheirStack flags 50+ open roles. Poach displaced talent via Apollo + Lusha into Lemlist.

coming soon

What your agent reads before it touches Lusha

Every tool ships with a written playbook, and the agent loads it before the first call. Auth, rate limits, what each call costs, which actions need your approval, and the mistakes worth avoiding. It is the difference between an agent that knows the tool and one that guesses at it.

Read the Lusha docs →
Show the raw playbookwritten for the agent

WHAT/guardrails here; HOW in reference/endpoints.md + lib/lusha.py. OpenAPI cached at reference/docs/lusha/raw/v2_openapi.md.

Auth & config

  • Base URL: https://api.lusha.com
  • Auth: header api_key (Lusha → API settings). Env LUSHA_API_KEY; never hardcode.

Two modes

  • Enrichment (known target): enrich_person_email / enrich_person_phone / enrich_company (single) or enrich_companies (bulk). Consumes credits on reveal, at the price of the field the method reveals.
  • Prospecting (search → enrich): search_contacts/search_companies (cheap, returns requestId + ids) → enrich_contact_emails/enrich_contact_phones/enrich_companies (reveal the chosen ids, consumes credits).

Guardrails

  • Credits: reveal/enrich consumes credits; search is cheap. A phone reveal costs 5x an email one, and the method fixes which you get — there's no field argument to get it wrong. Pilot one, confirm, then bulk. get_usage() is a safe read-only pilot.
  • V1 person API is deprecated — this adapter uses v2 only.

Handoff

Enrichment/prospecting layer: search or take a shortlist → Lusha for verified emails/direct-dials → ATS (recruit-crm) / sequencer. Enrich late, on the filtered set.

Field notes (production experience)

Field-shape note: these are vendor-native operational notes. The client returns the raw vendor JSON and uses the method names in this file — read field shapes accordingly (no normalized-wrapper / result.data. prefix).

Strength: direct dials (mobile + desk), not just HQ. Coverage global, strongest NA + Europe. ~$0.05/credit.

  • Person input: LinkedIn URL (best - must contain linkedin.com/in/; Sales Navigator/Recruiter URLs fail), OR email, OR first_name+last_name+(company_name or company_domain). Name+company is the minimum combo - don't pass company_name alone.
  • Company enrich: domain/name/Lusha ID; don't include http:// or www. in domain values.
  • Output: email at emails[].email (or top-level email); phone at phones[].number - check the array first, top-level phone isn't always populated.
  • Fields: an *_email method reveals emails, a *_phone method phones; intent data comes from the *_signals methods.
  • Search: filters seniority, companySize (e.g. "201-500"), department, etc.; returns { contacts, pagination:{ page, pageSize, total, totalPages } }.

Callable surface — lib/lusha.py

Import: from lib.lusha import Lusha → instantiate Lusha() (reads key from env). Base: https://api.lusha.com. Generic passthrough: request(method, path, *, params, json).

  • company_filter_types() -> dict — GET /v3/companies/prospecting/filters — list company filter types.
  • company_filter_values(filter_type: str, *, query: str | None = None) -> dict — GET /v3/companies/prospecting/filters/{type} — valid values (sizes/revenues/sics/naics/
  • company_lookalikes(payload: dict) -> dict — POST /v3/companies/lookalike — AI lookalikes from 5–100 seed companies (domains/linkedin).
  • company_signals(payload: dict) -> dict — POST /v3/companies/signals — hiring/headcount/web-traffic/IT-spend/news signals for up to
  • contact_filter_types() -> dict — GET /v3/contacts/prospecting/filters — list contact filter types (+ whether query required).
  • contact_filter_values(filter_type: str, *, query: str | None = None) -> dict — GET /v3/contacts/prospecting/filters/{type} — valid values for a contact filter
  • contact_lookalikes(payload: dict) -> dict — POST /v3/contacts/lookalike — AI lookalikes from 5–100 seed contacts (linkedinUrl/email/
  • contact_signals(payload: dict) -> dict — POST /v3/contacts/signals — job-change/promotion events for up to 100 contact ids
  • enrich_companies(ids: list[str], **opts) -> dict — POST /v3/companies/enrich — reveal firmographics (size/revenue/industry/tech/funding/
  • enrich_company(*, domain: str | None = None, name: str | None = None) -> dict — Convenience: search-and-enrich a SINGLE company by domain or name (no http/www in domain).
  • enrich_contact_emails(ids: list[str]) -> dict — POST /v3/contacts/enrich — reveal the EMAILS of contacts already found via search. ids =
  • enrich_contact_phones(ids: list[str]) -> dict — POST /v3/contacts/enrich — reveal the PHONES of contacts already found via search. ids =
  • enrich_person_email(*, linkedin_url: str | None = None, email: str | None = None, first_name: str | None = None, last_name: str | None = None, company_name: str | None = None, company_domain: str | None = None) -> dict — Convenience: search-and-enrich the EMAIL of a SINGLE contact (wraps
  • enrich_person_phone(*, linkedin_url: str | None = None, email: str | None = None, first_name: str | None = None, last_name: str | None = None, company_name: str | None = None, company_domain: str | None = None) -> dict — Convenience: search-and-enrich the PHONE of a SINGLE contact (wraps
  • get_usage() -> dict — GET /v3/account/usage — credits (total/used/remaining), rate limits, plan, and per-action
  • prospecting_companies(payload: dict) -> dict — POST /v3/companies/prospecting — filter-based company search (size/revenue/industry/tech/
  • prospecting_contacts(payload: dict) -> dict — POST /v3/contacts/prospecting — filter-based ICP search (title/seniority/department/
  • search_and_enrich_companies(companies: list[dict], **opts) -> dict — POST /v3/companies/search-and-enrich — find + reveal companies in one call (id/name/domain).
  • search_and_enrich_contact_emails(contacts: list[dict], *, options: dict | None = None) -> dict — POST /v3/contacts/search-and-enrich — find + reveal EMAILS in ONE call. contacts:
  • search_and_enrich_contact_phones(contacts: list[dict], *, options: dict | None = None) -> dict — POST /v3/contacts/search-and-enrich — find + reveal PHONES in ONE call. Same contacts
  • search_companies(companies: list[dict], **opts) -> dict — POST /v3/companies/search — preview companies by id/name/domain (max 100). Returns has
  • search_contacts(contacts: list[dict], *, filters: dict | None = None, **opts) -> dict — POST /v3/contacts/search — preview contacts by identifier (id / linkedinUrl / email /