# Radar Leads MCP Server

Radar Leads exposes a Model Context Protocol (MCP) server so any MCP-compatible AI agent — Claude, ChatGPT, Cursor, Windsurf and others — can search 250M+ verified B2B contacts, reveal verified emails and phone numbers, manage lead lists and export CSVs.

- Endpoint: `https://radarleads.io/api/mcp`
- Transport: streamable HTTP
- Auth: `Authorization: Bearer <your-api-key>`
- Availability: all paid plans

## Requirements

MCP access is available on all paid plans. Generate an API key from the AI Agent page in your Radar Leads dashboard. You can hold up to five active keys at a time, and any key can be revoked instantly. Your key scopes every call to your own organisation.

## Connecting

```yaml
mcpServers:
  radarleads:
    url: https://radarleads.io/api/mcp
    headers:
      Authorization: "Bearer YOUR_API_KEY"
```

Requests without a valid, unrevoked key are rejected with HTTP 401.

## Credits

Searching is always free. You spend credits only when revealing a lead, at 1 credit per newly revealed contact. Leads your organisation has already revealed are free forever. Nothing is charged until a reveal job reaches `status: "finished"`.

## Tools

### search_leads — free
Search the database and get a total count plus masked previews (names, titles and companies visible; emails and phones hidden). Supports cursor pagination via `search_after`. Use it to check volume before spending credits.

Arguments: `filters`, `pageSize` (1–100, default 25), `search_after`

### reveal_leads — 1 credit per newly revealed lead
Starts a job that unlocks real contact details for up to N leads matching your filters. Leads your organisation already owns are returned free. Returns a `revealJobId` immediately; it does not wait for the job to finish. Leads are saved to your Default List unless a `listId` is given.

Arguments: `filters` (required), `n` (required, 1–50000), `listId` (optional)

### check_reveal_status — free
Fetches the result of a reveal job. Credits are debited and leads saved only once this returns `status: "finished"`. If it returns `status: "processing"`, call again with the same arguments after a short wait.

Arguments: `revealJobId` (required), `filters`, `n`, `listId`

### list_lead_lists — free
Lists every saved lead list for your organisation with lead counts and IDs. Use the returned IDs with `reveal_leads` or `export_leads`.

Arguments: none

### create_lead_list — free
Creates a new named lead list and returns its ID. List names must be unique within your organisation.

Arguments: `name` (required)

### export_leads — free
Starts a CSV export job for a list, or for all owned leads if no list is given. Supports exporting a range. Returns an `exportJobId` immediately.

Arguments: `listId`, `listName`, `rangeFrom`, `rangeTo`, `customName`

### check_export_status — free
Fetches the result of an export job. Returns `downloadUrl`, `rowCount` and `expiresAt` once ready. Download URLs expire after 24 hours.

Arguments: `exportJobId` (required)

### get_credits — free
Returns your current credit balance broken down by pool: plan credits (monthly), rollover credits (unused from last month) and extra credits (purchased top-ups), plus total available.

Arguments: none

## Asynchronous jobs

`reveal_leads` and `export_leads` start background jobs and return an ID immediately rather than blocking. Follow each with its matching status call — `check_reveal_status` or `check_export_status`. Those calls wait briefly server-side, so most jobs resolve on the first check. If one returns `status: "processing"`, call it again with the same arguments. A job that never completes never costs credits.

## Filters

All filters are optional. Different filters are ANDed together; multiple values within one filter are ORed. Field names must match exactly — unrecognised names are ignored silently.

### Person

| Field | Type | Notes |
|---|---|---|
| `personTitleIncludes` | `string[]` | Job title keywords to include. Words within one string are ANDed; multiple strings are ORed. Include common variations, e.g. `["VP of Sales", "VP Sales"]`. |
| `personTitleExcludes` | `string[]` | Job title keywords to exclude, e.g. `["intern", "assistant"]`. |
| `seniority` | `{ anyOf: string[] }` | `c_suite`, `vp`, `director`, `manager`, `senior`, `entry`, `owner`, `intern`, `partner` |
| `functionIncludes` | `{ anyOf: string[] }` | `engineering`, `sales`, `marketing`, `finance`, `operations`, `human_resources`, `information_technology`, `business_development`, `support`, `education`, `consulting` |
| `personFirstName` | `string` | Partial first-name match. |
| `personLastName` | `string` | Partial last-name match. |
| `personCountry` | `{ anyOf: string[] }` | Where the person is physically based. |
| `personState` | `{ anyOf: string[] }` | Where the person is physically based. |
| `personCity` | `{ anyOf: string[] }` | Where the person is physically based. |

### Company

| Field | Type | Notes |
|---|---|---|
| `companyCountry` | `{ anyOf: string[] }` | Where the company is headquartered. Usually the better choice for B2B targeting. |
| `companyState` | `{ anyOf: string[] }` | Company HQ state. |
| `companyCity` | `{ anyOf: string[] }` | Company HQ city. |
| `companyName` | `string` | Company name partial match. |
| `companyDomain` | `string[]` | Company domains without `www`, e.g. `["stripe.com"]`. |
| `companyDomainMatchMode` | `"strict" \| "contains"` | Default `"strict"` (exact domain). `"contains"` matches substrings. |
| `companySize` | `{ anyOf: string[] }` | `1-10`, `11-50`, `51-200`, `201-500`, `501-1000`, `1001-5000`, `5001-10000`, `10001+` |
| `companyEmployeeMin` | `number` | Precise minimum headcount. |
| `companyEmployeeMax` | `number` | Precise maximum headcount. |
| `companyIndustry` | `{ anyOf: string[] }` | Industry vertical, e.g. `"Software"`, `"Financial Services"`. |
| `companyKeyword` | `string[]` | Keywords matched against company name, description and specialties. |
| `companyKeywordMode` | `"broad" \| "exact"` | Default `"broad"` (all words, any order). `"exact"` matches the phrase in order. |
| `companySpecialties` | `{ anyOf: string[] }` | Specialty tags, e.g. `"CRM"`. |
| `technologies` | `{ anyOf: string[] }` | Tech stack in use, e.g. `"Salesforce"`, `"HubSpot"`. |
| `fundingStage` | `{ anyOf: string[] }` | `seed`, `series_a`, `series_b`, `series_c`, `series_d`, `ipo`, `acquired`, `bootstrap` |
| `annualRevenue` | `{ anyOf: string[] }` | `lt_1m`, `1m_10m`, `10m_50m`, `50m_200m`, `200m_1b`, `gt_1b` |
| `totalFunding` | `{ anyOf: string[] }` | Same buckets as `annualRevenue`. |

### Contact availability

| Field | Type | Notes |
|---|---|---|
| `hasEmail` | `boolean` | Only leads with an email on file. |
| `hasPhone` | `boolean` | Only leads with a phone number on file. |
| `hasLinkedin` | `boolean` | Only leads with a LinkedIn URL on file. |
| `emailStatus` | `{ anyOf: string[] }` | `deliverable`, `catch_all`, `unavailable`, `pattern_match`. Use `deliverable` for highest quality. |

### Match modes

| Field | Type | Notes |
|---|---|---|
| `roleMatchMode` | `"all" \| "any"` | Default `"all"`. Combines `personTitleIncludes` + `seniority` + `functionIncludes`. Use `"any"` to broaden. |
| `companyMatchMode` | `"all" \| "any"` | Default `"any"`. Combines `companyName` + `companyIndustry`. Use `"all"` to tighten. |

## Example filter object

VPs of Sales at US software companies with 51–500 employees and verified deliverable emails:

```json
{
  "personTitleIncludes": ["VP of Sales", "VP Sales", "Head of Sales"],
  "seniority": { "anyOf": ["vp"] },
  "companyCountry": { "anyOf": ["United States"] },
  "companySize": { "anyOf": ["51-200", "201-500"] },
  "companyIndustry": { "anyOf": ["Software"] },
  "emailStatus": { "anyOf": ["deliverable"] }
}
```

## Zero results

When a search returns no matches, the response includes a `_diagnosis` object:

- `counterfactual_leads` — array of `{ remove_filter, results, relevance_warning }`, each meaning "removing this filter gives N results"
- `top_suggestion` — the single best suggestion in plain English
- `why_zero` — explanation of why nothing matched
- `diagnosis_type` — `narrow_filters` (relax a filter), `hard_blocker` (start over), or `conflicting_filters` (filters contradict each other)

## Support

hello@radarleads.io
