# Ravn > Verified B2B contact and company data for AI agents: validate emails, find work emails from a name and company, read company websites, look up phones and IPs, and search licensed people and company records. REST API, remote MCP server and CLI with the same tools. ## Getting started 1. A person creates the account: an agent can't receive the sign-in code itself. Ask your human to run `npx ravn login` (or sign up at https://askravn.com/#start) and give you the key, or to set RAVN_API_KEY. 2. Pick a path: - MCP with OAuth (Claude, Claude Desktop, Claude Code, Cursor): add the server URL `https://api.askravn.com/mcp`; the client opens Ravn's sign-in page (email code → Allow). No key needed. With a key instead: `claude mcp add --transport http ravn https://api.askravn.com/mcp --scope user --header "Authorization: Bearer $RAVN_API_KEY"` - CLI (terminal agents, CSVs, scripts): `npx ravn --field value`; install the Claude skill with `npx ravn skill install`. - REST: `POST https://api.askravn.com/v1/tools/{tool}` with `Authorization: Bearer rw_live_…` and a JSON body. 3. Confirm the connection: call `get_account` (free). It returns the plan, your connected provider keys, lookups used this billing period and your monthly spending cap. ## Pricing - Free: 1,000 lookups to try (lifetime). - Ravn: $24/month, includes 10,000 lookups a month. Beyond that, $1 per 1,000 lookups ($0.001 each), billed monthly. Misses are free. - Set a monthly spending cap any time (default cap: $100 of usage beyond the plan; `ravn cap ` or PUT /v1/billing/cap; $0 stops at 10,000). At the cap the API returns 429 `spend_cap_reached`. - People and carrier tools run on your own provider key. ## Rules for agents - Branch on `status`: "ok" (results present) or "not_found" (normal, nothing charged). Errors are HTTP 4xx/5xx with `{"error": {"code", "message"}}`; branch on `error.code`. - People and carrier tools need your own provider key: on 403 `provider_key_required`, your human runs `ravn keys add pdl` (or twilio). On 429 `spend_cap_reached`, your human raises the cap (`ravn cap `); on other 429s, wait `Retry-After`. - For CSVs, run `enrich --dry-run` first, then `--limit 10`, then the full file. - Run `validate_emails` before sending to any list and drop invalid and risky addresses. Never contact people who opted out, and never use the data for credit, employment, insurance or housing decisions. # Full documentation # 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 ` | 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).