Documentation
The hosted server at https://api.leadocean.io/mcp gives any MCP client the same data the REST API does — over OAuth 2.1, so there is no key to paste and nothing to leak into a chat log.
The server speaks Streamable HTTP with OAuth 2.1. You add one URL in your client, it sends you to LeadOcean to sign in, you approve, and the tools appear. Your assistant never sees an API key.
A connection is tied to your account, and usage counts against the same records as the REST API — one balance, however you call it. You can see and revoke every connected app from API keys → Connected apps in your account.
https://api.leadocean.io/mcp — that is the whole configuration. Everything below is just where each app wants you to paste it.| Client | How to add it |
|---|---|
| Claude | Settings → Connectors → Add custom connector → paste the URL → Connect. |
| ChatGPT | Settings → Security and login → Developer mode, then Connectors → Add → paste the URL, authentication OAuth. |
| Claude Code | claude mcp add --transport http leadocean https://api.leadocean.io/mcp then claude mcp login leadocean. |
| Cursor | mcp.json: {"mcpServers":{"leadocean":{"url":"https://api.leadocean.io/mcp"}}} |
| Codex | config.toml: [mcp_servers.leadocean] url = "https://api.leadocean.io/mcp" then codex mcp login leadocean. |
| VS Code | .vscode/mcp.json: {"servers":{"leadocean":{"type":"http","url":"https://api.leadocean.io/mcp"}}} |
| Windsurf | mcp_config.json with serverUrl set to the URL above. |
| Gemini CLI | settings.json with httpUrl set to the URL above, then /mcp auth leadocean. |
Any client that supports remote MCP over HTTP will work, whether or not it is listed — there is nothing client-specific about the server.
These are the exact descriptions the server gives the model, read live from the running server — so an agent can plan its work without ever reading this page.
leadocean_list_enum_values · Resolve enum values · freeLook up the exact strings a closed-list filter accepts. Industry has 534 values and country 254, so guessing fails: search here first, then pass the exact value to leadocean_search_leads or leadocean_search_companies. Free, and it never spends records.
| Input | Type | Meaning |
|---|---|---|
| name | enum | Which value set to read — ONE name per call. |
| q | string | Substring to search for, e.g. "software" or "germany". Omit to list from the start. |
| limit | int | How many values to return (default 50, max 200). |
leadocean_list_filters · List filters & schemas · freeReturns every filter that leadocean_search_leads and leadocean_count_leads accept, the exact enum values allowed (seniority, emailStatus, company size codes), the names of the response schemas (leadocean.person.v1, leadocean.company.v1) and which data sources are currently active. Free — call it once when you are unsure which values are valid instead of guessing.
leadocean_search_leads · Search leads (people) · 1 recordFind people by job title, seniority, country, industry, company domain, company size or free text. Returns up to `limit` THIN person records (leadocean.person.v1 projection): full name, headline, current job title and company (name, domain, industry, size), location (city, country), profile URL and the has_email / email_status / email_type flags — never email addresses or phone numbers (use leadocean_get_lead with reveal_email for those). Combine filters to narrow, e.g. title="CTO", seniority=["c_suite"], country=["DE"], minEmployees=50. Paginate with `cursor` (meta.nextCursor). Each returned record counts 1 against the monthly records quota (limit=25 → up to 25); the page is capped to what is left this month. Every row carries meta.source_ids.person_id (the record — pass it to leadocean_get_lead) and meta.source_ids.person_group_id (the person — equal across duplicate records of one human; compare it, never look it up). meta.unsupportedFilters lists any filter the active source ignored. By default this searches only people whose best email is `verified` or `catch_all` — the addresses worth sending to. To widen it, pass `emailStatus` yourself (the full enum opts out entirely, `["verified"]` narrows further), or pass `hasEmail` or `reachable`; any one of those replaces the default rather than adding to it. Paging is capped: one search pages through 10,000 rows by default, and on the page that reaches the cap meta.nextCursor is null and meta.depthCapped is true. That walk is over — retrying the same query returns no further cursors, and paging harder is never the fix. Narrow the filters and run several smaller searches instead (split by country, then industry, then headcount band, then title). The figure is per account: an account with a genuine large export to run can have it raised, so tell the user to ask support rather than looping around it. It counts rows PAGED THROUGH and is not the monthly records allowance, which is a separate limit and unaffected. The cursor itself is opaque and signed: pass back exactly the string meta.nextCursor gave you, unchanged. A cursor that has been edited, truncated or built by hand, or one minted for a different search, is refused with 400 "Invalid cursor"; so is any cursor issued before 2026-09-19, in which case start that query again from the first page. Do not store a cursor for later — it is a position in one walk, not a handle on a result set.
| Input | Type | Meaning |
|---|---|---|
| q | string | Free text over name, headline, job title and company. A short query needs every word; a longer one needs most of them, so a five-word phrase behaves as a keyword search rather than an exact match. Prefer the structured filters below. |
| profileKeywords | string[] | The same as keywords — the person’s own profile text; kept under this name for clients that adopted it. |
| excludeProfileKeywords | string[] | Profile keywords to exclude. |
| title | string[] | Job title keywords, e.g. ["Head of Sales","VP Sales"]. Wrap a value in [brackets] for an exact match. |
| excludeTitle | string[] | Job titles to exclude, e.g. ["assistant","intern"]. |
| seniority | enum[] | LeadOcean seniority band (finer than jobLevel). |
| jobLevel | enum[] | Seniority band of the current role. |
| jobFunction | enum[] | Department of the current role. |
| country | enum[] | Where the person is, ISO 3166-1 alpha-2, e.g. ["US","DE"]. |
| continent | enum[] | Continent the person is in. |
| salesRegion | enum[] | Commercial region the person is in. |
| city | string[] | City keywords for the person. |
| excludeCity | string[] | Cities to exclude. |
| education | string[] | School, degree or year phrases, e.g. ["Stanford"]. |
| minConnections | int | Minimum LinkedIn connections on the person's profile, 0–500. |
| domain | string[] | Company website domains, e.g. ["stripe.com"]. |
| companyName | string[] | Company name keywords. |
| excludeCompanyName | string[] | Company name keywords to exclude. |
| companyLinkedin | string[] | Company LinkedIn URLs, e.g. ["https://www.linkedin.com/company/stripe"]. |
| industry | enum[] | Company industry — 534 values; resolve with leadocean_list_enum_values before guessing. |
| excludeIndustry | enum[] | Industries to exclude; same 534 values. |
| companyType | enum[] | Legal type of the company. |
| employeeRange | enum[] | Headcount bracket — cheaper and broader than minEmployees/maxEmployees. |
| minEmployees | int | Company headcount, lower bound — only records whose value we hold; one with no figure on file is never returned. |
| maxEmployees | int | Company headcount, upper bound — only records whose value we hold; one with no figure on file is never returned. |
| minRevenue | int | Annual revenue in USD — only records whose value we hold; one with no figure on file is never returned. |
| maxRevenue | int | Annual revenue ceiling in USD — only records whose value we hold; one with no figure on file is never returned. |
| minFounded | int | Year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| maxFounded | int | Latest year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| minFollowers | int | Minimum company LinkedIn followers — 0 means no minimum; any value of 1 or more matches only records whose figure we hold. |
| fundingType | enum[] | Type of the last funding round. |
| minFunding | int | Total funding raised, USD — only records whose value we hold; one with no figure on file is never returned. |
| maxFunding | int | Total funding raised, USD ceiling — only records whose value we hold; one with no figure on file is never returned. |
| minLastFundingYear | int | Funded no earlier than this year — only records whose value we hold; one with no figure on file is never returned. |
| maxLastFundingYear | int | Funded no later than this year — only records whose value we hold; one with no figure on file is never returned. |
| investors | string[] | Lead investor name keywords, e.g. ["Sequoia"]. |
| naics | string[] | NAICS industry codes, e.g. ["5415"]. |
| sic | string[] | SIC industry codes. |
| keywords | string[] | Keywords in the person’s own profile: headline, summary, skills and job descriptions — the PERSON’s text, not the employer’s (for the company’s text search companies, or filter people by industry / companyName). Each value is a phrase whose words must all appear; several values OR together. |
| excludeKeywords | string[] | Profile keywords to exclude. |
| hqCountry | enum[] | Company headquarters country, ISO 3166-1 alpha-2. |
| hqCity | string[] | Company headquarters city keywords. |
| hqState | string[] | Company headquarters state or region. |
| hqContinent | enum[] | Continent of the company headquarters. |
| hqSalesRegion | enum[] | Commercial region of the company headquarters. |
| technologies | string[] | Technologies detected on the company website (LeadOcean data only). |
| technologyCategories | enum[] | What the detected technology is for, e.g. Analytics or CDN, rather than its name (own data only; covers the ~7.8M crawled companies). |
| emailStatus | enum[] | Email deliverability (LeadOcean data only). |
| emailType | enum[] | Work or personal email (LeadOcean data only). Pair with emailStatus to ask for a deliverable WORK address. |
| hasEmail | boolean | true = only people with a known email, false = only those without (LeadOcean data only). |
| hasPhone | boolean | Only people with, or without, a known phone number (own data only). |
| reachable | enum | Holding an email OR a phone — `strict` additionally requires the email to be verified, catch_all_valid or catch_all. |
| cursor | string | Opaque page token — pass meta.nextCursor from the previous call back, exactly as it was returned, to get the next page. It is signed: an edited, truncated or hand-built cursor, or one from a different search, is refused with 400 "Invalid cursor". Null nextCursor means there are no more pages, either because the results ran out or because the search hit its depth cap (meta.depthCapped). |
| limit | int | Records per page, 1–100 (default 25). |
| facets | boolean | Also return counts per seniority / country / industry where the source supports it. |
leadocean_count_leads · Count leads · freeEstimate how many people match a set of filters BEFORE pulling pages — use it to size an audience or check that a filter combination is not empty. Same filters as leadocean_search_leads. Returns the total, `totalIsExact` (false when counting stopped at the 100,000 cap, so the total is a floor — read the facets for the real shape), a one-record sample count, whether more pages exist, and facet counts. FREE: sizing an audience spends no records, however many times you ask. Pass facets=false for just the number, which is far quicker on a wide filter. By default this searches only people whose best email is `verified` or `catch_all` — the addresses worth sending to. To widen it, pass `emailStatus` yourself (the full enum opts out entirely, `["verified"]` narrows further), or pass `hasEmail` or `reachable`; any one of those replaces the default rather than adding to it. The total therefore describes the mailable audience, not everyone matching the other filters — say so when you report it.
| Input | Type | Meaning |
|---|---|---|
| q | string | Free text over name, headline, job title and company. A short query needs every word; a longer one needs most of them, so a five-word phrase behaves as a keyword search rather than an exact match. Prefer the structured filters below. |
| profileKeywords | string[] | The same as keywords — the person’s own profile text; kept under this name for clients that adopted it. |
| excludeProfileKeywords | string[] | Profile keywords to exclude. |
| title | string[] | Job title keywords, e.g. ["Head of Sales","VP Sales"]. Wrap a value in [brackets] for an exact match. |
| excludeTitle | string[] | Job titles to exclude, e.g. ["assistant","intern"]. |
| seniority | enum[] | LeadOcean seniority band (finer than jobLevel). |
| jobLevel | enum[] | Seniority band of the current role. |
| jobFunction | enum[] | Department of the current role. |
| country | enum[] | Where the person is, ISO 3166-1 alpha-2, e.g. ["US","DE"]. |
| continent | enum[] | Continent the person is in. |
| salesRegion | enum[] | Commercial region the person is in. |
| city | string[] | City keywords for the person. |
| excludeCity | string[] | Cities to exclude. |
| education | string[] | School, degree or year phrases, e.g. ["Stanford"]. |
| minConnections | int | Minimum LinkedIn connections on the person's profile, 0–500. |
| domain | string[] | Company website domains, e.g. ["stripe.com"]. |
| companyName | string[] | Company name keywords. |
| excludeCompanyName | string[] | Company name keywords to exclude. |
| companyLinkedin | string[] | Company LinkedIn URLs, e.g. ["https://www.linkedin.com/company/stripe"]. |
| industry | enum[] | Company industry — 534 values; resolve with leadocean_list_enum_values before guessing. |
| excludeIndustry | enum[] | Industries to exclude; same 534 values. |
| companyType | enum[] | Legal type of the company. |
| employeeRange | enum[] | Headcount bracket — cheaper and broader than minEmployees/maxEmployees. |
| minEmployees | int | Company headcount, lower bound — only records whose value we hold; one with no figure on file is never returned. |
| maxEmployees | int | Company headcount, upper bound — only records whose value we hold; one with no figure on file is never returned. |
| minRevenue | int | Annual revenue in USD — only records whose value we hold; one with no figure on file is never returned. |
| maxRevenue | int | Annual revenue ceiling in USD — only records whose value we hold; one with no figure on file is never returned. |
| minFounded | int | Year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| maxFounded | int | Latest year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| minFollowers | int | Minimum company LinkedIn followers — 0 means no minimum; any value of 1 or more matches only records whose figure we hold. |
| fundingType | enum[] | Type of the last funding round. |
| minFunding | int | Total funding raised, USD — only records whose value we hold; one with no figure on file is never returned. |
| maxFunding | int | Total funding raised, USD ceiling — only records whose value we hold; one with no figure on file is never returned. |
| minLastFundingYear | int | Funded no earlier than this year — only records whose value we hold; one with no figure on file is never returned. |
| maxLastFundingYear | int | Funded no later than this year — only records whose value we hold; one with no figure on file is never returned. |
| investors | string[] | Lead investor name keywords, e.g. ["Sequoia"]. |
| naics | string[] | NAICS industry codes, e.g. ["5415"]. |
| sic | string[] | SIC industry codes. |
| keywords | string[] | Keywords in the person’s own profile: headline, summary, skills and job descriptions — the PERSON’s text, not the employer’s (for the company’s text search companies, or filter people by industry / companyName). Each value is a phrase whose words must all appear; several values OR together. |
| excludeKeywords | string[] | Profile keywords to exclude. |
| hqCountry | enum[] | Company headquarters country, ISO 3166-1 alpha-2. |
| hqCity | string[] | Company headquarters city keywords. |
| hqState | string[] | Company headquarters state or region. |
| hqContinent | enum[] | Continent of the company headquarters. |
| hqSalesRegion | enum[] | Commercial region of the company headquarters. |
| technologies | string[] | Technologies detected on the company website (LeadOcean data only). |
| technologyCategories | enum[] | What the detected technology is for, e.g. Analytics or CDN, rather than its name (own data only; covers the ~7.8M crawled companies). |
| emailStatus | enum[] | Email deliverability (LeadOcean data only). |
| emailType | enum[] | Work or personal email (LeadOcean data only). Pair with emailStatus to ask for a deliverable WORK address. |
| hasEmail | boolean | true = only people with a known email, false = only those without (LeadOcean data only). |
| hasPhone | boolean | Only people with, or without, a known phone number (own data only). |
| reachable | enum | Holding an email OR a phone — `strict` additionally requires the email to be verified, catch_all_valid or catch_all. |
| facets | boolean | Break the total down by seniority, country, industry and company size. On by default. The breakdown is the slow part of this call — four aggregations over the matched set — so pass false when you only want the number and it returns in well under a second. |
leadocean_get_lead · Get lead (enrich one person) · 1 recordEnrich ONE specific person. Pass a LinkedIn profile URL (preferred), a work email, or — when the person came from leadocean_search_leads — the person_id that row carries at meta.source_ids.person_id, which is the only identifier that works for the roughly one person in five who has no LinkedIn URL. The same row carries meta.source_ids.person_group_id: person_id names a RECORD, person_group_id names the PERSON as far as our deduplication knows — two records with the same person_group_id are one human, whichever key you reached them by. Compare it; do not look it up. Returns the full leadocean.person.v1 record: profile_data (identity, headline, summary, location, picture, languages, tags), contact_data (current experiences with company details, still-at-company status, has_email / email_status / email_type flags; email addresses and phones ONLY when reveal_email=true), resume_data — EMPTY from the current source: the enrichment store carries contact points and the current role, not career history, so experiences/educations/certifications/awards/skills come back as {} and a caller should not wait on them. Not for discovery — use leadocean_search_leads to find people first. Counts as 1 record, with or without reveal_email.
| Input | Type | Meaning |
|---|---|---|
| linkedin_url | url | LinkedIn profile URL, e.g. https://www.linkedin.com/in/jane-doe (preferred key). |
| Work email address — used when no LinkedIn URL is known. | ||
| phone | string | Phone number in any common format — the reverse lookup: who is this number. The REST API has always accepted it; this tool used to strip it and then answer "phone is required", which is the opposite of the truth. |
| person_id | string | The id a search row already handed you: meta.source_ids.person_id on any leadocean_search_leads result. Pass it straight back to enrich that exact person — this is the tool to reach for after a search, because it saves carrying an identifier between the two calls. It is a translation rather than a fourth key: the person is looked up by id and the enrichment then runs on the identifier their own record holds, so it is the same record, the same one-record price, the same cached answer and the same meta.key_hash as passing that identifier yourself. Reach for it in particular when a search row has no profile_url — about a fifth of the people search can find have no LinkedIn URL, and search publishes email FLAGS and never an address, so for those people this is the only identifier you can hold. Digits only, as returned. Two distinct 404s: nobody carries that id (a typo, or a row from an older index), or the person exists and we hold no identifier we can enrich by for them — the second says so plainly, is not your mistake, and will not change on a retry. |
| reveal_email | boolean | true = include every contact point we hold — email addresses AND phone numbers — in contact_data. Despite the name it governs BOTH; for phones alone, leadocean_find_phone is the tool to reach for. Costs no extra credit: one enrichment is one record either way. |
leadocean_find_phone · Find phone number · 1 recordGet the PHONE NUMBERS we hold for ONE person. Pass a LinkedIn profile URL (preferred), a work email, a phone number you already have, or the person_id a leadocean_search_leads row carries at meta.source_ids.person_id. Returns has_phone plus every number, each with its type (mobile/direct/office) and priority — best first. has_phone: false means we genuinely hold none, not that the lookup failed. Pair it with leadocean_search_leads: search with hasPhone=true (or reachable) to find people who HAVE a number, which costs nothing extra, then call this for the ones you want. Counts as 1 record, the same as an enrichment — this is the same lookup under a name you can find, so calling it instead of leadocean_get_lead costs nothing more. Not for discovery: it answers about a person you can already name.
| Input | Type | Meaning |
|---|---|---|
| linkedin_url | url | LinkedIn profile URL, e.g. https://www.linkedin.com/in/jane-doe (preferred key). |
| Work email address — used when no LinkedIn URL is known. | ||
| phone | string | A number you already hold, to find the rest we have for that person. |
| person_id | string | The id a search row already handed you: meta.source_ids.person_id on any leadocean_search_leads result. Search with hasPhone=true to find people who HAVE a number, then pass each row's person_id here — nothing to carry between the two calls, and it works for the rows with no profile_url, which is about one person in five. It is a translation rather than a fourth key: the person is looked up by id and the lookup then runs on the identifier their own record holds, so it is the same numbers, the same one-record price and the same meta.key_hash as passing that identifier yourself. Digits only, as returned. Two distinct 404s: nobody carries that id, or the person exists and we hold no identifier we can enrich by for them — the second says so plainly and will not change on a retry. |
leadocean_search_companies · Search companies · 1 recordFind companies rather than people: industry, headcount, revenue, funding, technologies, headquarters. Returns leadocean.company.v1 records. Use it to build an account list first, then pass a domain to leadocean_search_leads to find the people inside. Each company returned counts one record. Paginate with `cursor` (meta.nextCursor). Paging is capped: one search pages through 10,000 rows by default, and on the page that reaches the cap meta.nextCursor is null and meta.depthCapped is true. That walk is over — retrying the same query returns no further cursors, and paging harder is never the fix. Narrow the filters and run several smaller searches instead (split by country, then industry, then headcount band, then title). The figure is per account: an account with a genuine large export to run can have it raised, so tell the user to ask support rather than looping around it. It counts rows PAGED THROUGH and is not the monthly records allowance, which is a separate limit and unaffected. The cursor itself is opaque and signed: pass back exactly the string meta.nextCursor gave you, unchanged. A cursor that has been edited, truncated or built by hand, or one minted for a different search, is refused with 400 "Invalid cursor"; so is any cursor issued before 2026-09-19, in which case start that query again from the first page. Do not store a cursor for later — it is a position in one walk, not a handle on a result set.
| Input | Type | Meaning |
|---|---|---|
| q | string | Free text over name, headline, job title and company. A short query needs every word; a longer one needs most of them, so a five-word phrase behaves as a keyword search rather than an exact match. Prefer the structured filters below. |
| name | string[] | Company name keywords. |
| excludeName | string[] | Company name keywords to exclude. |
| domain | string[] | Company website domains, e.g. ["stripe.com"]. |
| string[] | Company LinkedIn URLs — resolved through the people we hold at the company (most real companies); a URL nobody works at is a 404 pointing at GET /v1/companies/enrich?linkedin_url=…, which always works. | |
| industry | enum[] | Company industry — 534 values; resolve with leadocean_list_enum_values before guessing. |
| excludeIndustry | enum[] | Industries to exclude; same 534 values. |
| companyType | enum[] | Legal type of the company. |
| employeeRange | enum[] | Headcount bracket — cheaper and broader than minEmployees/maxEmployees. |
| minEmployees | int | Company headcount, lower bound — only records whose value we hold; one with no figure on file is never returned. |
| maxEmployees | int | Company headcount, upper bound — only records whose value we hold; one with no figure on file is never returned. |
| minRevenue | int | Annual revenue floor in USD — only records whose value we hold; one with no figure on file is never returned. |
| maxRevenue | int | Annual revenue ceiling in USD — only records whose value we hold; one with no figure on file is never returned. |
| minFollowers | int | Minimum company LinkedIn followers — 0 means no minimum; any value of 1 or more matches only records whose figure we hold. |
| minFounded | int | Earliest year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| maxFounded | int | Latest year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| fundingType | enum[] | Type of the most recent funding round. |
| minFunding | int | Total funding raised, USD floor — only records whose value we hold; one with no figure on file is never returned. |
| maxFunding | int | Total funding raised, USD ceiling — only records whose value we hold; one with no figure on file is never returned. |
| minLastFundingYear | int | Funded no earlier than this year — only records whose value we hold; one with no figure on file is never returned. |
| maxLastFundingYear | int | Funded no later than this year — only records whose value we hold; one with no figure on file is never returned. |
| investors | string[] | Lead investor name keywords, e.g. ["Sequoia"]. |
| naics | string[] | NAICS industry codes, e.g. ["5415"]. |
| sic | string[] | SIC industry codes. |
| keywords | string[] | Keywords across company description, specialties and categories. |
| excludeKeywords | string[] | Company keywords to exclude. |
| hqCountry | enum[] | Company headquarters country, ISO 3166-1 alpha-2. |
| hqCity | string[] | Company headquarters city keywords. |
| hqState | string[] | Company headquarters state or region. |
| hqContinent | enum[] | Continent of the company headquarters. |
| hqSalesRegion | enum[] | Commercial region of the company headquarters. |
| technologies | string[] | Technologies detected on the company website (LeadOcean data only). |
| technologyCategories | enum[] | What the detected technology is for, e.g. Analytics or CDN, rather than its name (own data only; covers the ~7.8M crawled companies). |
| cursor | string | Opaque page token from meta.nextCursor — pass it back exactly as returned. It is signed: an edited, truncated or hand-built cursor, or one from a different search, is refused with 400 "Invalid cursor". Null nextCursor means no more pages, either because the results ran out or because the search hit its depth cap (meta.depthCapped). |
| limit | int | Companies per page, 1–100 (default 25). |
leadocean_get_company · Get company · 1 recordLook up ONE company by website domain (preferred) or LinkedIn company URL. Returns leadocean.company.v1: company_data (name, description, industry, headcount and size code in company_employees, LinkedIn follower count in company_linkedin_followers, headquarters address, founded year, website, social links), company_detected_technologies, and company_metrics (completion_score, marketability_score). Use it to qualify an account, or to get the domain before searching its people with leadocean_search_leads(domain=…). Counts as 1 record.
| Input | Type | Meaning |
|---|---|---|
| linkedin_url | url | LinkedIn company page, e.g. https://www.linkedin.com/company/stripe |
| domain | string | Website domain, e.g. "stripe.com" (preferred key). |
leadocean_export_leads · Start a bulk CSV export · 1 recordStart a bulk export of people as a plain CSV file — ASYNCHRONOUS. This tool returns at once with an export id and status "queued"; the file is NOT ready. Poll leadocean_get_export every pollAfter seconds until status is "done", then hand the user the download link it returns (do not try to read the CSV through a tool). BILLING: up to `limit` records are reserved on the account the moment this is accepted, and each written row is charged like a search result; a column outside the search row adds one enrichment per row. Refused up front, never silently capped: 400 when `limit` exceeds 50,000 or a filter cannot be applied, 429 export_limit_daily / export_limit_monthly with `remaining` and `resetsAt`, 402 when the account has fewer records left than the export needs. One export runs per account at a time; further ones queue. Always call leadocean_count_leads first and confirm the size with the user.
| Input | Type | Meaning |
|---|---|---|
| q | string | Free text over name, headline, job title and company. A short query needs every word; a longer one needs most of them, so a five-word phrase behaves as a keyword search rather than an exact match. Prefer the structured filters below. |
| profileKeywords | string[] | The same as keywords — the person’s own profile text; kept under this name for clients that adopted it. |
| excludeProfileKeywords | string[] | Profile keywords to exclude. |
| title | string[] | Job title keywords, e.g. ["Head of Sales","VP Sales"]. Wrap a value in [brackets] for an exact match. |
| excludeTitle | string[] | Job titles to exclude, e.g. ["assistant","intern"]. |
| seniority | enum[] | LeadOcean seniority band (finer than jobLevel). |
| jobLevel | enum[] | Seniority band of the current role. |
| jobFunction | enum[] | Department of the current role. |
| country | enum[] | Where the person is, ISO 3166-1 alpha-2, e.g. ["US","DE"]. |
| continent | enum[] | Continent the person is in. |
| salesRegion | enum[] | Commercial region the person is in. |
| city | string[] | City keywords for the person. |
| excludeCity | string[] | Cities to exclude. |
| education | string[] | School, degree or year phrases, e.g. ["Stanford"]. |
| minConnections | int | Minimum LinkedIn connections on the person's profile, 0–500. |
| domain | string[] | Company website domains, e.g. ["stripe.com"]. |
| companyName | string[] | Company name keywords. |
| excludeCompanyName | string[] | Company name keywords to exclude. |
| companyLinkedin | string[] | Company LinkedIn URLs, e.g. ["https://www.linkedin.com/company/stripe"]. |
| industry | enum[] | Company industry — 534 values; resolve with leadocean_list_enum_values before guessing. |
| excludeIndustry | enum[] | Industries to exclude; same 534 values. |
| companyType | enum[] | Legal type of the company. |
| employeeRange | enum[] | Headcount bracket — cheaper and broader than minEmployees/maxEmployees. |
| minEmployees | int | Company headcount, lower bound — only records whose value we hold; one with no figure on file is never returned. |
| maxEmployees | int | Company headcount, upper bound — only records whose value we hold; one with no figure on file is never returned. |
| minRevenue | int | Annual revenue in USD — only records whose value we hold; one with no figure on file is never returned. |
| maxRevenue | int | Annual revenue ceiling in USD — only records whose value we hold; one with no figure on file is never returned. |
| minFounded | int | Year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| maxFounded | int | Latest year the company was founded — only records whose value we hold; one with no figure on file is never returned. |
| minFollowers | int | Minimum company LinkedIn followers — 0 means no minimum; any value of 1 or more matches only records whose figure we hold. |
| fundingType | enum[] | Type of the last funding round. |
| minFunding | int | Total funding raised, USD — only records whose value we hold; one with no figure on file is never returned. |
| maxFunding | int | Total funding raised, USD ceiling — only records whose value we hold; one with no figure on file is never returned. |
| minLastFundingYear | int | Funded no earlier than this year — only records whose value we hold; one with no figure on file is never returned. |
| maxLastFundingYear | int | Funded no later than this year — only records whose value we hold; one with no figure on file is never returned. |
| investors | string[] | Lead investor name keywords, e.g. ["Sequoia"]. |
| naics | string[] | NAICS industry codes, e.g. ["5415"]. |
| sic | string[] | SIC industry codes. |
| keywords | string[] | Keywords in the person’s own profile: headline, summary, skills and job descriptions — the PERSON’s text, not the employer’s (for the company’s text search companies, or filter people by industry / companyName). Each value is a phrase whose words must all appear; several values OR together. |
| excludeKeywords | string[] | Profile keywords to exclude. |
| hqCountry | enum[] | Company headquarters country, ISO 3166-1 alpha-2. |
| hqCity | string[] | Company headquarters city keywords. |
| hqState | string[] | Company headquarters state or region. |
| hqContinent | enum[] | Continent of the company headquarters. |
| hqSalesRegion | enum[] | Commercial region of the company headquarters. |
| technologies | string[] | Technologies detected on the company website (LeadOcean data only). |
| technologyCategories | enum[] | What the detected technology is for, e.g. Analytics or CDN, rather than its name (own data only; covers the ~7.8M crawled companies). |
| emailStatus | enum[] | Email deliverability (LeadOcean data only). |
| emailType | enum[] | Work or personal email (LeadOcean data only). Pair with emailStatus to ask for a deliverable WORK address. |
| hasEmail | boolean | true = only people with a known email, false = only those without (LeadOcean data only). |
| hasPhone | boolean | Only people with, or without, a known phone number (own data only). |
| reachable | enum | Holding an email OR a phone — `strict` additionally requires the email to be verified, catch_all_valid or catch_all. |
| limit | int | Rows to export — the account is charged up to this many records (times 2 when a column needs an enrichment). At most 50,000 per export; 500,000 per day and 10,000,000 per period per account. Call leadocean_count_leads first to size it. |
| columns | string[] | Column ids from GET /v1/exports/columns (the catalogue), in order. Omit to use a preset. Every row costs one record whatever the columns; email_N_* columns need reveal email, phone_N_* need email_phone (enrich scope). |
| preset | string | A named column set instead of listing columns. full (default, 45 columns): identity, profile, address, contact flags, emails 1–3, phones 1–2, current role, education, skills — needs the enrich scope. search (27 columns): what the search row carries, no contact points. One record per row either way. |
| reveal | string | Whether contact points are exported. Derived from the columns when omitted. Needs a key with the enrich scope. |
| name | string | A label for the export, shown in the list and the ready email. |
leadocean_get_export · Get an export (poll) · freeStatus and progress of one export. FREE. While queued or running, wait pollAfter seconds and call again. When done, downloadUrl is a signed link to the plain .csv that opens in a browser WITHOUT an API key — the same link as the ready email — valid until downloadExpiresAt (24 hours, never past the file’s 7 days), and downloadNote says so in a sentence you can pass on. An expired link is renewed by calling this tool again: a new 24-hour link, free, nothing rebuilt, for as long as the file is kept. Give it to the user rather than fetching it here. failed and cancelled exports keep what they wrote: rows written are billed and the partial file is downloadable.
| Input | Type | Meaning |
|---|---|---|
| id | string | The export id from leadocean_export_leads. |
leadocean_get_account · Account status · freeEverything about the account this key spends, in one FREE call: plan and whether it is fair use, records used / remaining / when they reset and what happens past the ceiling (throttled or refused), an active grant, the subscription state (no invoices or card details), today’s and this period’s export rows against their ceilings, and the rate. Call this before a large job, or after a 402 or 429, instead of guessing.
leadocean_list_exports · List exports · freeThe account’s newest exports (up to 100), 90 days of history. FREE. The REST list (GET /v1/exports) pages further with a cursor.
| Input | Type | Meaning |
|---|---|---|
| status | string | Only exports in this state. |
| limit | int | How many of the newest to return, default 25. |
What each tool costs is the same as the REST call behind it — see records, limits and errors. So are its limits: a tool that pages with a cursor stops at the same depth cap as the endpoint underneath it.
The whole API is published as plain Markdown for models: llms.txt is the index and llms-full.txt has every endpoint, parameter, enum value, schema field group, error and tool. Both are generated from the running API, so they never drift. Paste the URL into Claude, add it to Cursor as a doc, or fetch it in your own system prompt.