{"openapi":"3.0.3","info":{"title":"MailFlyr API","version":"1.4.0","description":"MailFlyr finds and verifies B2B work emails and watches companies for buying signals.\nThis API exposes the same engine as the dashboard, for Clay, Zapier, n8n, Make or your own backend.\n\n### Products and keys\n\nThere are three APIs, and each has its own key so you can share, meter and revoke them separately.\nCreate keys in the dashboard under **Developer API**.\n\n| API | Endpoint | Key |\n|---|---|---|\n| Email Finder | `POST /api/v1/find` | Email Finder key, `ef_…` |\n| Email Verifier | `POST /api/v1/verify` | Email Verification key, `ev_…` |\n| Signals | `POST /api/v1/signals` | Signals key, `sg_…` |\n\nThe verifier also accepts an `ef_` key, for integrations created before `ev_` keys existed.\nThe finder and signals APIs only accept their own key.\n\n### Authentication\n\nSend the key in either header:\n\n```\nAuthorization: Bearer ef_your_key\nx-api-key: ef_your_key\n```\n\nKeys never go in the URL. Every `GET` on an endpoint is public and returns a short machine-readable\ndescription of it (limits, examples and the other two APIs), with no key and no charge.\n\n### Plans and access\n\nAPI access is included on **Starter** and above. A key on the Free plan is refused with\n`403 upgrade_required`; the dashboard itself works on every plan.\n\n### Credits\n\nAll three APIs spend the same monthly credit balance.\n\n| Action | Cost |\n|---|---|\n| Email found (`/find`) | 1 credit for each person an email is returned for. No email, no charge. |\n| Email verified (`/verify`) | 1 credit per 10 verdicts on paid plans (0.1 each). `unknown` results and malformed addresses are free. |\n| Domain checked (`/signals`) | 1 credit per domain checked. Domains skipped because the engine was offline or busy are free. |\n\nMonthly allowances: Starter 10,000, Growth 20,000, Scale 40,000.\nCredits refill on each renewal and do not roll over.\n\nVerification is paid a whole credit at a time: the first check takes 1 credit and covers that check\nand the next 9. Checks a credit has paid for but not yet used carry over to your\nnext request, so a run of single-email calls costs exactly the same as one batch.\n\n### Rate limits\n\nLimits are per API key, per minute: Starter 60, Growth 120, Scale 300.\nOver the limit you get `429 rate_limited` with `Retry-After` (seconds), `X-RateLimit-Limit` and\n`X-RateLimit-Remaining`. Wait for `Retry-After` before retrying.\n\nA finder request can carry up to 2,000 people and can take minutes, so for large lists\nsend batches of 25 to 100 people rather than one request per person or one huge request.\nSet your HTTP timeout to at least 300 seconds.\n\n### Errors\n\nEvery error has the same shape: a stable machine-readable `error` code and a human-readable\n`detail`. Code against `error`; `detail` wording can change.\n\n```json\n{ \"error\": \"insufficient_credits\", \"detail\": \"You have 0 credits remaining.\" }\n```\n\n### Changelog\n\n**1.4.0** (2026-10-09): `POST /api/v1/find` never returns more emails than the balance pays for. Credits for the\nrequest are held up front (one per person, up to the balance) and unused ones are refunded. When they run out\nmid-request, the remaining people come back with `status: \"skipped\"`, uncharged, and `summary.note` says so.\nPreviously a request could find more emails than the balance and only charge what was left.\n\n**1.3.0** (2026-10-07): Verification costs 0.1 credit per verdict on paid plans\n(1 credit = 10 checks); responses add `verificationsRemaining`.\n`POST /api/v1/verify` now returns `402 insufficient_credits` when a batch is larger than the\nbalance covers, instead of verifying it and charging only what was left. Malformed addresses\nare no longer charged.\n\n**1.2.0** (2026-10-02): The finder is also served at `/api/v1/find`, beside `/verify` and\n`/signals`. `/api/find` keeps working. Every endpoint's `GET` now lists all three APIs and\ntheir keys.\n\n**1.1.0** (2026-09-21): New Email Verification API (`/api/v1/verify`) with its own `ev_` key.\n\n**1.0.0**: Email Finder (`/api/find`) and Signals (`/api/v1/signals`).","contact":{"name":"MailFlyr support","url":"https://mailflyr.com/book-demo"}},"servers":[{"url":"https://mailflyr.com"}],"tags":[{"name":"Email Finder","description":"Find a person's work email from their name and company domain. Key: `ef_…`."},{"name":"Email Verifier","description":"Check whether addresses you already have will deliver. Key: `ev_…`."},{"name":"Signals","description":"Buying signals for company domains: hiring, funding, layoffs, tech and more. Key: `sg_…`."}],"security":[{"BearerAuth":[]},{"ApiKeyHeader":[]}],"paths":{"/api/v1/find":{"get":{"tags":["Email Finder"],"summary":"Describe the finder API","description":"Public, no key needed and never charged. Returns limits, examples and the catalog of all three APIs.","operationId":"describeFind","security":[],"responses":{"200":{"description":"Endpoint description","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"version":"v1","endpoint":"/api/v1/find","limits":{"maxPeoplePerRequest":2000,"creditsPerFoundEmail":1}}}}}}},"post":{"tags":["Email Finder"],"summary":"Find work emails","description":"Finds each person's work email from their name and company domain.\n\nFor every person, MailFlyr generates the likely address patterns (`first.last@`, `flast@`, `first@` and around 30 more,\nwith accents folded: `Renée` → `renee`), verifies them one by one, most likely first, and stops at the first\ndeliverable one. If the domain accepts every address (catch-all), it stops after the first check and returns the most\nlikely pattern with status `catch_all`.\n\n**Charged:** 1 credit for each person an `email` is returned for (`valid` or `catch_all`). People with no email cost nothing. If your credits run out part-way, the people not yet searched are returned with status `skipped` and are not charged.\n\n**Names:** send `firstName` + `lastName`, `first_name` + `last_name`, or one full-name field (`fullName`, `full_name` or `name`),\nwhich is split on the first space. **Domain:** `domain`, `website`, `company_domain` or `companyDomain`; a full URL such as\n`https://www.acme.io/about` is fine; the address is built on `acme.io`. `companyName`, `companyLinkedin` and `personalLinkedin` are optional and are\nreturned unchanged, so you can match results back to your rows.\n\nUp to 2,000 people per request. The response arrives when every person has been processed, which for a large\nrequest can take minutes; batches of 25 to 100 keep requests short.","operationId":"findEmails","deprecated":false,"security":[{"BearerAuth":[]},{"ApiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindRequest"},"examples":{"single":{"summary":"One person","value":{"people":[{"firstName":"Alex","lastName":"Smith","domain":"stripe.com"}]}},"mixed":{"summary":"Batch with different field spellings","value":{"people":[{"fullName":"Sarah Chen","website":"https://linear.app"},{"first_name":"Bob","last_name":"Stone","company_domain":"stone.dev","companyName":"Stone Labs"}]}}}}}},"responses":{"200":{"description":"Every person processed. Rows with no deliverable address have `email: null`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindResponse"},"example":{"summary":{"total":2,"found":1,"creditsUsed":1,"byStatus":{"valid":1,"invalid":1},"userCredits":9999},"results":[{"firstName":"Alex","lastName":"Smith","domain":"stripe.com","email":"alex.smith@stripe.com","status":"valid","score":96},{"firstName":"Sarah","lastName":"Chen","domain":"linear.app","email":null,"status":"invalid"}]}}}},"400":{"description":"The body is not valid: not JSON, no `people`, more than 2,000 people, or a person with no domain.\n\nPossible `error` codes: `invalid_request`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","detail":"…"}}}},"401":{"description":"No key, an unknown key, or a key for a different API.\n\nPossible `error` codes: `unauthorized`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"unauthorized","detail":"…"}}}},"403":{"description":"The key is valid but this account cannot search: Free plan (`upgrade_required`), no credits left, the account email is not confirmed, or the account has not been activated by MailFlyr yet.\n\nPossible `error` codes: `upgrade_required`, `insufficient_credits`, `email_unverified`, `activation_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"upgrade_required","detail":"…"}}}},"429":{"description":"Rate limit exceeded for this key. Wait `Retry-After` seconds before retrying.\n\nPossible `error` codes: `rate_limited`.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"rate_limited","detail":"Rate limit exceeded (60/min). Retry in 12s."}}}},"500":{"description":"The search failed part-way. Nothing is charged; retry the request.\n\nPossible `error` codes: `processing_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"processing_failed","detail":"…"}}}}}}},"/api/v1/verify":{"get":{"tags":["Email Verifier"],"summary":"Describe the verifier API","description":"Public, no key needed and never charged. Returns limits, examples and the catalog of all three APIs.","operationId":"describeVerify","security":[],"responses":{"200":{"description":"Endpoint description","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"version":"v1","endpoint":"/api/v1/verify","limits":{"maxBatchSize":50,"creditsPerResolvedEmail":{"paidPlans":0.1,"free":1}}}}}}}},"post":{"tags":["Email Verifier"],"summary":"Verify emails","description":"Checks whether addresses you already have will deliver: mailbox existence over SMTP, catch-all detection, and\nrole, disposable, full, disabled and spam-trap flags.\n\nSend one address as `email` or up to 50 as `emails`. Duplicates in `emails` are checked once.\nA single `email` returns one result object; `emails` returns `{ summary, results }`.\n\n**Charged:** 0.1 credit per verdict on paid plans (1 credit = 10 checks). `unknown` results and\nmalformed addresses are free. If the batch could cost more than your balance covers, nothing is verified and you get\n`402 insufficient_credits`.\n\nFor lists of thousands, use the bulk verifier in the dashboard (up to 10,000 rows per file).","operationId":"verifyEmails","security":[{"BearerAuth":[]},{"ApiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/VerifyRequest"},"examples":{"single":{"summary":"One address","value":{"email":"alex@stripe.com"}},"batch":{"summary":"Several addresses","value":{"emails":["alex@stripe.com","info@linear.app","nobody@example.com"]}}}}}},"responses":{"200":{"description":"Verified. The shape depends on whether you sent `email` or `emails`.","content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/VerifySingleResponse"},{"$ref":"#/components/schemas/VerifyBatchResponse"}]},"examples":{"single":{"summary":"Response to { email }","value":{"email":"alex@stripe.com","status":"valid","safeToSend":"yes","score":97,"isCatchAll":false,"isRole":false,"isDisposable":false,"creditsUsed":0.1,"remainingCredits":9999,"verificationsRemaining":99999}},"batch":{"summary":"Response to { emails }","value":{"summary":{"total":2,"valid":1,"invalid":0,"catch_all":1,"risky":0,"unknown":0,"creditsUsed":0.2,"remainingCredits":9999,"verificationsRemaining":99998},"results":[{"email":"alex@stripe.com","status":"valid","safeToSend":"yes","score":97,"isCatchAll":false,"isRole":false,"isDisposable":false},{"email":"info@linear.app","status":"catch_all","safeToSend":"risky","score":50,"isCatchAll":true,"isRole":true,"isDisposable":false}]}}}}}},"400":{"description":"The body is not valid: not JSON, neither `email` nor `emails`, no usable address, or more than 50 addresses.\n\nPossible `error` codes: `invalid_request`, `invalid_json`, `empty_request`, `batch_limit_exceeded`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","detail":"…"}}}},"401":{"description":"No key, an unknown key, or a key for a different API.\n\nPossible `error` codes: `unauthorized`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"unauthorized","detail":"…"}}}},"402":{"description":"The batch could cost more than the balance covers. Nothing was verified or charged; send fewer addresses or top up.\n\nPossible `error` codes: `insufficient_credits`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"insufficient_credits","detail":"…"}}}},"403":{"description":"The key is valid but this account cannot verify: Free plan, a balance of 0, or the account email is not confirmed.\n\nPossible `error` codes: `upgrade_required`, `insufficient_credits`, `email_unverified`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"upgrade_required","detail":"…"}}}},"429":{"description":"Rate limit exceeded for this key. Wait `Retry-After` seconds before retrying.\n\nPossible `error` codes: `rate_limited`.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"rate_limited","detail":"Rate limit exceeded (60/min). Retry in 12s."}}}},"503":{"description":"The account has not been activated for verification yet. Contact MailFlyr support.\n\nPossible `error` codes: `activation_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"activation_pending","detail":"…"}}}}}}},"/api/v1/signals":{"get":{"tags":["Signals"],"summary":"Describe the signals API","description":"Public, no key needed and never charged. Returns limits, examples and the catalog of all three APIs.","operationId":"describeSignals","security":[],"responses":{"200":{"description":"Endpoint description","content":{"application/json":{"schema":{"type":"object","additionalProperties":true},"example":{"version":"v1","limits":{"maxDomainsPerRequest":25,"creditsPerDomain":1}}}}}}},"post":{"tags":["Signals"],"summary":"Check company signals","description":"Returns the current buying signals for up to 25 company domains: hiring, funding filings, layoffs,\nad activity, tech stack, launches and more (see `SignalType`). Each signal comes back `detected`, `clear`, `unknown` or `error`.\nA domain with nothing to report has every signal `clear`; nothing is ever invented.\n\nSend one `domain` or a `domains` array. Emails and URLs are reduced to their domain. `signals` limits which checks run;\nleave it out to run every signal except `gdelt`, which is slow and only runs when asked for.\n\n**Charged:** 1 credit per domain checked. If you ask for more domains than you have credits, the extra domains are skipped\n(see `summary.note`) rather than the request failing. Domains that hit an offline or busy engine are free.\n\nChecks run live against public sources, so a request can take tens of seconds.","operationId":"checkSignals","security":[{"BearerAuth":[]},{"ApiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignalsRequest"},"examples":{"batch":{"summary":"Two domains, three signals","value":{"domains":["stripe.com","notion.so"],"signals":["ads","tech","news"]}},"single":{"summary":"One domain, every signal","value":{"domain":"linear.app"}}}}}},"responses":{"200":{"description":"Signals per domain. A domain that failed carries `error` instead of `signals`.","headers":{"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SignalsResponse"},"example":{"summary":{"requested":1,"checked":1,"creditsUsed":1,"userCredits":9999},"results":[{"domain":"stripe.com","company":"Stripe","detected":2,"signals":[{"type":"ads","label":"Meta ad activity","status":"detected","detail":"12 active ads"},{"type":"tech","label":"Website tech stack","status":"detected","detail":"Next.js, Segment, HubSpot"},{"type":"warn","label":"WARN layoff notices","status":"clear","detail":null}]}]}}}},"400":{"description":"The body is not JSON, has no usable domain, or has more than 25 domains.\n\nPossible `error` codes: `invalid_request`, `too_many_domains`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","detail":"…"}}}},"401":{"description":"No key, an unknown key, or a non-signals key. An `ef_` key here gets a message saying to use the `sg_` key.\n\nPossible `error` codes: `unauthorized`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"unauthorized","detail":"…"}}}},"403":{"description":"The key is valid but this account cannot run checks: Free plan or a balance of 0.\n\nPossible `error` codes: `upgrade_required`, `insufficient_credits`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"upgrade_required","detail":"…"}}}},"429":{"description":"Too many requests for this key (`rate_limited`), or the signal engine is at capacity (`engine_busy`). Nothing is charged. Wait `Retry-After` seconds.\n\nPossible `error` codes: `rate_limited`, `engine_busy`.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"rate_limited","detail":"Rate limit exceeded (60/min). Retry in 12s."}}}},"503":{"description":"The signal engine is temporarily unavailable. Nothing is charged; retry shortly.\n\nPossible `error` codes: `engine_offline`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"engine_offline","detail":"…"}}}}}}},"/api/find":{"post":{"tags":["Email Finder"],"summary":"Find work emails (original path)","description":"Identical to `POST /api/v1/find`. Kept for integrations built before the `/api/v1` path; new integrations should use `/api/v1/find`.","operationId":"findEmailsLegacy","deprecated":true,"security":[{"BearerAuth":[]},{"ApiKeyHeader":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindRequest"},"examples":{"single":{"summary":"One person","value":{"people":[{"firstName":"Alex","lastName":"Smith","domain":"stripe.com"}]}},"mixed":{"summary":"Batch with different field spellings","value":{"people":[{"fullName":"Sarah Chen","website":"https://linear.app"},{"first_name":"Bob","last_name":"Stone","company_domain":"stone.dev","companyName":"Stone Labs"}]}}}}}},"responses":{"200":{"description":"Every person processed. Rows with no deliverable address have `email: null`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FindResponse"},"example":{"summary":{"total":2,"found":1,"creditsUsed":1,"byStatus":{"valid":1,"invalid":1},"userCredits":9999},"results":[{"firstName":"Alex","lastName":"Smith","domain":"stripe.com","email":"alex.smith@stripe.com","status":"valid","score":96},{"firstName":"Sarah","lastName":"Chen","domain":"linear.app","email":null,"status":"invalid"}]}}}},"400":{"description":"The body is not valid: not JSON, no `people`, more than 2,000 people, or a person with no domain.\n\nPossible `error` codes: `invalid_request`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"invalid_request","detail":"…"}}}},"401":{"description":"No key, an unknown key, or a key for a different API.\n\nPossible `error` codes: `unauthorized`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"unauthorized","detail":"…"}}}},"403":{"description":"The key is valid but this account cannot search: Free plan (`upgrade_required`), no credits left, the account email is not confirmed, or the account has not been activated by MailFlyr yet.\n\nPossible `error` codes: `upgrade_required`, `insufficient_credits`, `email_unverified`, `activation_pending`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"upgrade_required","detail":"…"}}}},"429":{"description":"Rate limit exceeded for this key. Wait `Retry-After` seconds before retrying.\n\nPossible `error` codes: `rate_limited`.","headers":{"Retry-After":{"$ref":"#/components/headers/Retry-After"},"X-RateLimit-Limit":{"$ref":"#/components/headers/X-RateLimit-Limit"},"X-RateLimit-Remaining":{"$ref":"#/components/headers/X-RateLimit-Remaining"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"rate_limited","detail":"Rate limit exceeded (60/min). Retry in 12s."}}}},"500":{"description":"The search failed part-way. Nothing is charged; retry the request.\n\nPossible `error` codes: `processing_failed`.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"error":"processing_failed","detail":"…"}}}}}}}},"components":{"securitySchemes":{"BearerAuth":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer <key>` — the key for the API you are calling (`ef_`, `ev_` or `sg_`)."},"ApiKeyHeader":{"type":"apiKey","in":"header","name":"x-api-key","description":"Alternative to the Authorization header: `x-api-key: <key>`."}},"headers":{"Retry-After":{"description":"Seconds to wait before retrying.","schema":{"type":"integer"}},"X-RateLimit-Limit":{"description":"Requests allowed per minute for this key.","schema":{"type":"integer"}},"X-RateLimit-Remaining":{"description":"Requests left in the current minute.","schema":{"type":"integer"}}},"schemas":{"Error":{"type":"object","required":["error","detail"],"properties":{"error":{"type":"string","description":"Stable machine-readable code. Code against this.","example":"insufficient_credits"},"detail":{"type":"string","description":"Human-readable explanation. Wording may change.","example":"You have 0 credits remaining."}}},"VerificationStatus":{"type":"string","enum":["valid","invalid","catch_all","role","disposable","inbox_full","disabled","spam_trap","unknown"],"description":"Verdict on the mailbox.\n\n| Status | Meaning | safeToSend |\n|---|---|---|\n| `valid` | Mailbox exists and accepts mail. | `yes` |\n| `invalid` | Mailbox does not exist; mail will bounce. | `no` |\n| `catch_all` | The domain accepts every address, so this one cannot be confirmed. | `risky` |\n| `role` | Deliverable, but a shared inbox such as `info@` or `sales@`. | `risky` |\n| `disposable` | A temporary or throwaway provider. | `no` |\n| `inbox_full` | Mailbox exists but is over quota. | `no` |\n| `disabled` | Mailbox exists but is suspended. | `no` |\n| `spam_trap` | Known or suspected spam trap. | `no` |\n| `unknown` | No verdict could be reached (server timeout, greylisting). Free. | `risky` |"},"SafeToSend":{"type":"string","enum":["yes","risky","no"],"description":"What to do with the address: `yes` send, `risky` send with care or skip for cold outreach, `no` do not send."},"Person":{"type":"object","description":"One person to find. Needs a domain and at least a first or last name (or a full name).","properties":{"firstName":{"type":"string","example":"Alex"},"lastName":{"type":"string","example":"Smith"},"first_name":{"type":"string","description":"Alias of `firstName`."},"last_name":{"type":"string","description":"Alias of `lastName`."},"fullName":{"type":"string","description":"Used when no first/last name is sent; split on the first space.","example":"Alex Smith"},"full_name":{"type":"string","description":"Alias of `fullName`."},"name":{"type":"string","description":"Alias of `fullName`."},"domain":{"type":"string","description":"Company domain. Required unless an alias below is sent.","example":"stripe.com"},"website":{"type":"string","description":"Alias of `domain`; a full URL is fine.","example":"https://www.stripe.com"},"company_domain":{"type":"string","description":"Alias of `domain`."},"companyDomain":{"type":"string","description":"Alias of `domain`."},"companyName":{"type":"string","description":"Optional, returned unchanged."},"companyLinkedin":{"type":"string","description":"Optional, returned unchanged."},"personalLinkedin":{"type":"string","description":"Optional, returned unchanged."}}},"FindRequest":{"type":"object","required":["people"],"properties":{"people":{"type":"array","minItems":1,"maxItems":2000,"items":{"$ref":"#/components/schemas/Person"}},"options":{"type":"object","description":"Optional tuning.","properties":{"acceptable":{"type":"array","items":{"$ref":"#/components/schemas/VerificationStatus"},"default":["valid","catch_all"],"description":"Statuses that count as found. Send `[\"valid\"]` to skip catch-all guesses (they are then neither returned nor charged)."},"concurrency":{"type":"integer","minimum":1,"maximum":20,"default":5,"description":"People processed in parallel within this request."}}}}},"FindResult":{"type":"object","required":["firstName","lastName","domain","email","status"],"properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"domain":{"type":"string","description":"The domain field as you sent it (a URL stays a URL here; the email uses the bare domain)."},"email":{"type":"string","nullable":true,"description":"The address found, or null if none was deliverable. Charged only when not null."},"status":{"type":"string","enum":["valid","invalid","catch_all","role","disposable","inbox_full","disabled","spam_trap","unknown","no_candidates","skipped"],"description":"Status of the returned address. When `email` is null: `invalid` (every pattern bounced), `catch_all` (catch-all domain and catch-all not in `acceptable`), `unknown` (no verdict reachable) `no_candidates` (no name was sent, so there was nothing to try) or `skipped` (credits ran out before this person was searched; not charged)."},"score":{"type":"integer","minimum":0,"maximum":100,"description":"Confidence the address delivers, when available."},"companyName":{"type":"string"},"companyLinkedin":{"type":"string"},"personalLinkedin":{"type":"string"}}},"FindResponse":{"type":"object","properties":{"summary":{"type":"object","properties":{"total":{"type":"integer","description":"People in the request."},"found":{"type":"integer","description":"People an email was returned for."},"creditsUsed":{"type":"integer","description":"Credits charged; equals `found`."},"byStatus":{"type":"object","additionalProperties":{"type":"integer"},"description":"Count of results per status."},"userCredits":{"type":"integer","description":"Balance after this request."},"warning":{"type":"string","description":"Present when some rows arrived with no name, which usually means the name fields are misnamed."},"skipped":{"type":"integer","description":"People not searched because credits ran out. Present only when non-zero."},"note":{"type":"string","description":"Present when people were skipped; says how many and why."}}},"results":{"type":"array","items":{"$ref":"#/components/schemas/FindResult"},"description":"One per person, in request order."}}},"VerifyRequest":{"type":"object","description":"Send `email` or `emails`.","properties":{"email":{"type":"string","format":"email","example":"alex@stripe.com"},"emails":{"type":"array","maxItems":50,"items":{"type":"string"},"example":["alex@stripe.com","info@linear.app"]}}},"VerifyResult":{"type":"object","required":["email","status","safeToSend"],"properties":{"email":{"type":"string","description":"The address checked, lowercased."},"status":{"$ref":"#/components/schemas/VerificationStatus"},"safeToSend":{"$ref":"#/components/schemas/SafeToSend"},"score":{"type":"integer","minimum":0,"maximum":100,"description":"Deliverability confidence."},"isCatchAll":{"type":"boolean"},"isRole":{"type":"boolean"},"isDisposable":{"type":"boolean"},"reason":{"type":"string","description":"Present for malformed addresses and provider failures."}}},"Billing":{"type":"object","properties":{"creditsUsed":{"type":"number","description":"Cost of this request in credits (0.1 per verdict on paid plans)."},"remainingCredits":{"type":"integer","description":"Whole-credit balance after this request."},"verificationsRemaining":{"type":"integer","description":"Checks the balance still covers, including prepaid ones."}}},"VerifySingleResponse":{"allOf":[{"$ref":"#/components/schemas/VerifyResult"},{"$ref":"#/components/schemas/Billing"}]},"VerifyBatchResponse":{"type":"object","properties":{"summary":{"allOf":[{"type":"object","properties":{"total":{"type":"integer"},"valid":{"type":"integer"},"invalid":{"type":"integer"},"catch_all":{"type":"integer"},"risky":{"type":"integer","description":"role, disposable, inbox_full, disabled and spam_trap combined."},"unknown":{"type":"integer"}}},{"$ref":"#/components/schemas/Billing"}]},"results":{"type":"array","items":{"$ref":"#/components/schemas/VerifyResult"}}}},"SignalType":{"type":"string","enum":["ads","tech","news","edgar","warn","velocity","dns","ats","subdomains","companieshouse","apps","gdelt","ph"],"description":"| Type | Signal |\n|---|---|\n| `ads` | Meta ad activity |\n| `tech` | Website tech stack |\n| `news` | Company news: funding, launches, layoffs, leadership |\n| `edgar` | US SEC filings (Form D, S-1, 8-K) |\n| `warn` | US WARN layoff notices |\n| `velocity` | Hiring velocity (needs monitoring history) |\n| `dns` | DNS and email tooling changes |\n| `ats` | Applicant-tracking-system switch |\n| `subdomains` | New subdomains in certificate logs |\n| `companieshouse` | UK Companies House share allotments |\n| `apps` | App Store / Google Play presence |\n| `gdelt` | Global funding news (opt-in, slow) |\n| `ph` | Product Hunt launches |"},"SignalsRequest":{"type":"object","description":"Send `domain` or `domains`.","properties":{"domain":{"type":"string","example":"stripe.com"},"domains":{"type":"array","maxItems":25,"items":{"type":"string"},"example":["stripe.com","notion.so"]},"signals":{"type":"array","items":{"$ref":"#/components/schemas/SignalType"},"description":"Checks to run. Unknown types are ignored. Default: all except `gdelt`."}}},"SignalResult":{"type":"object","properties":{"type":{"$ref":"#/components/schemas/SignalType"},"label":{"type":"string","example":"Meta ad activity"},"status":{"type":"string","enum":["detected","clear","unknown","error"]},"detail":{"type":"string","nullable":true,"description":"What was found, or why the check failed."}}},"SignalsDomainResult":{"type":"object","properties":{"domain":{"type":"string"},"company":{"type":"string","nullable":true},"detected":{"type":"integer","description":"Number of signals with status `detected`."},"signals":{"type":"array","items":{"$ref":"#/components/schemas/SignalResult"}},"error":{"type":"string","description":"Only when the domain could not be checked: `engine_offline`, `engine_busy` or a message."}}},"SignalsResponse":{"type":"object","properties":{"summary":{"type":"object","properties":{"requested":{"type":"integer","description":"Distinct domains in the request."},"checked":{"type":"integer","description":"Domains checked (capped by your balance)."},"creditsUsed":{"type":"integer"},"userCredits":{"type":"integer","description":"Balance after this request."},"note":{"type":"string","description":"Present when domains were skipped for lack of credits."}}},"results":{"type":"array","items":{"$ref":"#/components/schemas/SignalsDomainResult"}}}}}}}