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.
start from companies with live roles instead of a cold list
validate before the sequencer, not after the bounce
dial the direct line and log the call automatically
file the contact detail on the record
A skill is a whole pipeline your agent already knows. These ones call Lusha as part of the run.
TheirStack flags 50+ open roles. Poach displaced talent via Apollo + Lusha into Lemlist.
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 →WHAT/guardrails here; HOW in reference/endpoints.md + lib/lusha.py. OpenAPI cached at reference/docs/lusha/raw/v2_openapi.md.
https://api.lusha.comapi_key (Lusha → API settings). Env LUSHA_API_KEY; never hardcode.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.search_contacts/search_companies (cheap, returns requestId + ids) → enrich_contact_emails/enrich_contact_phones/enrich_companies (reveal the chosen ids, consumes credits).get_usage() is a safe read-only pilot.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-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.
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.http:// or www. in domain values.emails[].email (or top-level email); phone at phones[].number - check the array first, top-level phone isn't always populated.*_email method reveals emails, a *_phone method phones; intent data comes from the *_signals methods.seniority, companySize (e.g. "201-500"), department, etc.; returns { contacts, pagination:{ page, pageSize, total, totalPages } }.lib/lusha.pyImport: 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 tocontact_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 filtercontact_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 idsenrich_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 (wrapsenrich_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 (wrapsget_usage() -> dict — GET /v3/account/usage — credits (total/used/remaining), rate limits, plan, and per-actionprospecting_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 contactssearch_companies(companies: list[dict], **opts) -> dict — POST /v3/companies/search — preview companies by id/name/domain (max 100). Returns hassearch_contacts(contacts: list[dict], *, filters: dict | None = None, **opts) -> dict — POST /v3/contacts/search — preview contacts by identifier (id / linkedinUrl / email /