# Docs: Contact Data API, MCP Server & CLI — Ravn

Canonical: https://askravn.com/docs

[Getting started](#start)[Authentication](#auth)[MCP server](#mcp)[CLI](#cli)[Responses](#envelope)
[Errors](#errors)[Limits](#limits)[Tools](#tools)[Privacy](#privacy)

# Ravn docs

## Getting started

Three ways in, all with the same tools and the same answers:

- MCP for Claude, Codex, Cursor and other agents: `https://api.askravn.com/mcp`

- CLI for scripts, CSVs and terminal agents: `npx ravn`

- REST for jobs and integrations: `POST https://api.askravn.com/v1/tools/{tool}`

Agents can set themselves up. Paste this into yours:

```
Read https://askravn.com/llms.txt and follow its Getting started section.
Then call get_account to confirm the connection.
```

## Authentication

Every call sends `Authorization: Bearer rw_live_…`. Get a key on the [home page](https://askravn.com/#start), with `npx ravn login`, or headless:

```
curl -X POST https://api.askravn.com/v1/auth/start -d '{"email":"you@company.com"}'
curl -X POST https://api.askravn.com/v1/auth/verify -d '{"email":"you@company.com","code":"123456"}'
# → {"api_key": "rw_live_…"}  shown once
```

A person has to receive the code, so an agent can't create an account on its own. Manage keys with `GET /v1/keys`, `POST /v1/keys {"name"}` and `DELETE /v1/keys/{id}` (up to 20 per account).

## MCP server

Remote MCP over Streamable HTTP at `https://api.askravn.com/mcp`. The tools are the same as the REST tools below. It supports OAuth sign-in, so there's no key to copy:

- Claude (web, desktop, mobile): Settings → Connectors → Add custom connector → paste `https://api.askravn.com/mcp` → sign in with your email code → Allow.

- Claude Code: `claude mcp add --transport http ravn https://api.askravn.com/mcp`, then run `/mcp` and choose Authenticate.

- Cursor, Windsurf and other MCP clients: add the URL; they open the same sign-in page.

Prefer an API key (CI, servers, scripts)? Send it as a header instead:

```
claude mcp add --transport http ravn https://api.askravn.com/mcp \
--scope user --header "Authorization: Bearer $RAVN_API_KEY"
```

Cursor (`.cursor/mcp.json`) and other clients:

```
{ "mcpServers": { "ravn": {
"url": "https://api.askravn.com/mcp",
"headers": { "Authorization": "Bearer rw_live_…" } } } }
```

## CLI

```
npx ravn login                       # email code → key saved to ~/.config/ravn
npx ravn whoami                      # plan, usage this month, spending cap (free)
npx ravn cap 100                     # monthly spending cap beyond the plan, in dollars (0 = stop at 10,000)
npx ravn tools                       # every tool and its cost
npx ravn keys add pdl                # connect your People Data Labs key (or: twilio)
npx ravn validate_email --email jane@acme.com
npx ravn search_people --title "Head of Design" --company_domain linear.app --limit 5
npx ravn enrich leads.csv --tool validate_email --column Email --dry-run
npx ravn enrich leads.csv --tool search_business_email_by_name --map name=Name,company=Website --limit 10 --out sample.csv
npx ravn skill list                  # the skills library
npx ravn skill install --all         # Claude skills → ~/.claude/skills
npx ravn audience leads.enriched.csv --column Email --format meta --only-valid
# SHA-256 ad audience, hashed locally, nothing uploaded
```

Key order: `--api-key`, `RAVN_API_KEY`, saved login. Output is JSON when piped and tables in a terminal; logs go to stderr. `enrich` keeps every original column, adds `rw_*` columns, saves progress every 25 rows and resumes where it stopped (`--restart` to start over). When the 5-hour window fills it waits for the reset unless you pass `--no-wait`. Exit codes: 0 ok · 1 error · 2 usage · 3 auth · 4 rate limited · 5 upgrade needed.

## Responses

Every finished call returns HTTP 200 with the same shape. Branch on `status`, not the HTTP code.

```
{
"status": "ok",            // or "not_found": a normal outcome, nothing charged
"results": { … },          // null when not_found
"metadata": {
"request_id": "req_…", "lookups_used": 1,
"fair_use": { "lookups_remaining_5h": 999, "lookups_reset_5h": "2026-11-03T20:00:00.000Z" }
}
}
```

Email verdicts carry `status` (`valid`, `invalid`, `risky`, `unknown`), a `risk_score` from A (send) to F (don't), a `reason` and the checks behind it. `mailbox_checked: false` means we proved the domain takes mail but did not test the mailbox itself; we never call that `valid`.

## Errors

Errors are `{"error": {"code", "message"}}` with no results and no charge. Branch on `error.code`.

HTTP | code | Meaning | 

401 | `unauthorized` | Missing, wrong or revoked key | 

403 | `provider_key_required` | People, company or carrier tool called with no provider key connected | 

502 | `provider_error` | Your data provider rejected the key or returned an error | 

404 | `unknown_tool` | No tool by that name (`GET /v1/tools` lists them) | 

413 | `too_large` | Body over 1 MB | 

422 | `invalid_input` | Input failed validation; `message` says which field | 

429 | `fair_use_exhausted`, `rate_limited` | Free allowance used up or a burst limit; wait `Retry-After` seconds, or upgrade. `spend_cap_reached`: your monthly spending cap is hit; raise it with `PUT /v1/billing/cap` or `ravn cap <dollars>` | 

503 | `upstream_unavailable`, `provider_keys_unconfigured` | A source is down (retry), or provider keys aren’t set up on the server yet | 

500 | `internal_error` | Our fault; retry, or send a smaller request | 

## Limits

Every tool counts as one lookup. Free is 1,000 lookups to try. Ravn is $24 a month and includes 10,000 lookups a month; beyond that it is $1 per 1,000 lookups ($0.001 each), billed monthly, with a monthly spending cap you set (default $100 beyond the plan; $0 stops at 10,000). A miss gives the lookup back and is free. People, company and carrier tools (`search_people`, `domain_to_company`, `reverse_email_lookup`, `phone_to_carrier` and the like) run on your own provider account: connect a People Data Labs key (people and companies) or a Twilio key (carrier) with `ravn keys add pdl` or on the account page. Called without one, they return `403 provider_key_required`. `get_billing` lists every tool.

## Tools

All tools are `POST /v1/tools/{name}` with a JSON body. Machine-readable list with JSON Schemas: [GET /v1/tools](https://api.askravn.com/v1/tools). Names follow `search_`, `reverse_` and `x_to_y`; `/api/v1/tools/{name}` works too.

### validate_email
Checks one email: syntax, domain and MX, disposable/role/free-provider flags, catch-all and (where enabled) the mailbox itself. Returns status valid|invalid|risky|unknown and a risk score A-F. (lookup)
- `email` string, required

### validate_emails
Checks up to 1,000 emails at once; one verdict per unique address. Use before sending to a list. (lookup)
- `emails` string[], required

### search_business_email_by_name
Finds a person's work email from their name and company domain. Learns the company's address pattern from its own website, then checks the best candidates. Charged only when found. (lookup)
- `name` string
- `first_name` string
- `last_name` string
- `company` string, required — Company domain or website URL, e.g. acme.com

### verify_lead
Checks a whole lead in one call: verifies the email (or finds it from name + company), validates the phone, confirms the company website is live and reads it (socials, phones, team), and checks the person is listed there. Returns verdict good|review|bad with reasons. One lookup per check run. (lookup)
- `email` string
- `phone` string
- `name` string
- `company` string — Company domain or website URL, e.g. acme.com
- `country` string — ISO country for national phone numbers, default US

### search_businesses
Searches Ravn's own directory of US and Canadian businesses by name, category, MCC/NAICS code, service offered, city, region, postcode or distance, with website, phones, generic role addresses and socials. Business data only — never named people. One lookup per business returned; misses are free. Page with next_cursor. (lookup)
- `query` string — Business name or part of it
- `category` string | string[] — e.g. dentist, plumber, italian_restaurant, accountant
- `country` string
- `region` string — State or province, e.g. TX or ON
- `city` string
- `postcode` string — Full or leading part, e.g. M5V or 787
- `mcc` string — Merchant category code
- `naics` string — NAICS code or prefix
- `service` string — A service the business offers, e.g. "drain cleaning"
- `near` object
- `has_email` boolean
- `has_phone` boolean
- `has_website` boolean
- `live_only` boolean — Only businesses whose website we found live
- `limit` integer, default 25
- `cursor` string

### lookup_business
Every business in Ravn's directory at a domain or phone number (chains return each location), with generic role addresses and socials. Business data only — never named people. One lookup per call; misses are free. (lookup)
- `domain` string — Company domain or website URL, e.g. acme.com
- `phone` string

### scrape_website
Reads a company's own website (homepage plus its contact, about and team pages) and returns structured JSON: name, description, logo, address, public emails and phones, social profiles, named team members with titles, technologies used, and whether the site looks live. (lookup)
- `domain` string, required — Company domain or website URL, e.g. acme.com

### ip_to_company
Returns the organisation that owns an IPv4 address, from public registry data. Works for company networks; home and mobile ISPs return not_found. (lookup)
- `ip` string, required

### validate_phone
Checks a phone number: valid or not, E.164 format, country and number-plan line type (mobile, landline, VoIP, toll-free). Free of credits; use phone_to_carrier for a live carrier check. (lookup)
- `phone` string, required
- `country` string — ISO country for national numbers, default US

### phone_to_carrier
Live carrier lookup: the real line type (mobile, landline, fixed or non-fixed VoIP) and carrier name. Use to make sure a number is a real mobile before texting. (lookup + your Twilio key)
- `phone` string, required
- `country` string

### linkedin_to_mobile_phone
Returns a mobile number for a LinkedIn profile (single or up to 100). When carrier checks are on, only numbers confirmed as mobile are returned. (lookup + your PDL key)
- `linkedin_url` string
- `linkedin_urls` string[]

### search_people
Searches people by title, company, domain, location, seniority, industry, department and company size. Lists mean OR within a field, AND across fields. Charged per profile returned. Page with next_cursor. (lookup + your PDL key)
- `query` string
- `title` string | string[]
- `company` string | string[]
- `company_domain` string | string[]
- `country` string | string[]
- `state` string | string[]
- `city` string | string[]
- `seniority` string | string[] — Intern, Entry, Senior, Manager, Director, VP, Head, C Suite, Owner, Partner
- `industry` string | string[]
- `department` string | string[]
- `functional_area` string | string[]
- `employee_range` string | string[] — 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001+
- `exclude_company_domain` string[]
- `exclude_country` string[]
- `exclude_state` string[]
- `exclude_industry` string[]
- `exclude_seniority` string[]
- `exclude_employee_range` string[]
- `limit` integer, default 10
- `cursor` string

### search_companies
Searches companies by name, domain, industry, location and size. Charged per company returned. (lookup + your PDL key)
- `query` string
- `domain` string | string[]
- `industry` string | string[]
- `country` string | string[]
- `state` string | string[]
- `employee_range` string | string[]
- `exclude_industry` string[]
- `limit` integer, default 10
- `cursor` string

### domain_to_company
Full company profile from its domain: size, employee count, industry, founded, location, LinkedIn, NAICS. (lookup + your PDL key)
- `domain` string, required — Company domain or website URL, e.g. acme.com

### search_business_profile_by_name
Full business profile (title, company, location, LinkedIn, verified work email) from a name and company. (lookup + your PDL key)
- `name` string, required
- `company` string, required

### search_linkedin_profile
Finds a person's LinkedIn profile URL from their name and company. (lookup + your PDL key)
- `name` string, required
- `company` string, required

### linkedin_to_best_email
Best available verified email for a LinkedIn profile: work email first, personal as fallback. (lookup + your PDL key)
- `linkedin_url` string
- `linkedin_urls` string[]

### linkedin_to_business_email
Verified work email for a LinkedIn profile. (lookup + your PDL key)
- `linkedin_url` string
- `linkedin_urls` string[]

### linkedin_to_personal_email_usa
Verified US personal email for a LinkedIn profile. (lookup + your PDL key)
- `linkedin_url` string
- `linkedin_urls` string[]

### reverse_email_lookup
Full business profile from an email (single or up to 100). (lookup + your PDL key)
- `email` string
- `emails` string[]

### reverse_linkedin_lookup
Full business profile from a LinkedIn profile URL. (lookup + your PDL key)
- `linkedin_url` string, required

### email_to_linkedin
The LinkedIn profile URL behind an email. (lookup + your PDL key)
- `email` string, required

### get_account
Your account, plan, lookups used this billing period, your monthly spending cap, and which data-provider keys you have connected. Free. (free)

### get_billing
Your plan, renewal date, usage and spending cap this period, and what each tool costs. Free. (free)

### get_usage
Lookups per 5-hour window for the last N days (default 7). Free. (free)
- `days` integer, default 7

## Privacy

Anyone can opt out at [askravn.com/privacy](https://askravn.com/privacy#optout) or with `POST /v1/privacy/optout {"identifier": "email or phone"}`. Opted-out people are never returned again, to anyone. You agree to use the data lawfully: no contacting people who opted out, no spam, and never for credit, employment, insurance or housing decisions. See the [terms](https://askravn.com/terms).
