{"openapi":"3.1.0","info":{"title":"Consulti.ai API","version":"1.0","description":"B2B lead search, enrichment, email verification, local business and creator discovery, inbox placement tests and list management. Full reference: https://www.consulti.ai/llms.txt. Integrations and MCP: https://www.consulti.ai/api-docs."},"servers":[{"url":"https://www.consulti.ai/api/v1","description":"Production"}],"tags":[{"name":"Verification","description":"Email and catch-all verification."},{"name":"Leads","description":"B2B lead search, lookup and enrichment."},{"name":"Local","description":"Local businesses from Google Maps."},{"name":"Creators","description":"Podcasts and YouTube channels."},{"name":"Instagram","description":"Instagram profiles."},{"name":"Deliverability","description":"Inbox placement tests."},{"name":"Lists","description":"Saved lists."},{"name":"Account","description":"Credit balances."},{"name":"Meta","description":"Canonical filter values. No auth."}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"API key authentication. Use your API key (starts with \"capi_\") from https://www.consulti.ai/settings?tab=integrations#api-keys in the Authorization header: Authorization: Bearer capi_your_api_key"}},"parameters":{"IdempotencyKey":{"name":"Idempotency-Key","in":"header","required":false,"description":"Accepted but has no effect at present. A retried request is a new billable request, so de-duplicate on your side.","schema":{"type":"string"}}},"headers":{"RetryAfter":{"description":"Seconds to wait before retrying","schema":{"type":"integer"}}},"schemas":{"ErrorResponse":{"type":"object","properties":{"success":{"type":"boolean","enum":[false]},"error":{"type":"string","description":"Error message"},"hint":{"type":"string","description":"Optional hint for resolving the error"},"field":{"type":"string","description":"Optional field name for validation errors"},"code":{"type":"string","description":"Optional stable error code to branch on, e.g. search_unavailable. The `error` text may change; this does not."},"credits_used":{"type":"integer","description":"Credits consumed by this request (0 for auth/validation errors)"}},"required":["success","error"]}},"responses":{"BadRequest":{"description":"Bad Request. Malformed body or invalid filter value. Validation responses include the field, the invalid values, and a hint pointing at the /api/v1/meta/{filter} endpoint that lists accepted values. Rejected before any credit is reserved, so never billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Unauthorized":{"description":"Unauthorized. The API key is missing or invalid. Check the Authorization header. Never billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"InsufficientCredits":{"description":"Payment Required. Several causes: insufficient credits, a plan gate (reason \"tier_required\"), a usage cap, a billing lock, or paused lead access (reason \"billing_past_due\": the last payment failed, so lead endpoints are refused until billing is updated; is_owner: false means the key belongs to a team member and the team owner must update billing). Read the body; do not assume a top-up is the fix.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"Forbidden":{"description":"Forbidden. The current plan does not include this endpoint. Lead data endpoints require Pro or higher. Never billed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"NotFound":{"description":"Not Found. No lead matched the query on a find or enrich request. The reserved credit is refunded, but the route does not verify the refund succeeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"RateLimited":{"description":"Too Many Requests. Rate limited. Honor the Retry-After header or retry with exponential backoff.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"ServerError":{"description":"Server Error. The request failed inside Consulti. On billed endpoints treat it as possibly charged and do not retry automatically: every retry is a new billable request with no replay protection. With code refund_unconfirmed the refund could not be confirmed. Reconcile with GET /credits before any retry. If a 500 persists, contact support with the request id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}},"paths":{"/verify":{"post":{"operationId":"verify","summary":"Verify email","description":"Verify whether an email address is valid and deliverable. Uses SMTP checks.\n\nRouting guide: use the boolean flags alongside the status field to route follow-ups. is_catch_all=true → route to LinkedIn enrichment (SMTP cannot verify catch-all mailboxes). is_role_account=true → deprioritise (info@, support@, sales@ usually low reply rates). is_disposable=true → drop (10minutemail, mailinator, etc.). status=unknown → retry later (transient upstream issue, not a permanent verdict).","tags":["Verification"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"Only successful verifications are billed — 1 verification credit each. Requests that fail validation (400) or auth (401), or hit insufficient credits (402), are never billed and return credits_used: 0. Results served from our cache cost the same as fresh ones. When the verifier does not answer in time or is temporarily unavailable, the call returns 503 with code verifier_unavailable, credits_used: 0 and a Retry-After header in seconds. The 503 is sent only after the reserved credit is confirmed refunded. Retry after Retry-After only when credits_used: 0 is present. If the refund cannot be confirmed, the call returns 500 with code refund_unconfirmed and no Retry-After. Reconcile with GET /credits before retrying, because a retry reserves another credit.","x-consulti-billable":true,"x-consulti-tier":"any","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":"Email address to verify. Leading/trailing whitespace is trimmed automatically before validation."}},"required":["email"]},"example":{"email":"john@acme.com"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"email":{"type":["string","null"]},"status":{"type":["string","null"],"description":"Status (good / risky / bad / unknown)"},"is_deliverable":{"type":["boolean","null"],"description":"Deliverable?"},"is_disposable":{"type":["boolean","null"],"description":"Disposable provider (10minutemail, mailinator)? Drop."},"is_role_account":{"type":["boolean","null"],"description":"Role/shared inbox (info@, support@, sales@)? Deprioritise — typically low reply rate."},"is_catch_all":{"type":["boolean","null"],"description":"Catch-all domain? Route to LinkedIn enrichment when true — SMTP cannot verify catch-all mailboxes."},"credits_used":{"type":["number","null"],"description":"Verification credits charged (1)."}}}}},"example":{"success":true,"data":{"email":"john@acme.com","status":"good","is_deliverable":true,"is_disposable":false,"is_role_account":false,"is_catch_all":false,"credits_used":1}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Service Unavailable. code verifier_unavailable: the verifier did not answer in time or is temporarily unavailable. Sent only after the reserved credit is confirmed refunded, so the body carries credits_used: 0. Wait the Retry-After seconds, then retry. If the refund cannot be confirmed, the call returns 500 with code refund_unconfirmed and no Retry-After instead: reconcile with GET /credits before retrying.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/verify/catchall":{"post":{"operationId":"verify-catchall","summary":"Verify catch-all mailbox","description":"Resolve whether a specific mailbox on a catch-all domain actually exists. Unlike /verify (SMTP), this uses Consulti’s ESP verification engine to probe mailbox existence. Use it on addresses that /verify reports as is_catch_all=true.\n\nRequires a plan with catch-all verification enabled (Pro or higher); returns 402 when the plan isn't entitled.","tags":["Verification"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"Only successful verifications are billed — 5 verification credits each. Requests that fail validation (400) or auth (401), or hit insufficient credits (402), are never billed and return credits_used: 0. That includes an address that passes the format check but is rejected as malformed during verification: it returns 400 \"Invalid email address.\" and the reserved credits are refunded. Results served from our cache cost the same as fresh ones. The verdict normally arrives within about 50 seconds of the request, but a slow call can run for up to 300 seconds, so set your client timeout to more than 300 seconds. A call your client gives up on can still be billed if the verdict lands afterwards. When the engine does not return a verdict within about 50 seconds, the call returns 503 with code verifier_unavailable and a Retry-After header in seconds. The reserved credits are refunded, but this 503 carries no credits_used, so check GET /credits before you retry. When the reserved credits cannot be confirmed as refunded, any failure (including a malformed address that would otherwise be a 400) returns 500 with code refund_unconfirmed and no Retry-After. Reconcile with GET /credits before retrying, because a retry reserves another 5 credits. The call stays billed when a 500 follows a verdict the engine already returned, so check GET /credits before you retry any 500.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":"Email address on a catch-all domain to resolve. Leading/trailing whitespace is trimmed automatically before validation."}},"required":["email"]},"example":{"email":"john@catchall-domain.com"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"email":{"type":["string","null"]},"status":{"type":["string","null"],"description":"Resolved status (good / risky / bad / unknown). good = mailbox exists; bad = does not exist; risky = catch-all/inconclusive (do not send)."},"is_catch_all":{"type":["boolean","null"]},"catch_all_verification":{"type":["object","null"],"properties":{"score":{"type":["number","null"],"description":"Confidence score 0–100 for the resolved verdict."},"status":{"type":["string","null"],"description":"Deliverable / Catch-All / Undeliverable / Unknown."},"provider":{"type":["string","null"]}}},"credits_used":{"type":["number","null"],"description":"Verification credits charged (5)."},"cached":{"type":["boolean","null"],"description":"true when served from a prior result without re-probing upstream."}}}}},"example":{"success":true,"data":{"email":"john@catchall-domain.com","status":"good","is_catch_all":true,"catch_all_verification":{"score":95,"status":"Deliverable","provider":"consulti"},"credits_used":5,"cached":false}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Service Unavailable. code verifier_unavailable: the verification engine did not return a verdict within about 50 seconds of the request. Wait the Retry-After seconds before retrying. The reserved credits are refunded, but the body has no credits_used, so check GET /credits before you retry. A slow call can run for up to 300 seconds, so set your client timeout to more than 300 seconds. If the refund cannot be confirmed, the call returns 500 with code refund_unconfirmed and no Retry-After instead: reconcile with GET /credits before retrying.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/credits":{"get":{"operationId":"credits","summary":"Get current credit balance","description":"Return the authenticated API user’s current lead and verification credit balances. Useful as a pre-flight check before running a large search/enrich batch.","tags":["Account"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"Free — no deduction.","x-consulti-billable":false,"x-consulti-tier":"any","x-consulti-mcp":true,"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"lead_credits":{"type":["number","null"],"description":"Total spendable lead credits across all pools; team accounts report the shared team pool"},"verification_credits":{"type":["number","null"],"description":"Current verification credit balance"}}}}},"example":{"success":true,"data":{"lead_credits":1500,"verification_credits":5000}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/meta/{filter}":{"get":{"operationId":"meta-filter","summary":"Get canonical values for a filter","description":"Return the canonical list of values accepted by a given enum filter. No auth required. Snapshot-first with a 1-hour edge cache that revalidates against the live database. Use slug \"_all\" to fetch every filter in one snapshot-only payload.\n\nThe technologies filter is non-strict (values outside the list still filter) and its response also includes \"entries\": [{ name, category }] for category grouping.\n\nResponse (_all): { \"filters\": { \"<slug>\": { \"canonical_filter\", \"count\", \"values\": string[], \"generated_at\" } } }","tags":["Meta"],"parameters":[{"name":"filter","in":"path","required":true,"description":"A canonical filter slug (e.g. industries, technologies, states, instagram-categories) — or \"_all\" to return every filter in one payload. The complete slug set is listed in the Canonical filter values section of the docs and returned by the \"_all\" response. The technologies response also includes entries: [{ name, category }] for category grouping.","schema":{"type":"string"}}],"x-consulti-credits":"Free. No auth required.","x-consulti-billable":false,"x-consulti-tier":"any","x-consulti-mcp":false,"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"filter":{"type":["string","null"],"description":"The slug you requested"},"canonical_filter":{"type":["string","null"],"description":"The canonical filter name the slug resolves to (e.g. b2b-industries)"},"source":{"type":["string","null"],"description":"\"live\" if fetched from DB, \"snapshot\" if served from build-time fallback"},"count":{"type":["number","null"],"description":"Total number of canonical values"},"generated_at":{"type":["string","null"],"description":"ISO timestamp the values were generated at"},"values":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Array of accepted values for this filter"},"entries":{"description":"technologies only: [{ name, category }] pairs for category grouping, returned when source is \"snapshot\"."},"filters":{"description":"\"_all\" only: an object keyed by slug, each value { canonical_filter, count, values: string[], generated_at }."}}},"example":{"filter":"industries","canonical_filter":"b2b-industries","source":"live","count":145,"generated_at":"2026-05-03T19:18:38.822Z","values":["Accounting","Agriculture","Airlines/Aviation","..."]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/leads/find-by-name":{"post":{"operationId":"find-by-name","summary":"Find lead by name and domain","description":"Look up a B2B lead by first name, last name, and company domain.\n\nBeta: contract may change.","tags":["Leads"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"1 lead credit — only deducted on a successful match.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":false,"x-beta":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"first_name":{"type":"string","description":"First name"},"last_name":{"type":"string","description":"Last name"},"domain":{"type":"string","description":"Company domain (e.g. acme.com)"}},"required":["first_name","last_name","domain"]},"example":{"first_name":"John","last_name":"Doe","domain":"acme.com"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"email":{"type":["string","null"],"description":"Work email"},"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"job_title":{"type":["string","null"]},"seniority":{"type":["string","null"],"description":"Seniority label as recorded (e.g. Director, Manager, C-Suite), or null. Not normalized, so casing and spelling can vary"},"departments":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Departments as recorded (e.g. Marketing, Sales), string array, or null when unknown"},"company_name":{"type":["string","null"]},"company_domain":{"type":["string","null"]},"industry":{"type":["string","null"],"description":"Company industry, or null when unknown"},"linkedin_url":{"type":["string","null"],"description":"LinkedIn URL"},"city":{"type":["string","null"],"description":"City, or null when unknown"},"state":{"type":["string","null"],"description":"State / region, or null when unknown"},"country":{"type":["string","null"],"description":"Country, or null when unknown"},"employee_count":{"type":["number","null"],"description":"Company employee count, or null when unknown"},"technologies":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Technologies the company’s website uses (Wappalyzer-style names), or null when unknown"},"mobile_phone":{"description":"Personal mobile phone if known, else null"},"company_phone":{"type":["string","null"],"description":"Company phone line if known, else null"},"email_status":{"type":["string","null"],"description":"Email verification status: valid / risky / invalid / unknown / unverified"},"verified_at":{"type":["string","null"],"description":"When the email was last verified, or null when unknown"}}},"credits_used":{"type":["number","null"]}}},"example":{"success":true,"data":{"email":"john@acme.com","first_name":"John","last_name":"Doe","job_title":"CEO","seniority":"C-Suite","departments":["C-Suite"],"company_name":"Acme Inc","company_domain":"acme.com","industry":"Technology","linkedin_url":"https://linkedin.com/in/johndoe","city":"San Francisco","state":"California","country":"United States","employee_count":250,"technologies":["Shopify","HubSpot"],"mobile_phone":null,"company_phone":"+14155550100","email_status":"valid","verified_at":"2026-05-10T12:00:00.000Z"},"credits_used":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/leads/enrich":{"post":{"operationId":"enrich","summary":"Enrich lead by email","description":"Given an email address, return the full enriched B2B lead profile, including a company object (funding, traffic, HQ, followers, specialties) when the lead is linked to a company.","tags":["Leads"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"1 lead credit — only deducted on a successful match.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"email":{"type":"string","description":"Work email address"}},"required":["email"]},"example":{"email":"john@acme.com"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"email":{"type":["string","null"]},"first_name":{"type":["string","null"],"description":"First name"},"last_name":{"type":["string","null"],"description":"Last name"},"job_title":{"type":["string","null"],"description":"Job title"},"seniority":{"type":["string","null"],"description":"Seniority label as recorded (e.g. Director, Manager, C-Suite), or null. Not normalized, so casing and spelling can vary"},"departments":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Departments as recorded (e.g. Marketing, Sales), string array, or null when unknown"},"company_name":{"type":["string","null"],"description":"Company"},"company_domain":{"type":["string","null"]},"industry":{"type":["string","null"],"description":"Company industry, or null when unknown"},"linkedin_url":{"type":["string","null"]},"city":{"type":["string","null"],"description":"City, or null when unknown"},"state":{"type":["string","null"],"description":"State / region, or null when unknown"},"country":{"type":["string","null"],"description":"Country, or null when unknown"},"employee_count":{"type":["number","null"],"description":"Company employee count, or null when unknown"},"technologies":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Technologies the company’s website uses (Wappalyzer-style names), or null when unknown"},"mobile_phone":{"description":"Personal mobile phone if known, else null"},"company_phone":{"type":["string","null"],"description":"Company phone line if known, else null"},"email_status":{"type":["string","null"],"description":"Email verification status: valid / risky / invalid / unknown / unverified"},"verified_at":{"type":["string","null"],"description":"When the email was last verified, or null when unknown"},"company":{"type":["object","null"],"properties":{"funding_stage":{"type":["string","null"],"description":"Funding stage if known, else null"},"total_funding_usd":{"type":["number","null"],"description":"Company total funding in USD if known, else null"},"monthly_web_visits":{"type":["number","null"],"description":"Monthly website visits if known, else null"},"operating_status":{"type":["string","null"],"description":"Operating status if known, else null"},"hq_city":{"type":["string","null"],"description":"Headquarters city if known, else null"},"hq_state":{"type":["string","null"],"description":"Headquarters state if known, else null"},"hq_country":{"type":["string","null"],"description":"Headquarters country code if known, else null"},"linkedin_followers":{"type":["number","null"],"description":"Company LinkedIn follower count if known, else null"},"specialties":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Company specialties (string[]), or null when unknown"}},"description":"Company-level profile, or null when the lead is not linked to a company. Not filterable. Each nested field is null when unknown."}}},"credits_used":{"type":["number","null"]}}},"example":{"success":true,"data":{"email":"john@acme.com","first_name":"John","last_name":"Doe","job_title":"CEO","seniority":"C-Suite","departments":["C-Suite"],"company_name":"Acme Inc","company_domain":"acme.com","industry":"Technology","linkedin_url":"https://linkedin.com/in/johndoe","city":"San Francisco","state":"California","country":"United States","employee_count":250,"technologies":["Shopify","HubSpot"],"mobile_phone":null,"company_phone":"+14155550100","email_status":"valid","verified_at":"2026-05-10T12:00:00.000Z","company":{"funding_stage":"series_b","total_funding_usd":25000000,"monthly_web_visits":1200000,"operating_status":"active","hq_city":"San Francisco","hq_state":"California","hq_country":"US","linkedin_followers":54000,"specialties":["SaaS","B2B"]}},"credits_used":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/enrich/linkedin":{"post":{"operationId":"enrich-linkedin","summary":"Enrich by LinkedIn URL (live)","description":"Enrich a person by their LinkedIn profile URL → a verified work email plus their US mobile phone as a free add-on. Cache-first: served instantly from our database when we already hold a verified email for that profile, otherwise enriched live via the provider and cached for next time.","tags":["Leads"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"1 lead credit — charged only when an email is returned. Phone is a free add-on; phone-only or no-email responses are free.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"linkedin_url":{"type":"string","description":"LinkedIn profile URL (e.g. https://linkedin.com/in/johndoe)"}},"required":["linkedin_url"]},"example":{"linkedin_url":"https://linkedin.com/in/johndoe"}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"source":{"type":["string","null"],"description":"\"cache\" (served from our database) or \"live\" (freshly enriched)"},"credits_used":{"type":["number","null"]},"data":{"type":["object","null"],"properties":{"email":{"type":["string","null"],"description":"Verified work email"},"email_status":{"type":["string","null"],"description":"Canonical verification status: valid / risky / invalid / unknown / unverified. Live responses return \"unknown\" today — the result lands as \"valid\" or \"invalid\" once it is re-verified."},"phone":{"type":["string","null"],"description":"US mobile phone if found, else null (free add-on)"},"company_domain":{"type":["string","null"],"description":"Company domain (derived from the email on a live miss-fill)"},"person_linkedin_url":{"type":["string","null"],"description":"The matched LinkedIn URL"},"first_name":{"description":"Profile fields (first_name, last_name, full_name, job_title, company_name, company_linkedin_url, industry, city, state, country, technologies) are also returned on a cache hit; each is null when unknown"},"full_name":{"description":"Full name — cache hits only"},"company_linkedin_url":{"description":"Company LinkedIn URL — cache hits only"},"technologies":{"description":"Technologies the company’s website uses (Wappalyzer-style names), string[] or null when unknown — cache hits only"}}}}},"example":{"success":true,"source":"live","credits_used":1,"data":{"email":"john@acme.com","email_status":"unknown","phone":"+14155550100","company_domain":"acme.com","person_linkedin_url":"https://linkedin.com/in/johndoe"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/leads/search":{"post":{"operationId":"leads-search","summary":"Search B2B leads","description":"Search the B2B lead database with filters on title, industry, location, company size, and more. Results are always restricted to leads carrying our verified email label — there is no option to include unverified leads.\n\nRetries: responses do not currently carry an `artifact_id`, so de-duplicate on your side rather than relying on replay or re-fetch.","tags":["Leads"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"x-consulti-credits":"1 lead credit per result returned. Reserved up to `size`, then refunded down to the actual row count. 0 results = 0 credits. A retried request is a new billable search. An `Idempotency-Key` header is accepted but has no effect at present, so de-duplicate on your side rather than relying on replay. A 503 means the search could not run right now: code search_unavailable when the database was busy or a connection dropped, and code search_timeout when the query ran too long, so narrow the filters. Both carry a Retry-After header in seconds. The reserved credits are refunded, but the body has no credits_used, so check GET /credits before you retry.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"q":{"type":"string","description":"Free-text search across job title, company name, and industry only. Does not match person names; use POST /leads/find-by-name for that (Beta, REST-only)."},"titles":{"type":"array","items":{"type":"string"},"description":"Job titles to match (e.g. [\"CEO\", \"CTO\"])"},"industries":{"type":"array","items":{"type":"string"},"description":"Industries (case-sensitive exact match). See GET /api/v1/meta/industries for the full canonical list."},"technologies":{"type":"array","items":{"type":"string"},"description":"Technologies the company’s website uses, exact Wappalyzer-style names (e.g. [\"Shopify\", \"HubSpot\"]). OR-matched: a lead matches if it uses any listed technology. Max 25. See GET /api/v1/meta/technologies for the canonical list."},"excludeTechnologies":{"type":"array","items":{"type":"string"},"description":"Technologies to exclude, same Wappalyzer-style names. A lead is dropped if its company uses any listed technology; leads with no technology data are kept. Max 25."},"countries":{"type":"array","items":{"type":"string"},"description":"Countries. Alias-group members are equivalent (e.g. \"United States\" and \"US\"). See GET /api/v1/meta/countries."},"states":{"type":"array","items":{"type":"string"},"description":"States/regions. Alias-group members are equivalent (e.g. \"Ontario\" and \"ON\"). See GET /api/v1/meta/states."},"cities":{"type":"array","items":{"type":"string"},"description":"Cities to match"},"company":{"type":"string","description":"Company name substring"},"empMin":{"type":"number","description":"Minimum employee count"},"empMax":{"type":"number","description":"Maximum employee count"},"revenueMin":{"type":"number","description":"Minimum annual revenue in USD, whole dollars. Leads with no revenue data on file are excluded when either bound is set. Must be less than or equal to revenueMax when both are set."},"revenueMax":{"type":"number","description":"Maximum annual revenue in USD, whole dollars. Leads with no revenue data on file are excluded when either bound is set. Must be greater than or equal to revenueMin when both are set."},"excludeTitles":{"type":"array","items":{"type":"string"},"description":"Job titles to exclude. A lead is dropped if its job title matches any listed value. Max 25."},"excludeListId":{"type":"string","description":"UUID of a saved list you own, and it must be a LEAD list (the kind created by POST /lists with kind \"lead\"). Business, podcast, YouTube and Instagram lists hold no lead emails; passing one returns 400 rather than silently excluding nothing. GET /lists reports the kind of each list. Leads whose email is on that list are removed from the result set before billing, so excluded leads never cost credits. Because exclusion is applied in the query, request `page: 1` on every call and add each batch you receive to the list with POST /lists/{id}/add-leads — paging forward while excluding skips leads. Returns 400 if the list does not exist or is not owned by the API key holder."},"page":{"type":"number","description":"Page number (default: 1). When using excludeListId, keep this at 1 and grow the list instead."},"size":{"type":"number","description":"Results per page, max 100 (default: 25)"}}},"example":{"titles":["CEO","Founder"],"industries":["Computer Software"],"countries":["United States"],"empMin":10,"empMax":200,"revenueMin":1000000,"revenueMax":50000000,"excludeTitles":["intern","assistant"],"page":1,"size":25}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"leads":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"first_name":{"type":["string","null"]},"last_name":{"type":["string","null"]},"email":{"type":["string","null"],"description":"Lead email"},"job_title":{"type":["string","null"],"description":"Job title"},"seniority":{"type":["string","null"],"description":"Seniority label as recorded (e.g. Director, Manager, C-Suite), or null. Not normalized, so casing and spelling can vary"},"departments":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Departments as recorded (e.g. Marketing, Sales), string array, or null when unknown"},"company_name":{"type":["string","null"],"description":"Company"},"company_domain":{"type":["string","null"]},"linkedin_url":{"type":["string","null"]},"city":{"type":["string","null"]},"state":{"type":["string","null"],"description":"State / region, or null when unknown"},"country":{"type":["string","null"]},"employees":{"type":["number","null"]},"annual_revenue":{"type":["number","null"],"description":"Annual revenue in USD, whole dollars (`number | null`). Null when we hold no revenue data for that lead."},"industry":{"type":["string","null"]},"technologies":{"type":["array","null"],"items":{"type":["string","null"]},"description":"Technologies the company’s website uses (Wappalyzer-style names), or null when unknown"},"mobile_phone":{"description":"Personal mobile phone if known, else null"},"company_phone":{"type":["string","null"],"description":"Company phone line if known, else null"},"email_status":{"type":["string","null"],"description":"Email verification status: valid / risky / invalid / unknown / unverified"}}}},"total":{"type":["number","null"],"description":"Total matching leads before excludeListId is applied. Capped at 10001 — treat 10001 as \"more than 10,000\"."},"total_after_exclusion":{"type":["number","null"],"description":"Total after excludeListId is applied; equal to `total` when excludeListId is omitted. A LOWER value proves matching rows were excluded. An equal value is inconclusive, not a failure signal: it also occurs when the list holds no email matching this search, and when both counts saturate the 10001 cap. To confirm exclusion directly, re-request a page whose leads you just added to the list."},"page":{"type":["number","null"]},"size":{"type":["number","null"]},"has_more":{"type":["boolean","null"],"description":"true when this page was full - request the next page to continue; an empty page ends pagination"},"credits_used":{"type":["number","null"]}}},"example":{"leads":[{"first_name":"John","last_name":"Doe","email":"john@acme.com","job_title":"CEO","seniority":"C-Suite","departments":["C-Suite"],"company_name":"Acme Inc","company_domain":"acme.com","linkedin_url":"https://linkedin.com/in/johndoe","city":"San Francisco","state":"California","country":"United States","employees":150,"annual_revenue":12000000,"industry":"Computer Software","technologies":["Shopify","HubSpot"],"mobile_phone":null,"company_phone":"+14155550100","email_status":"valid"}],"total":1247,"total_after_exclusion":1247,"page":1,"size":25,"has_more":true,"credits_used":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Service Unavailable. code search_unavailable: the database was busy or a connection dropped. code search_timeout: the query ran too long, so narrow the filters. Wait the Retry-After seconds before retrying. The reserved credits are refunded, but the body has no credits_used, so check GET /credits before you retry.","headers":{"Retry-After":{"$ref":"#/components/headers/RetryAfter"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/local-leads/search":{"post":{"operationId":"local-leads-search","summary":"Search local businesses","description":"Search local businesses scraped from Google Maps, with verified business emails, ratings, and reviews. The email returned for a business is always one that passed verification — the verified-only filter is forced on and cannot be broadened.\n\nHistorical purchases require a separate repair.\n\nRetries: responses do not currently carry an `artifact_id`, so de-duplicate on your side rather than relying on replay or re-fetch.\n\nRate limited per API key and per source IP; a 429 carries Retry-After.\n\nBeta: contract may change.","tags":["Local"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"x-consulti-credits":"1 lead credit per result returned. Reserved up to `size`, then refunded down to the actual row count. 0 results = 0 credits. Successful paid results become business unlocks for your account, so saving those IDs costs no additional credits. A retried search remains billable. An `Idempotency-Key` header has no effect at present. A rare success response carries `warning.code` SEARCH_COMMIT_UNCONFIRMED: the results are paid and delivered, but the charge record is unconfirmed, so do not repeat the search and contact support with `warning.request_id`.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":false,"x-beta":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"q":{"type":"string","description":"Free-text search (name or category), max 200 chars. Matches singular/plural variants and related categories for common niches (e.g. \"restaurants\" includes every restaurant type, \"med spa\" includes Medical spa). A trailing \"near me\" is ignored."},"name":{"type":"string","description":"Business name substring"},"keywords":{"type":"array","items":{"type":"string"},"description":"OR-matched keywords, max 50, each max 100 chars. Each keyword also matches singular/plural variants and related Google Maps categories for common niches (e.g. \"commercial cleaners\" includes Janitorial service, \"hvac\" includes Air conditioning contractor)."},"states":{"type":"array","items":{"type":"string"},"description":"US states, DC, and Canadian provinces/territories. Accepts full names or 2-letter codes. See GET /api/v1/meta/local-states."},"cities":{"type":"array","items":{"type":"string"},"description":"Cities"},"zips":{"type":"array","items":{"type":"string"},"description":"ZIP codes"},"ratingMin":{"type":"number","description":"Minimum Google rating"},"ratingMax":{"type":"number","description":"Maximum Google rating"},"reviewsMin":{"type":"number","description":"Minimum review count"},"reviewsMax":{"type":"number","description":"Maximum review count"},"hasWeb":{"type":"boolean","description":"true: only businesses with a website. false: only businesses without one"},"hasPhone":{"type":"boolean","description":"Only return businesses with a phone number"},"hasOwnerEmail":{"type":"boolean","description":"Only return businesses whose returned email was derived from the owner’s name (email_type \"owner\") rather than a shared alias. A pattern classification, not a claim that the named person reads the mailbox. Set with hasRoleEmail to accept either."},"hasRoleEmail":{"type":"boolean","description":"Only return businesses whose returned email is a role address such as info@ or contact@ (email_type \"role\"). Set with hasOwnerEmail to accept either."},"hasOwner":{"type":"boolean","description":"Only return businesses that have a named owner on record. Filters on owner presence, which is not the same as the returned email belonging to that person — see owner_name below."},"excludeListId":{"type":"string","description":"UUID of a saved list you own, and it must be a business list (kind \"business\"). GET /lists reports the kind of each list. Businesses on that list are removed from the result set before billing, so excluded rows never cost credits. A blank or malformed id, a list that does not exist or is not yours, and a list of another kind each return 400. A list with more than 100,000 members returns 422 (code exclusion_too_large), and a temporary lookup failure returns 503 (code list_resolution_unavailable). All of these are returned before anything is reserved, with credits_used: 0. Because exclusion is applied in the query, a harvest loop that adds each batch to the list should request page 1 on every call."},"page":{"type":"number","description":"Page number (default: 1)"},"size":{"type":"number","description":"Results per page, max 100 (default: 25)"}}},"example":{"keywords":["plumber"],"states":["Texas"],"ratingMin":4,"hasOwnerEmail":true,"page":1,"size":25}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"businesses":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"id":{"type":["number","null"],"description":"Business identifier"},"name":{"type":["string","null"],"description":"Business name"},"phone":{"type":["string","null"],"description":"Phone if known, else null"},"website":{"type":["string","null"],"description":"Website if known, else null"},"email":{"type":["string","null"],"description":"A business email that passed verification. May be an owner-name-derived address or a role-based one (info@, contact@) — read email_type to tell which, rather than assuming."},"address":{"type":["string","null"],"description":"Street address if known, else null"},"city":{"type":["string","null"],"description":"City if known, else null"},"state":{"type":["string","null"],"description":"State / province if known, else null"},"zip":{"type":["string","null"],"description":"Postal code if known, else null"},"rating":{"type":["number","null"],"description":"Google rating if known, else null"},"reviews":{"type":["number","null"],"description":"Google review count if known, else null"},"category":{"type":["string","null"],"description":"Google Maps category if known, else null"},"google_maps_url":{"type":["string","null"],"description":"Google Maps listing URL if known, else null"},"scraped_at":{"type":["string","null"],"description":"When this business was FIRST recorded, if known, else null — it never moves again. A re-scrape refreshes only name, rating and reviews; phone, website, address, category and google_maps_url keep their originally-scraped values indefinitely and no field reports their age. Read this as record age, and re-verify contact details independently rather than inferring freshness."},"email_type":{"type":["string","null"],"description":"How the address was derived, not who reads it. \"owner\" means it matches a pattern built from the owner’s name (jane@, jane.doe@, jdoe@) and carries our verified label; \"role\" means a shared pattern such as info@ or contact@. An \"owner\" address is a name-derived address that passed verification — that is a verified label, NOT a deliverability guarantee (it is catch-all-blind and can be stale), and NOT proof that a person owns or reads the mailbox. Use it to rank and to decide whether personalising is sensible, not as an identity claim."},"email_status":{"type":["string","null"],"description":"Verification verdict for the returned email. Always \"safe\" while the verified-only filter is forced on — included for parity with the CSV export, whose selection is user-controllable."},"verified_at":{"type":["string","null"],"description":"When that email was last verified, if known, else null"},"owner_name":{"type":["string","null"],"description":"The owner on record for the BUSINESS — not verified as the person who owns or uses the returned email. Present for 82.7% of returned businesses (289,129 / 349,630, measured 2026-07-25); null when unknown. Check email_type before merging this into a greeting: 8.8% of rows (30,603 / 349,630) pair a known owner name with a role address like info@. A role address is a shared alias and we do not track who reads it, so on those rows the owner name is unconfirmed for greeting purposes — the figure sizes that cohort, it is not a measured mis-delivery rate. All figures are database-wide snapshots, not per-query guarantees — a narrow filter can return far fewer. Resolved by a second lookup, so in rare cases a row matched by hasOwner can still come back with a null owner."},"owner_title":{"type":["string","null"],"description":"The owner’s role at the business (e.g. \"Owner\", \"President\") if known, else null. Same caveats as owner_name."}}}},"total":{"type":["number","null"],"description":"Approximate size of the matching set, capped at 10001. May be null when the count exceeds its internal time budget — the results themselves are unaffected, so use has_more to paginate."},"page":{"type":["number","null"],"description":"The page actually used — the requested value after normalization (floored, minimum 1)"},"size":{"type":["number","null"],"description":"The page size actually used — the requested value after normalization (floored, clamped to 1–100). Read it back rather than assuming your requested size was honoured."},"has_more":{"type":["boolean","null"],"description":"true when this page was full - request the next page to continue; an empty page ends pagination. Paginate on this, not on total."},"credits_used":{"type":["number","null"],"description":"Lead credits actually charged — equal to the number of rows returned. Always present on success, and returned as 0 on the rate-limit, tier and pagination-depth errors. On a 402 or a 5xx the key may be absent, and its absence is UNSPECIFIED — it does not assert that nothing was charged, because a reservation may already have been taken and the automatic refund can itself fail. Reconcile against GET /credits before retrying either; a blind retry reserves again."},"total_after_exclusion":{"description":"Present only when excludeListId is sent. Matching rows after the exclusion, counted up to 10001; null when the count times out. When excludeListId is sent, `total` is counted the same way (up to 10001, nullable). A lower value proves rows were excluded; an equal value is inconclusive."},"exclusion_ignored_members":{"description":"Present only when excludeListId is sent and some list members cannot match any record in this database (for example a malformed id). Those members are skipped."},"warning":{"description":"Absent on almost every response. Present with `code: \"SEARCH_COMMIT_UNCONFIRMED\"`, `request_id` and `message` when the results were charged and delivered but we could not confirm the charge record. `credits_used` is still what you paid. Do not repeat the search, since that buys the rows again, and contact support with `request_id`. The rows are still unlocked for your account, so saving them is normally free. In the rarer case where that unlock could not be written either, `message` says a save may charge again: contact support before saving those IDs."}}},"example":{"businesses":[{"id":12345,"name":"Acme Plumbing","phone":"+1-512-555-0100","website":"https://acmeplumbing.com","email":"owner@acmeplumbing.com","address":"123 Main St","city":"Austin","state":"TX","zip":"78701","rating":4.8,"reviews":127,"category":"Plumber","google_maps_url":"https://maps.google.com/?cid=12345","scraped_at":"2026-07-01T12:00:00.000Z","email_type":"owner","email_status":"safe","verified_at":"2026-07-02T09:30:00.000Z","owner_name":"Jane Doe","owner_title":"Owner"}],"total":892,"page":1,"size":25,"has_more":true,"credits_used":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"Unprocessable: the excludeListId list has over 100,000 members (code exclusion_too_large). Rejected before anything is reserved, credits_used 0.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Service Unavailable: the excludeListId list could not be looked up (code list_resolution_unavailable). Rejected before anything is reserved, credits_used 0.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/creator-leads/search":{"post":{"operationId":"creator-leads-search","summary":"Search creator leads (YouTube & Podcast)","description":"Search podcasts and YouTube channels with contact emails, subscriber counts, and categories. Every result is restricted to a verified email — podcasts to a verified (non-catch-all) status, YouTube channels to those with a valid, non-catch-all verification verdict. Unverified, catch-all, and invalid emails are never returned.\n\nRetries: responses do not currently carry an `artifact_id`, so de-duplicate on your side rather than relying on replay or re-fetch.\n\nRate limited per API key and per source IP; a 429 carries Retry-After.\n\nBeta: contract may change.","tags":["Creators"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"x-consulti-credits":"1 lead credit per result returned. Reserved up to `size`, then refunded down to the actual row count. 0 results = 0 credits. A retried request is a new billable search. An `Idempotency-Key` header is accepted but has no effect at present, so de-duplicate on your side rather than relying on replay.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":false,"x-beta":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"source":{"enum":["youtube","podcast"],"description":"Which creator type to search"},"q":{"type":"string","description":"Free-text keyword search"},"categories":{"type":"array","items":{"type":"string"},"description":"Podcast category filters (exact match against the categories[] column). See GET /api/v1/meta/creator-podcast-categories."},"niches":{"type":"array","items":{"type":"string"},"description":"YouTube niche filters (fuzzy ILIKE match). See GET /api/v1/meta/creator-youtube-niches for common values."},"country":{"type":"string","description":"YouTube country filter — comma-separated ISO codes (e.g. \"US,GB\"). See GET /api/v1/meta/creator-countries."},"language":{"type":"string","description":"Podcast language filter (e.g. \"en\"). See GET /api/v1/meta/creator-languages."},"minSubscribers":{"type":"number","description":"Minimum subscriber count (YouTube)"},"maxSubscribers":{"type":"number","description":"Maximum subscriber count (YouTube)"},"minEpisodes":{"type":"number","description":"Minimum episode count (podcasts)"},"excludeListId":{"type":"string","description":"UUID of a saved list you own, and it must be a list whose kind matches `source`: kind \"youtube\" for source \"youtube\", kind \"podcast\" for source \"podcast\". GET /lists reports the kind of each list. Creators on that list are removed from the result set before billing, so excluded rows never cost credits. A blank or malformed id, a list that does not exist or is not yours, and a list of another kind each return 400. A list with more than 100,000 members returns 422 (code exclusion_too_large), and a temporary lookup failure returns 503 (code list_resolution_unavailable). All of these are returned before anything is reserved, with credits_used: 0."},"page":{"type":"number","description":"Page number (default: 1)"},"size":{"type":"number","description":"Results per page, max 100 (default: 25)"}},"required":["source"]},"example":{"source":"youtube","niches":["Marketing"],"minSubscribers":10000,"page":1,"size":25}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"creators":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"id":{"type":["string","null"],"description":"Creator identifier (prefixed yt_ for YouTube, pod_ for podcasts)"},"name":{"type":["string","null"],"description":"Channel / podcast name"},"email":{"type":["string","null"],"description":"Contact email"},"source":{"type":["string","null"],"description":"\"youtube\" or \"podcast\""},"niche":{"type":["string","null"],"description":"YouTube channels only: the channel niche"},"country":{"type":["string","null"],"description":"Country code (YouTube)"},"subscribers":{"type":["number","null"],"description":"Subscribers (YouTube)"},"website":{"type":["string","null"],"description":"Website URL if known, else null"},"category":{"description":"Podcasts only: the primary podcast category (returned in place of niche)"},"episodes":{"description":"Podcasts only: episode count, or null when unknown (returned in place of subscribers)"},"language":{"description":"Podcasts only: the podcast language"}}}},"total":{"type":["number","null"]},"page":{"type":["number","null"]},"size":{"type":["number","null"]},"has_more":{"type":["boolean","null"],"description":"true when this page was full - request the next page to continue; an empty page ends pagination"},"credits_used":{"type":["number","null"]},"total_after_exclusion":{"description":"Present only when excludeListId is sent. Matching rows after the exclusion, counted up to 10001; null when the count times out. When excludeListId is sent, `total` is counted the same way (up to 10001, nullable). A lower value proves rows were excluded; an equal value is inconclusive."},"exclusion_ignored_members":{"description":"Present only when excludeListId is sent and some list members cannot match any record in this database (for example a malformed id). Those members are skipped."}}},"example":{"creators":[{"id":"yt_UC123abc","name":"Marketing Weekly","email":"hello@marketingweekly.com","source":"youtube","niche":"Marketing","country":"US","subscribers":125000,"website":"https://marketingweekly.com"}],"total":1840,"page":1,"size":25,"has_more":true,"credits_used":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"Unprocessable: the excludeListId list has over 100,000 members (code exclusion_too_large). Rejected before anything is reserved, credits_used 0.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Service Unavailable: the excludeListId list could not be looked up (code list_resolution_unavailable). Rejected before anything is reserved, credits_used 0.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/artifacts/{id}":{"get":{"operationId":"artifacts-get","summary":"Retrieve a stored search result","description":"NOT CURRENTLY ENABLED: billed searches do not return an `artifact_id` today and this endpoint returns 404 for every id. The contract below is what it serves once the feature is switched on. Re-fetch the result of a billed search you already paid for, using the `artifact_id` returned by /leads/search, /local-leads/search, or /creator-leads/search. Built for the case where the original response was lost — a dropped connection, a client timeout, a crashed consumer — so you do not have to re-run and re-pay for the same search. Artifacts are retained for 30 days and are tied to your user account, not to the API key that created them, so they survive key rotation and plan changes.\n\nErrors: 404 for an unknown, foreign, or unfinished artifact (a foreign id is deliberately 404, not 403); 410 once past the 30-day retention window. Not exposed through MCP while Beta.\n\nBeta: contract may change.","tags":["Leads"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The artifact_id returned by a billed search response.","schema":{"type":"string"}}],"x-consulti-credits":"Free. Retrieval is never billed — the search that produced the artifact was already charged.","x-consulti-billable":false,"x-consulti-tier":"pro+","x-consulti-mcp":false,"x-beta":true,"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"artifact_id":{"type":["string","null"]},"endpoint":{"type":["string","null"]},"created_at":{"type":["string","null"]},"expires_at":{"type":["string","null"],"description":"When this artifact is deleted (30 days after creation). A request after this returns 410."},"row_count":{"type":["number","null"]},"credits_charged":{"type":["number","null"],"description":"Credits the original search cost"},"result":{"type":["object","null"],"properties":{"leads":{"type":["array","null"],"items":{}},"total":{"type":["number","null"]},"page":{"type":["number","null"]},"size":{"type":["number","null"]},"has_more":{"type":["boolean","null"]},"credits_used":{"type":["number","null"]}},"description":"The original search response, byte-for-byte as it was first returned"}}},"example":{"artifact_id":"0f9d2c6a-2b47-4c5e-9a10-7b2c8d1e4f30","endpoint":"/api/v1/leads/search","created_at":"2026-08-03T12:00:00.000Z","expires_at":"2026-09-02T12:00:00.000Z","row_count":25,"credits_charged":25,"result":{"leads":[],"total":1247,"page":1,"size":25,"has_more":true,"credits_used":25}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"410":{"description":"Gone: the artifact is past the 30-day retention window.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/placement-tests":{"post":{"operationId":"placement-tests-create","summary":"Create an inbox placement test","description":"Create an inbox placement test. Checking starts when the test is created, so there is no second call to make: send one email from the mailbox you want to test to every seed address within 15 minutes, with the test code anywhere in the subject or the body, then poll GET /placement-tests/{id}. Results usually arrive within about 2 minutes of sending. A test can complete before expires_at, 2 minutes after the last arrival, with the seeds still missing reported as not found. Until expires_at, such a seed is corrected if the email reaches it, and the score is rebuilt. Results are final at expires_at. If the test could not be started, the response says status \"draft\" with a next_step: call POST /placement-tests/{id}/sent after sending. If that draft already has a sent_at, its window runs from sent_at and closes at expires_at. At most 3 of your tests can be checking at once; another create is a 429 with code too_many_open_tests and a Retry-After header, is not charged, and is safe to retry.","tags":["Deliverability"],"security":[{"bearerAuth":[]}],"parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"x-consulti-credits":"1 lead credit per test, charged when the test is created. Refunded if creation fails. Not refunded for poor placement results. A retried request creates, and charges for, a new test. An `Idempotency-Key` header is accepted but has no effect at present, so de-duplicate on your side rather than relying on replay.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"Optional label for the test (max 120 characters). Defaults to the subject."},"subject":{"type":"string","description":"Optional subject of the email you will send (max 998 characters). Used for the content check when no seed returns a body."},"body":{"type":"string","description":"Optional body of the email you will send (max 100,000 characters), for the same purpose."}}},"example":{"name":"October outreach","subject":"Quick question about your Q4 hiring","body":"Hi Sam, ..."}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"status":{"type":["string","null"],"description":"checking: the test is being watched, send your email now. draft: it could not be started; follow data.next_step."},"test_code":{"type":["string","null"],"description":"Put this anywhere in the subject or the body of the email you send."},"seed_addresses":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"email":{"type":["string","null"]},"provider":{"type":["string","null"]}}},"description":"Send one email to every one of these addresses. provider is google, microsoft, yahoo or other."},"seed_list":{"type":["string","null"],"description":"The same addresses as one comma-separated string, ready to paste into a To or Bcc field."},"instructions":{"type":["array","null"],"items":{"type":["string","null"]}},"created_at":{"type":["string","null"]},"sent_at":{"type":["string","null"]},"expires_at":{"type":["string","null"],"description":"When results are final. A test can complete earlier and still correct a seed reported missing until then. Present when status is checking."},"next_step":{"description":"Present only when status is draft."}}},"credits_used":{"type":["number","null"]},"code":{"description":"On a 429 from the open-test cap: too_many_open_tests (the rate limit's 429 has no code). Nothing was charged; retry after Retry-After seconds."}}},"example":{"success":true,"data":{"id":"6b1f0c2e-5a3d-4f7e-9c21-0d8e4b7a9f10","name":"October outreach","status":"checking","test_code":"IPT-abc12xyz","seed_addresses":[{"email":"seed1@example.com","provider":"google"},{"email":"seed2@example.com","provider":"microsoft"}],"seed_list":"seed1@example.com,seed2@example.com","instructions":["Send one email from the mailbox you want to test to every address in seed_addresses (To, Cc or Bcc). seed_list holds the same addresses as one comma-separated string.","..."],"created_at":"2026-10-04T12:00:00.000Z","sent_at":"2026-10-04T12:00:00.400Z","expires_at":"2026-10-04T12:30:00.400Z"},"credits_used":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}},"get":{"operationId":"placement-tests-list","summary":"List placement tests","description":"Your placement tests, newest first. Only tests your account created are listed.","tags":["Deliverability"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"page","in":"query","required":false,"description":"Page number, from 1 (default 1).","schema":{"type":"number"}},{"name":"size","in":"query","required":false,"description":"Tests per page, 1 to 100 (default 25).","schema":{"type":"number"}}],"x-consulti-credits":"Free.","x-consulti-billable":false,"x-consulti-tier":"pro+","x-consulti-mcp":true,"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"tests":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"status":{"type":["string","null"]},"created_at":{"type":["string","null"]},"completed_at":{"type":["string","null"]},"report_status":{"type":["string","null"]},"score":{"type":["number","null"]}}}},"page":{"type":["number","null"]},"size":{"type":["number","null"]},"has_more":{"type":["boolean","null"],"description":"True when another page exists."}}},"credits_used":{"type":["number","null"]}}},"example":{"success":true,"data":{"tests":[{"id":"6b1f0c2e-5a3d-4f7e-9c21-0d8e4b7a9f10","name":"October outreach","status":"completed","created_at":"2026-10-04T12:00:00.000Z","completed_at":"2026-10-04T12:31:00.000Z","report_status":"ready","score":8.5}],"page":1,"size":25,"has_more":false},"credits_used":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/placement-tests/{id}/sent":{"post":{"operationId":"placement-tests-sent","summary":"Start checking a draft placement test","description":"Optional. A test created through the API is already checking, and calling this on a checking test changes nothing: it returns the current status and sent_at, so it can never extend the window. Call it when the create response said status \"draft\", once the email has gone out; checking then starts. A completed test is a 409 with code already_finished; create a new test to check again. A failed test is re-checked for free, but only while fewer than 3 of your tests are checking; otherwise it is the same 429 too_many_open_tests as a create, with Retry-After, and the test stays failed. A test whose creation failed and was refunded is a 409 with code not_charged and cannot be started. Another account's test, or an id that does not exist, is a 404.","tags":["Deliverability"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The test id returned by POST /placement-tests.","schema":{"type":"string"}}],"x-consulti-credits":"Free. The test was charged when it was created.","x-consulti-billable":false,"x-consulti-tier":"pro+","x-consulti-mcp":true,"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"id":{"type":["string","null"]},"status":{"type":["string","null"]},"sent_at":{"type":["string","null"],"description":"When checking started. Set once and never moved by a later call."},"expires_at":{"type":["string","null"]}}},"credits_used":{"type":["number","null"]}}},"example":{"success":true,"data":{"id":"6b1f0c2e-5a3d-4f7e-9c21-0d8e4b7a9f10","status":"checking","sent_at":"2026-10-04T12:03:00.000Z","expires_at":"2026-10-04T12:33:00.000Z"},"credits_used":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"409":{"description":"Conflict: the test already finished (code already_finished; create a new test to check again), or a draft that was never charged cannot be started (code not_charged).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/placement-tests/{id}":{"get":{"operationId":"placement-tests-get","summary":"Get a placement test","description":"Status, per-seed placement, a summary of where the email landed, and the 0 to 10 score and full report once built. Each seed shows where the email landed (inbox, spam, promotions and other tabs, not_found), delivery times, and the SPF, DKIM and DMARC verdicts that seed recorded. report_status is pending while the report is being built, ready once it is stored, and unavailable when it could not be built. Another account's test, or an id that does not exist, is a 404.","tags":["Deliverability"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"The test id returned by POST /placement-tests.","schema":{"type":"string"}}],"x-consulti-credits":"Free.","x-consulti-billable":false,"x-consulti-tier":"pro+","x-consulti-mcp":true,"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"success":{"type":["boolean","null"]},"data":{"type":["object","null"],"properties":{"id":{"type":["string","null"]},"name":{"type":["string","null"]},"status":{"type":["string","null"],"description":"draft, checking, completed or failed."},"created_at":{"type":["string","null"]},"completed_at":{"type":["string","null"]},"test_code":{"type":["string","null"]},"sent_at":{"type":["string","null"]},"expires_at":{"type":["string","null"]},"score":{"type":["number","null"],"description":"The 0 to 10 placement score, the one the app shows. Null until report_status is ready."},"summary":{"type":["object","null"],"properties":{"seeds":{"type":["number","null"]},"inbox":{"type":["number","null"]},"spam":{"type":["number","null"]},"missing":{"type":["number","null"]},"archived":{"type":["number","null"]},"undetermined":{"type":["number","null"]},"error":{"type":["number","null"]},"pending":{"type":["number","null"]},"tabs":{"type":["object","null"],"properties":{"primary":{"type":["number","null"]},"promotions":{"type":["number","null"]},"updates":{"type":["number","null"]},"social":{"type":["number","null"]},"forums":{"type":["number","null"]},"focused":{"type":["number","null"]},"other":{"type":["number","null"]}}},"inbox_rate":{"type":["number","null"]}},"description":"Seed counts: inbox (every tab counts, broken down in tabs), spam, missing (not found when the test completed, still corrected until expires_at if the email arrives), archived, undetermined, error, and pending (no result yet while the test is checking). inbox_rate is a percent of the seeds read."},"seeds":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"mailbox":{"type":["string","null"]},"provider":{"type":["string","null"],"description":"google, microsoft, yahoo or other."},"placement":{"type":["string","null"],"description":"inbox, primary, promotions, updates, social, forums, spam, archived, not_found, unknown or error."},"sub_placement":{"type":["string","null"]},"received_at":{"type":["string","null"]},"delivered_at":{"type":["string","null"]},"discovered_at":{"type":["string","null"]},"spf":{"type":["string","null"]},"dkim":{"type":["string","null"]},"dmarc":{"type":["string","null"]}}}},"report_status":{"type":["string","null"],"description":"pending, ready or unavailable once the test is completed; null before that."},"report":{"type":["object","null"],"properties":{"score":{"type":["number","null"]},"report_version":{"type":["number","null"]},"built_at":{"type":["string","null"]},"report":{"type":["object","null"],"properties":{"summary":{"type":["object","null"],"properties":{}},"providers":{"type":["object","null"],"properties":{}},"authentication":{"type":["object","null"],"properties":{}},"blacklist":{"type":["object","null"],"properties":{}},"content":{"type":["object","null"],"properties":{}},"domain_health":{"type":["object","null"],"properties":{}}}}},"description":"The built report: { score, report_version, built_at, report }. Null until report_status is ready."}}},"credits_used":{"type":["number","null"]}}},"example":{"success":true,"data":{"id":"6b1f0c2e-5a3d-4f7e-9c21-0d8e4b7a9f10","name":"October outreach","status":"completed","created_at":"2026-10-04T12:00:00.000Z","completed_at":"2026-10-04T12:31:00.000Z","test_code":"IPT-abc12xyz","sent_at":"2026-10-04T12:00:00.400Z","expires_at":"2026-10-04T12:30:00.400Z","score":8.5,"summary":{"seeds":2,"inbox":1,"spam":1,"missing":0,"archived":0,"undetermined":0,"error":0,"pending":0,"tabs":{"primary":0,"promotions":1,"updates":0,"social":0,"forums":0,"focused":0,"other":0},"inbox_rate":50},"seeds":[{"mailbox":"seed1@example.com","provider":"google","placement":"promotions","sub_placement":"promotions","received_at":"2026-10-04T12:03:20.000Z","delivered_at":"2026-10-04T12:03:21.000Z","discovered_at":"2026-10-04T12:04:02.000Z","spf":"pass","dkim":"pass","dmarc":"pass"}],"report_status":"ready","report":{"score":8.5,"report_version":1,"built_at":"2026-10-04T12:33:00.000Z","report":{"summary":{},"providers":{},"authentication":{},"blacklist":{},"content":{},"domain_health":{}}}},"credits_used":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/instagram/search":{"post":{"operationId":"instagram-search","summary":"Search Instagram profiles","description":"Search a database of Instagram profiles with filters on follower count, category, location, account type, and email data quality. Results can be filtered by verification status; all listed filters are optional. Pagination depth is capped at 100,000 results to prevent deep-offset performance issues.","tags":["Instagram"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"1 lead credit per result returned. Reserved up to `size`, then refunded down to the actual row count. 0 results = 0 credits.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"q":{"type":"string","description":"Case-insensitive text match on username or full name"},"bioKeywords":{"type":"array","items":{"type":"string"},"description":"Keywords to search for in profile bios"},"excludeKeywords":{"type":"array","items":{"type":"string"},"description":"Exclude profiles whose bio contains any of these keywords"},"excludeAdultContent":{"type":"boolean","description":"Exclude profiles with adult/sexual content terms in their bio"},"followerMin":{"type":"number","description":"Minimum follower count"},"followerMax":{"type":"number","description":"Maximum follower count"},"followingMin":{"type":"number","description":"Minimum following count"},"followingMax":{"type":"number","description":"Maximum following count"},"postsMin":{"type":"number","description":"Minimum post count"},"postsMax":{"type":"number","description":"Maximum post count"},"categories":{"type":"array","items":{"type":"string"},"description":"Categories to filter by. See GET /api/v1/meta/instagram-categories for the canonical list."},"countries":{"type":"array","items":{"type":"string"},"description":"Countries to filter by (exact match). See GET /api/v1/meta/instagram-countries for the canonical list."},"accountTypes":{"type":"array","items":{"type":"string"},"description":"Account types to filter by (e.g. [\"business\", \"personal\"]). See GET /api/v1/meta/instagram-account-types for valid values."},"emailStatus":{"type":"array","items":{"type":"string"},"description":"Email verification status to filter by (e.g. [\"good\", \"risky\", \"bad\", \"catch_all\"]). See GET /api/v1/meta/instagram-email-statuses for valid values."},"verified":{"type":"boolean","description":"Filter to only verified accounts"},"hasEmail":{"type":"boolean","description":"Filter to only accounts with an email on file. Use emailStatus to filter by verification quality."},"hasPhone":{"type":"boolean","description":"Filter to only accounts with a phone number"},"hasWebsite":{"type":"boolean","description":"Filter to only accounts with a website"},"excludeListId":{"type":"string","description":"UUID of a saved list you own, and it must be an Instagram list (kind \"instagram\"). GET /lists reports the kind of each list. Profiles on that list are removed from the result set before billing, so excluded rows never cost credits. A blank or malformed id, a list that does not exist or is not yours, and a list of another kind each return 400. A list with more than 100,000 members returns 422 (code exclusion_too_large), and a temporary lookup failure returns 503 (code list_resolution_unavailable). All of these are returned before anything is reserved, with credits_used: 0."},"sortBy":{"type":"string","description":"Field to sort by (followers, posts, engagement, username)"},"sortOrder":{"type":"string","description":"Sort order (asc or desc)"},"page":{"type":"number","description":"Page number (default: 1). Keep (page - 1) * size ≤ 100,000 to avoid deep-offset performance issues."},"size":{"type":"number","description":"Results per page, max 100 (default: 25)"}}},"example":{"categories":["Digital creator","Photographer"],"followerMin":5000,"followerMax":100000,"hasEmail":true,"page":1,"size":25}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"username":{"type":["string","null"],"description":"Instagram username"},"full_name":{"type":["string","null"],"description":"Profile display name"},"bio":{"type":["string","null"],"description":"Profile bio"},"followers":{"type":["number","null"],"description":"Follower count"},"following_cnt":{"type":["number","null"],"description":"Following count"},"posts":{"type":["number","null"],"description":"Post count"},"category_std":{"type":["string","null"],"description":"Standardized category"},"email":{"type":["string","null"],"description":"Contact email if available, else null"},"email_verification_status":{"type":["string","null"],"description":"Email verification status (good / risky / bad / catch_all)"},"email_verified_at":{"type":["string","null"],"description":"ISO timestamp of email verification, if verified"},"phone":{"type":["string","null"],"description":"Phone number if available, else null"},"website":{"type":["string","null"],"description":"Website URL if available, else null"},"verified":{"type":["boolean","null"],"description":"Instagram verified badge status"},"is_business":{"type":["boolean","null"],"description":"Whether the account is a business account"},"is_private":{"type":["boolean","null"],"description":"Whether the account is private"},"country":{"type":["string","null"],"description":"Country code if inferred"},"engagement_pct":{"type":["number","null"],"description":"Engagement rate as a percentage"}}}},"total":{"type":["number","null"],"description":"Total matching profiles before pagination"},"page":{"type":["number","null"]},"size":{"type":["number","null"]},"has_more":{"type":["boolean","null"],"description":"true when this page was full - request the next page to continue; an empty page ends pagination"},"credits_used":{"type":["number","null"],"description":"Lead credits charged for this request"},"total_after_exclusion":{"description":"Present only when excludeListId is sent. Matching rows after the exclusion, counted up to 10001; null when the count times out. When excludeListId is sent, `total` is counted the same way (up to 10001, nullable). A lower value proves rows were excluded; an equal value is inconclusive."},"exclusion_ignored_members":{"description":"Present only when excludeListId is sent and some list members cannot match any record in this database (for example a malformed id). Those members are skipped."}}},"example":{"data":[{"username":"john_photography","full_name":"John Smith","bio":"Professional photographer | Travel & lifestyle","followers":15400,"following_cnt":342,"posts":287,"category_std":"Photography","email":"john@photography.com","email_verification_status":"good","email_verified_at":"2026-05-10T12:00:00Z","phone":"+1-415-555-0100","website":"https://johnphoto.com","verified":true,"is_business":true,"is_private":false,"country":"US","engagement_pct":3.2}],"total":1247,"page":1,"size":25,"has_more":true,"credits_used":1}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"422":{"description":"Unprocessable: the excludeListId list has over 100,000 members (code exclusion_too_large). Rejected before anything is reserved, credits_used 0.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"},"503":{"description":"Service Unavailable: the excludeListId list could not be looked up (code list_resolution_unavailable). Rejected before anything is reserved, credits_used 0.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}},"/lists":{"get":{"operationId":"lists-get","summary":"Get saved lists","description":"Return all saved lead lists owned by the authenticated API user, with member counts.","tags":["Lists"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"Free.","x-consulti-billable":false,"x-consulti-tier":"pro+","x-consulti-mcp":true,"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"lists":{"type":["array","null"],"items":{"type":["object","null"],"properties":{"id":{"type":["string","null"],"description":"List ID (use for add-leads)"},"name":{"type":["string","null"],"description":"List name"},"description":{"type":["string","null"]},"created_at":{"type":["string","null"]},"member_count":{"type":["number","null"],"description":"Number of leads"}}}}}},"example":{"lists":[{"id":"uuid-1","name":"Q1 Prospects","description":"CEOs in SaaS","created_at":"2025-01-15T10:30:00Z","member_count":142}]}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}},"post":{"operationId":"lists-create","summary":"Create a list","description":"Create a new saved lead list.","tags":["Lists"],"security":[{"bearerAuth":[]}],"x-consulti-credits":"Free.","x-consulti-billable":false,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","description":"List name"},"description":{"type":"string","description":"Optional description"},"kind":{"type":"string","description":"List kind: lead (B2B), business (local), podcast, youtube, or instagram (default: lead)"}},"required":["name"]},"example":{"name":"Q1 Prospects","description":"CEOs in SaaS","kind":"lead"}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"type":"object","properties":{"list":{"type":["object","null"],"properties":{"id":{"type":["string","null"],"description":"New list ID"},"name":{"type":["string","null"]},"description":{"type":["string","null"]},"kind":{"type":["string","null"],"description":"List kind (lead, business, podcast, youtube, instagram)"},"created_at":{"type":["string","null"]}}}}},"example":{"list":{"id":"uuid-1","name":"Q1 Prospects","description":"CEOs in SaaS","kind":"lead","created_at":"2025-01-15T10:30:00Z"}}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}},"/lists/{id}/add-leads":{"post":{"operationId":"lists-add-leads","summary":"Add leads to a list","description":"Add members to a saved list by email (B2B leads) or business ID (local leads). Duplicates are ignored. The payload must match the list's kind: send emails to a kind=\"lead\" list and businessIds to a kind=\"business\" list. A mismatch on a list that already has members is rejected with 400 LIST_KIND_MISMATCH and nothing is written; an empty list takes the kind of its first payload. Create the list with the matching kind first. Send only one of the two arrays per call.","tags":["Lists"],"security":[{"bearerAuth":[]}],"parameters":[{"name":"id","in":"path","required":true,"description":"Path parameter: id","schema":{"type":"string"}}],"x-consulti-credits":"B2B email saves are free. Business IDs cost 1 lead credit per newly unlocked business with usable email data. IDs already unlocked by your account, including successful paid local searches, cost 0 additional credits. Duplicate IDs and businesses without email data do not cost credits.","x-consulti-billable":true,"x-consulti-tier":"pro+","x-consulti-mcp":true,"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"emails":{"type":"array","items":{"type":"string"},"description":"Array of B2B lead emails. Requires a list with kind=\"lead\""},"businessIds":{"type":"array","items":{"type":"number"},"description":"Up to 50,000 local business IDs. Requires a list with kind=\"business\". Unpurchased IDs can consume lead credits."}}},"example":{"emails":["john@acme.com","jane@acme.com"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"added":{"type":["number","null"],"description":"Leads added to list"},"skipped":{"type":["number","null"],"description":"Duplicates ignored"},"credits_used":{"description":"Business saves only: lead credits consumed by newly unlocked businesses."},"already_unlocked":{"description":"Business saves only: businesses with usable email data already owned by this account."}}},"example":{"added":2,"skipped":0}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"402":{"$ref":"#/components/responses/InsufficientCredits"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}}}}},"x-generated-from":"lib/api/endpoints-data.ts"}