# Innerfetch API > B2B data enrichment API built from the public web: company details, customers, partners, vendors, > competitors and people for a given company domain, each relationship with the public page that shows it. Authentication: send `X-API-Key: gtm_live_…` (or `Authorization: Bearer gtm_live_…`). Every metered response carries `X-Credits-Cost` (credits charged for this call) and `X-Credits-Remaining`. Errors are RFC 9457 problem+json. You are never charged for errors, 202s, 404s or 503s, with one exception: `404 company_not_found` from `/company/details` costs 1 credit. Responses use snake_case JSON. `domain` accepts a bare domain, a URL, or an email address. A company's old domain (one it moved away from) answers with the company under its current domain, at the same price. Freshness: company endpoints always answer from stored data at once (typically < 50 ms) and refresh data older than 30 days in the background; `fresh=true` queues that refresh now. `cache=only` reads stored data, also when stale, and never queues anything: a company not in the graph is a free `404` with code `not_in_graph`, and `cache=only` together with `fresh=true` is `400 conflicting_parameters`. A company never seen before is crawled while you wait (typically 5–20 s) and returns partial data; if that takes longer you get `202` with `Retry-After` and nothing is charged. `X-Enrichment-Status` says what you got: `complete`, `partial` (some fields are still being filled: industry, business model and similar companies may be missing), `refreshing`, `refresh_deferred` (stored data; your organization already has 50 refreshes waiting, so none was queued and `fresh=true` adds no credit) or `queued`. A `fresh=true` details call for a company whose refresh is already queued costs the normal price. Poll the free `/company/status` endpoint until it says `complete` instead of polling a paid endpoint. Or add `callback_url=https://…` to details, customers or competitors: once the company is `complete` (or `not_found`) we POST a Standard Webhooks message (`webhook-id`, `webhook-timestamp`, `webhook-signature`; body `{"type":"company.enrichment.completed","timestamp":…,"data":{"domain":…,"status":…}}`) signed with the secret from `/callbacks/secret`. Free; retried up to 6 times over about 9 hours; dropped after 24 h without `complete`; no redirects followed; at most 100 pending per organization (more is a free `429 callback_limit`). An organization may have at most 10 never-seen companies waiting for their first data (200 from `/company/batch`, which wait behind all other work); another one is answered `429` with `Retry-After` (free). Companies already in the graph, or already queued, never count. A domain whose crawl found no website answers `404` for a day (1 credit on `/company/details`, free elsewhere), then is crawled again. Queued work is shared out between organizations in turn. Trial organizations (made by a console sign-up, no payment yet) have lower limits until their first payment: 60 requests a minute per key, 3 never-seen companies waiting at once (3 from batches), 25 new never-seen companies a UTC day and 2 active keys. Over a limit the answer is the usual free `429` (`rate_limited` or `queue_full`) and its `detail` names the trial limit. A sign-up with a verified work e-mail gets 200 trial credits for 30 days, once per e-mail domain. ## Prices Errors, `202`, `404`, `429` and `503` answers cost no credits, except `404 company_not_found` from `/company/details`, which costs 1. A call is refused with `402` before any work when the balance is below its maximum price; a list (customers, vendors, people) is refused only below the price of one row per list, and is cut to what the balance pays, with `notice` and the next cursor. A batch then answers `insufficient_credits` for found entries past what the balance pays. | Endpoint | Credits | Free when | | --- | --- | --- | | `GET /api/v1/company/details` | 2 (+1 with `fresh=true` when it queues a refresh); 1 for `404 company_not_found` | `202` queued, `404 not_in_graph` with `cache=only` | | `GET /api/v1/company/customers` | 1 + 2 per row returned over all lists (`include=customers,partners`), at most 401 a page per list | `202` queued | | `GET /api/v1/company/vendors` | 1 + 2 per vendor returned, at most 401 a page | `404 not_in_graph`, an empty list held back because the company's domain is not verified | | `GET /api/v1/company/people` | 1 + 1 per person returned, at most 101 a page; executives in details are free | `202` queued, an empty list while `partial` | | `GET /api/v1/company/competitors` | 3 | `202` queued, an empty list while `partial` | | `POST /api/v1/company/batch` | 2 per distinct found domain (up to 50 entries) | `queued`, `queue_full`, `not_found`, `invalid` and duplicate entries | | `GET /api/v1/company/search` | 1 per started 50 rows returned | an empty answer | | `GET /api/v1/company/status` | 0 | | | `GET /api/v1/profile` | 2 (+1 with `fresh=true`) | no person found | | `POST /api/v1/work-email/lookup` | `valid` 2, `likely_valid_pattern` or `catch_all_domain` 1, `unknown` or `invalid` 0 | | | `GET /api/v1/credits`, `GET /api/v1/usage`, `GET /api/v1/pricing`, `GET /api/v1/billing/plans`, `POST /api/v1/billing/checkout` | 0 | | | `POST /mcp` (each tool) | the price of the endpoint the tool calls | the same cases | ## MCP - [MCP server](https://api.innerfetch.com/mcp): stateless Streamable HTTP, same key (`X-API-Key` or `Authorization: Bearer`), same prices, rate limit and error codes as REST. Tools: `company_details`, `company_customers`, `company_competitors`, `company_status`, `company_batch`, `company_search`, `company_vendors`, `company_people`, `profile_lookup`, `work_email_lookup`, `credits_balance`, `usage`. Each result is JSON with `data`, `credits_charged`, `credits_remaining` and `trace_id`; an error result has `code`. `GET /mcp/health` is free and needs no key. ## Pay per call without an account (x402) An agent without an API key can pay for each call in USDC on Base with x402 (version 2, scheme `exact`). Send the request without a key. A payable endpoint answers `402 payment_required` with a quote in the `PAYMENT-REQUIRED` header (base64 JSON; `accepts[0]` has `amount` in millionths of a USDC, `asset`, `payTo` and `network`). Sign it and send the same request again with the signed payment in `PAYMENT-SIGNATURE`. A billable answer costs the full quote, at 0.0145 USDC a credit: `/company/details` is 0.029 USDC. A list is quoted and charged for a full page of `max_results` or `limit` rows, so ask only for the rows you need. The payment is taken only when the answer costs credits: a `202`, an empty `partial` list, an error or another free answer takes nothing. Each signed payment is good for one call: sign a new one for every request, valid for at least 60 seconds and at most `maxTimeoutSeconds`, for exactly `amount` to `payTo`. A paid answer carries `PAYMENT-RESPONSE` (base64 JSON with the `transaction`). A billable answer is sent only after the payment is confirmed; when it cannot be confirmed in time the answer is `503` and the same payment must not be sent again. Limits without a key: 60 requests a minute per client address (`429` above it, nothing charged), 3 never-seen companies waiting at once and 50 new ones a UTC day, shared by all keyless callers, and 20 free answers a UTC day per paying wallet and per client address (then `402 free_answer_limit`). A wallet or client address whose payment fails after the check because the funds moved or the payment was canceled is refused for 24 hours (`402 payer_blocked`). Payable: company details, customers, vendors, people, competitors and search, public profile and work e-mail lookup. Batch, MCP, `callback_url` and the account endpoints need an API key. `GET /api/v1/pricing` has the terms in `pay_per_call`, and `/openapi.json` lists the payable endpoints with the USD price range of each in `x-payment-info`. Codes: `payment_required`, `payment_invalid` (the body's `reason` says why, for example `payment_already_used`) and `payment_failed`. ### MCP tools - `company_details`: Company profile: firmographics, technographics, hiring. Never-seen companies are crawled (partial data, or queued). Costs 2 credits (+1 with fresh=true when it queues a refresh); 1 when no company is found; 0 when queued or, with cache=only, not stored. - `company_customers`: Customers publicly evidenced for the company, verified domains only unless includeUnverified, with evidence quotes; include=partners adds partner platforms. Pages of up to 200 per list; pass next_cursor or partners_next_cursor back as cursor. Costs 1 credit + 2 per row returned over all lists; 0 when queued. - `company_vendors`: Vendors of the company: vendors with a public page that names it as their customer, and vendors it names itself on the public web, with that evidence page (and quote when there is one). Rows only when the company's domain is verified unless includeUnverified (an empty list held back for that reason is free). Answers from stored data only; an unknown company is not_in_graph at no cost. Pages of up to 200. Costs 1 credit + 2 per vendor returned. - `company_people`: People of the company found on the public web, with the published title, executive roles (ceo, cto, founder, …), seniority and the page where each was seen; executives first; investors, advisors and board members last, with affiliation and no roles. Filter by seniority, role or title. No e-mail: use work_email_lookup. Pages of up to 100. Costs 1 credit + 1 per person returned; 0 when queued or for an empty partial list. - `company_competitors`: Competitors: evidenced competitors first, then similar companies (basis similar, same market, not confirmed competitors). Costs 3 credits; 0 when queued or for an empty partial list. - `company_status`: Enrichment status of a company (complete, partial, refreshing, queued, not_found) without starting a crawl. Free. - `company_batch`: Company details for up to 50 domains in one call, one result per input (found, queued, queue_full, not_found, invalid). Never crawls while you wait. Costs 2 credits per distinct found domain. - `company_search`: Finds enriched companies by what they do, from a plain-words query; rows have the company card and similarity and match_score, and an answer may be empty. Never crawls. Costs 1 credit per started 50 rows; 0 when empty. - `profile_lookup`: The person described by an openly published page (bio, speaker page). Pages that need a sign-in are rejected. Costs 2 credits (+1 with fresh=true); 0 when no person is found. - `work_email_lookup`: Finds and checks a work e-mail; no e-mail is sent. Billed by verdict: valid 2 credits, likely_valid_pattern or catch_all_domain 1, unknown or invalid 0. - `credits_balance`: The credit balance, split into plan and pay-as-you-go credits with their expiry dates. Free. - `usage`: Requests by status class and credits charged, per UTC day and operation, over at most 92 days. Free. ## Removal requests No public process to remove a person or a company from the graph is published yet. ## Endpoint reference Generated from the OpenAPI document (`/openapi/v1.json`). ### GET /api/v1/company/details Company details Enriches a company domain with its profile data: firmographics, tech stack, hiring and executives. - **Speed:** answers from stored data at once. Data older than 30 days, or a call with `fresh=true`, is refreshed in the background. - **New companies:** a company never seen before is crawled while you wait (up to about 25 s) and returns partial data. If that takes longer, the answer is `202` with `Retry-After`, at no cost. - **Status:** the `X-Enrichment-Status` header is `complete`, `partial` (some fields are still being filled), `refreshing`, `refresh_deferred` (stored data; the organization already has 50 refreshes waiting, so none was queued) or `queued`. - **Credits:** 2 (+1 with `fresh=true`, except when `refresh_deferred` or when a refresh of the company is already queued). 1 when no company is found (`404 company_not_found`). 0 when queued, or with `cache=only` for a company not in the graph. - **Limit:** an organization may have at most 10 never-seen companies waiting for their first data. One more is answered `429` with `Retry-After`, at no cost. Parameters: - `domain` (query, required): Company website, URL or email domain, e.g. stripe.com - `fresh` (query): Queue a background re-crawl now, even within the 30-day window (+1 credit). - `cache` (query): Set to only to answer from stored data, also stale, and never start a crawl or a refresh. A company not in the graph is 404 not_in_graph at no cost. Cannot be combined with fresh=true. - `callback_url` (query): https URL to POST a signed Standard Webhooks message to once the company is complete (or not found), within 24 h. Get the signing secret from GET /api/v1/callbacks/secret. Free. Responses: - `200`: OK - `202`: Accepted - `429`: Too Many Requests - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "domain": "airtable.com", "name": "Airtable", "description": "500,000\u002B brands use Airtable to enable real-time collaboration, automate repetitive tasks \u0026 manual work, and streamline business processes in minutes. Join them.", "industry": "Data Infrastructure", "headquarters": { "country": "US", "city": "San Francisco" }, "founded_year": 2012, "employee_count": { "min": 700, "max": 700 }, "business_model": "b2b", "pricing_motion": "hybrid", "logo_url": "https://www.airtable.com/favicon.ico", "identifiers": { "lei": null, "sec_cik": null, "isin": null }, "technographics": { "tech_stack": [ "apple", "atlassian", "atlassian_statuspage" ], "email_provider": "google_workspace", "email_security_gateway": null, "email_pattern": "first.last" }, "hiring": { "open_roles_by_department": [ { "department": "Sales", "open_roles": 3 }, { "department": "Marketing", "open_roles": 1 } ], "open_roles_total": 4, "observed_at": "2026-10-22T05:10:22.727522\u002B00:00" }, "last_enriched_at": "2026-10-22T05:10:22.727522\u002B00:00", "websites": [ "airtable.com", "dopt.com" ], "tagline": "Build Enterprise-ready AI Workflows, Apps \u0026 Agents", "cover_image_url": "https://cdn.sanity.io/images/hlcyrtq5/production/00c20d5108d1335f5a243ddcd70ea4a2d6d9a844-1200x630.webp", "social_profiles": { "linkedin": "https://www.linkedin.com/company/airtable", "x": "https://x.com/airtable", "facebook": "https://www.facebook.com/airtableapp", "instagram": "https://www.instagram.com/airtable", "youtube": "https://www.youtube.com/c/AirtableApp", "github": null, "tiktok": null }, "company_type": "private", "public_listing": null, "sic_code": null, "executives": [] } ``` ### GET /api/v1/company/customers Customers and partners B2B companies publicly evidenced as customers of the company, each with a confidence score and the verbatim quote that shows it. - **Verified domains:** only customers with a verified domain are returned, unless `include_unverified=true`. Each row carries `domain_status`. - **Partners:** `include=partners` (or `include=customers,partners`) adds partners: integration, partner and marketplace listings found on the public web. - **Freshness:** `fresh=true` queues a background re-crawl. A company never seen before behaves as in `/company/details` (partial data or `202`). - **Credits:** 1 + 2 per row returned over all lists. 0 when queued. When the balance pays less than a full page, the page is cut to what it pays (at least one row per list), with `notice` and the next cursor. Parameters: - `domain` (query, required): Company website, URL or email domain - `max_results` (query): Maximum customers per page (1–200, default 20). Bounds the maximum cost. - `fresh` (query): Queue a background re-crawl now, even within the 30-day window. - `cache` (query): Set to only to answer from stored data, also stale, and never start a crawl or a refresh. A company not in the graph is 404 not_in_graph at no cost. Cannot be combined with fresh=true. - `include_unverified` (query): Also return customers whose domain is guessed from the name or was rejected (see domain_status). - `cursor` (query): next_cursor (include=customers) or partners_next_cursor (include=partners) of the previous page; omit for the first page. Send the same domain, max_results and include_unverified, and one list in include. - `include` (query): Comma list of the lists to return: customers (default), partners. max_results applies to each list, and the price counts the rows of all lists together. investors is not offered yet. - `callback_url` (query): https URL to POST a signed Standard Webhooks message to once the company is complete (or not found), within 24 h. Get the signing secret from GET /api/v1/callbacks/secret. Free. Responses: - `200`: OK - `202`: Accepted - `429`: Too Many Requests - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "company": { "domain": "stripe.com", "name": "Stripe" }, "customers": [ { "domain": "carousell.sg", "name": "Carousell", "confidence": 1, "evidence": { "quote": "/customers/carousell", "url": "https://stripe.com/newsroom/stories/carousell-and-stripe" }, "source_count": 19, "first_seen_at": "2026-10-19T14:09:30.094156\u002B00:00", "last_verified_at": "2026-10-22T10:33:53.751575\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": null, "industry": null, "logo_url": null, "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": false, "tagline": null, "linkedin": null, "x": null }, "reason": null, "match_score": null, "similarity": null }, { "domain": "edfenergy.com", "name": "Edf", "confidence": 0.96, "evidence": { "quote": "/customers/edf", "url": "https://stripe.com/customers/edf" }, "source_count": 1, "first_seen_at": "2026-10-19T14:09:30.094156\u002B00:00", "last_verified_at": "2026-10-22T10:33:53.751575\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": null, "industry": null, "logo_url": null, "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": false, "tagline": null, "linkedin": null, "x": null }, "reason": null, "match_score": null, "similarity": null } ], "count": 2, "max_results": 2, "min_confidence": 0.7, "include_unverified": false, "next_cursor": "MC45Nnw2MzkyODI2MjAzMzc1MTU3NTB8MDFhMTU0N2VmOWVlNzBiNWE1NmQ1MGIwODNlNWVlMTg", "partners": [ { "domain": "linear.app", "name": "Linear", "confidence": 0.8436, "evidence": { "quote": "Linear partners with Stripe to handle billing and payments.", "url": "https://stripe.com/" }, "source_count": 1, "first_seen_at": "2026-10-19T14:09:41.897158\u002B00:00", "last_verified_at": "2026-10-20T19:36:24.41401\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": "Purpose-built for planning and building products with AI agents.", "industry": "Software", "logo_url": "https://linear.app/static/apple-touch-icon.png?v=2", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true, "tagline": "The system for product development", "linkedin": null, "x": "https://x.com/linear" }, "reason": null, "match_score": null, "similarity": null } ], "partners_count": 1, "partners_next_cursor": null } ``` ### GET /api/v1/company/competitors Competitors Direct competitors of a company: publicly evidenced competitors first, then similar companies. - **`basis=evidenced`:** competitors that a public page names as such, each with its evidence. - **`basis=similar`:** companies in the same market, often with another product; not confirmed competitors. They appear once `X-Enrichment-Status` is `complete`. - **Rows:** each row has a `reason` (`compare_page`, `compare_page_text`, `similar_profile`). A similar row's `confidence` is how often such rows are real competitors, with its `similarity` and `match_score` beside it. - **Freshness:** `fresh=true` queues a background re-crawl at no extra cost. A company never seen before behaves as in `/company/details`. - **Credits:** 3. 0 when queued, and 0 for an empty list while the status is `partial`. Parameters: - `domain` (query, required): Company website, URL or email domain - `limit` (query): Maximum competitors to return (1–25, default 10). - `fresh` (query): Queue a background re-crawl now, even within the 30-day window. No extra cost. - `cache` (query): Set to only to answer from stored data, also stale, and never start a crawl or a refresh. A company not in the graph is 404 not_in_graph at no cost. Cannot be combined with fresh=true. - `callback_url` (query): https URL to POST a signed Standard Webhooks message to once the company is complete (or not found), within 24 h. Get the signing secret from GET /api/v1/callbacks/secret. Free. Responses: - `200`: OK - `202`: Accepted - `429`: Too Many Requests - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "company": { "domain": "stripe.com", "name": "Stripe" }, "competitors": [ { "domain": "paddle.com", "name": "Paddle", "confidence": 0.8, "evidence": { "quote": "/compare/stripe", "url": "https://www.paddle.com/compare/stripe" }, "source_count": 1, "first_seen_at": "2026-10-19T15:19:46.401102\u002B00:00", "last_verified_at": "2026-10-20T19:35:00.466324\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": "Increase revenue, reduce risk and reduce cost with Paddle, the payment solution powering global revenue for n8n, Letterboxd \u0026 AdGuard. One platform that handles billing, tax and compliance.", "industry": "E-Commerce", "logo_url": "https://www.paddle.com/images/paddle-spark.png", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true }, "reason": "compare_page", "match_score": null, "similarity": null }, { "domain": "explo.co", "name": "Explo", "confidence": 0.7, "evidence": { "quote": "/stripe-vs-explo", "url": "https://www.explo.co/stripe-vs-explo" }, "source_count": 1, "first_seen_at": "2026-10-19T14:53:51.624724\u002B00:00", "last_verified_at": "2026-10-19T14:53:57.060849\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": "An embedded analytics solution for product and engineering teams. Interactive dashboards, self-serve reporting, Explo AI, and enterprise-grade security all for your end users.", "industry": "Cybersecurity", "logo_url": "https://cdn.prod.website-files.com/62f681c18d50329187681754/62f681c18d503265d86818da_favicon.ico", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true }, "reason": "compare_page", "match_score": null, "similarity": null }, { "domain": "numeral.com", "name": "Numeral", "confidence": 0.7, "evidence": { "quote": "/stripe-tax-alternative", "url": "https://www.numeral.com/stripe-tax-alternative" }, "source_count": 1, "first_seen_at": "2026-10-19T14:28:36.858537\u002B00:00", "last_verified_at": "2026-10-19T14:28:48.47187\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": "Numeral automates sales tax, VAT and GST compliance for ecommerce and SaaS businesses, from nexus monitoring and registrations to filing and remittance.", "industry": "Government", "logo_url": "https://cdn.prod.website-files.com/6a219d3ca4f9ef92939b285c/6a68bd170d8ac003735e8f06_Numeral-Logomark-Indigo-White.png", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true }, "reason": "compare_page", "match_score": null, "similarity": null } ], "count": 3, "limit": 3 } ``` ### GET /api/v1/company/status Enrichment status Where a company stands, without starting a crawl. Poll this free endpoint after a `202` or a partial answer, instead of a paid one. - `complete`: the data is complete. - `partial`: some fields are still being filled. - `refreshing`: stored data; a refresh is running. - `queued`: waiting for its first crawl. - `not_found`: no data and nothing queued; call a company endpoint to start. Parameters: - `domain` (query, required): Company website, URL or email domain Responses: - `200`: OK Example response: ```json { "domain": "stripe.com", "status": "complete", "last_enriched_at": "2026-10-20T19:36:24.41401\u002B00:00", "fields_completed_at": "2026-10-20T19:36:24.41401\u002B00:00" } ``` ### POST /api/v1/company/batch Batch company details Company details for up to 50 domains in one call, one result per input, in input order. Never crawls while you wait: stale data is refreshed in the background as in `/company/details`, and `cache=only` reads stored data and queues nothing. Each result has a `status`: - `found`: `data` is set. - `queued`: a never-seen company, crawled in the background; poll `/company/status`. - `queue_full`: the organization's batch slots for never-seen companies are taken: 200, or 3 at once and 25 a UTC day for a trial organization. Batch companies run after all other work. - `not_found`: with `cache=only` and not in the graph, or the last crawl found no website. - `invalid`: `error` is set. - `insufficient_credits`: the balance did not pay for this found entry; top up and send it again. **Credits:** 2 per distinct found domain; queued, not found, invalid and duplicate entries cost 0. The balance must cover at least 2; when it covers less than 2 per distinct valid domain, found entries past what it pays answer `insufficient_credits` and cost 0. **Rate limit:** its own per-key limit (default 30 batches a minute). JSON body: - `domains`: - `cache`: Responses: - `200`: OK - `400`: Bad Request - `429`: Too Many Requests Example response: ```json { "results": [ { "input": "stripe.com", "domain": "stripe.com", "status": "found", "enrichment_status": "complete", "last_enriched_at": "2026-10-20T19:36:24.41401\u002B00:00", "data": { "domain": "stripe.com", "name": "Stripe", "description": "Stripe powers online and in-person payment processing and financial solutions for businesses of all sizes.", "industry": "Financial Services" }, "error": null }, { "input": "linear.app", "domain": "linear.app", "status": "found", "enrichment_status": "complete", "last_enriched_at": "2026-10-21T19:10:16.97606\u002B00:00", "data": { "domain": "linear.app", "name": "Linear", "description": "Purpose-built for planning and building products with AI agents.", "industry": "Software", "headquarters": { "country": null, "city": null }, "founded_year": null, "employee_count": { "min": null, "max": null }, "business_model": "b2b", "pricing_motion": "hybrid", "logo_url": "https://linear.app/static/apple-touch-icon.png?v=2", "identifiers": { "lei": null, "sec_cik": null, "isin": null }, "technographics": { "tech_stack": [ "anthropic", "apple", "aws", "cloudflare", "docusign", "hubspot", "microsoft_365", "nextjs", "notion", "openai", "postmark", "slack", "stripe", "zoom" ], "email_provider": "google_workspace", "email_security_gateway": null, "email_pattern": "first" }, "hiring": { "open_roles_by_department": [ { "department": "GTM", "open_roles": 14 }, { "department": "Product", "open_roles": 13 }, { "department": "Operations", "open_roles": 2 } ], "open_roles_total": 29, "observed_at": "2026-10-21T19:10:16.97606\u002B00:00" }, "last_enriched_at": "2026-10-21T19:10:16.97606\u002B00:00", "websites": [ "linear.app" ], "company_type": "unknown", "public_listing": null, "sic_code": null }, "error": null }, { "input": "not-a-domain", "domain": null, "status": "invalid", "enrichment_status": null, "last_enriched_at": null, "data": null, "error": { "code": "invalid_domain", "message": "\u0027not-a-domain\u0027 is not a fully-qualified domain." } } ], "found": 2, "queued": 0, "not_found": 0, "failed": 1, "credits_charged": 4 } ``` ### GET /api/v1/company/search Search companies Ranks enriched companies by the meaning of a plain-words query, for example `q=tools to chase unpaid invoices`. Each row has the company card, its `similarity` and `match_score`. - **Results:** only strong matches are shown, so an answer can have fewer rows than `max_results`, or none. Never crawls. - **Credits:** 1 per started 50 rows returned; an empty answer is free. - **Rate limit:** its own per-key limit (default 60 searches a minute). When search is busy the answer is `503 busy` with `Retry-After`, at no cost. Parameters: - `q` (query): What the companies do, in plain words (3–200 characters). - `max_results` (query): Maximum rows to return (1–50, default 10). Responses: - `200`: OK - `400`: Bad Request - `429`: Too Many Requests - `503`: Service Unavailable - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "query": "tools to chase unpaid invoices", "results": [ { "domain": "getharvest.com", "name": "Harvest", "similarity": 0.7536, "match_score": 4.104, "card": { "description": "Time tracking and management software with powerful easy reporting and streamlined online invoicing. Loved by 73,000 businesses. Get started for free.", "industry": "Software", "logo_url": "https://www.getharvest.com/hubfs/apple-touch-icon.png", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true, "tagline": null, "linkedin": null, "x": null } }, { "domain": "midday.ai", "name": "Midday", "similarity": 0.7398, "match_score": 3.573, "card": { "description": "Invoicing, automatic reconciliation, time tracking, documents, and financial exports in one place. The business stack that replaces the tools you\u0027ve outgrown.", "industry": "Software", "logo_url": "https://cdn.midday.ai/logo.png", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true, "tagline": null, "linkedin": null, "x": null } }, { "domain": "tmetric.com", "name": "TMetric", "similarity": 0.7377, "match_score": 3.494, "card": { "description": "TMetric is a leading time tracking solution to boost your team\u0027s productivity. Keep track of working hours, bill clients, and automate payroll.", "industry": "Software", "logo_url": "https://tmetric.com/media/43il1ypw/tmetric-logo-with-text.svg", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true, "tagline": null, "linkedin": null, "x": null } } ], "count": 3, "max_results": 3 } ``` ### GET /api/v1/company/vendors Vendors The reverse of `/company/customers`: vendors with a public page that names this company as their customer, and vendors this company names itself on the public web, each with a confidence score and that evidence page (and quote when there is one). - **Verified domains:** rows come back only when the company's own domain is verified (`company_domain_status`); otherwise the list is empty and free. `include_unverified=true` also returns rows for an unverified company and vendors with unverified domains. - **Stored data only:** never crawls or queues. A company not in the graph is `404 not_in_graph`, at no cost. - **Credits:** 1 + 2 per vendor returned; also 1 for an empty list of a verified company. When the balance pays less than a full page, the page is cut to what it pays, with `notice` and the next cursor. Parameters: - `domain` (query, required): Company website, URL or email domain, e.g. openai.com - `max_results` (query): Maximum vendors per page (1–200, default 20). Bounds the maximum cost. - `include_unverified` (query): Also return rows when the company's domain or the vendor's domain is guessed or rejected (see company_domain_status and domain_status). - `cursor` (query): next_cursor of the previous page; omit for the first page. Send the same domain, max_results and include_unverified. Responses: - `200`: OK - `400`: Bad Request - `404`: Not Found - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "company": { "domain": "openai.com", "name": "OpenAI" }, "company_domain_status": "verified", "vendors": [ { "domain": "clay.com", "name": "Clay", "confidence": 0.988, "evidence": { "quote": "/customers/open-ai", "url": "https://www.clay.com/customers" }, "source_count": 1, "first_seen_at": "2026-10-19T13:48:47.171756\u002B00:00", "last_verified_at": "2026-10-19T13:48:53.44877\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": "Infrastructure to get any data, run agentic workflows, and launch GTM plays.", "industry": "E-Commerce", "logo_url": "https://cdn.prod.website-files.com/61477f2c24a826836f969afe/6a3a92ab3c92c9caa6245619_dot-com_favicon_2026_512.png", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true, "tagline": null, "linkedin": null, "x": null }, "reason": null, "match_score": null, "similarity": null }, { "domain": "notion.com", "name": "Notion", "confidence": 0.986, "evidence": { "quote": "/customers/openai", "url": "https://www.notion.com/customers" }, "source_count": 1, "first_seen_at": "2026-10-19T14:01:13.65149\u002B00:00", "last_verified_at": "2026-10-19T14:01:19.450338\u002B00:00", "basis": "evidenced", "domain_status": "verified", "card": { "description": "Build Custom Agents, search across all your apps, and automate busywork. The AI workspace where teams get more done, faster.", "industry": "Software", "logo_url": "https://www.notion.com/front-static/logo-ios.png", "headquarters_country": null, "employee_count": { "min": null, "max": null }, "enriched": true, "tagline": null, "linkedin": null, "x": null }, "reason": null, "match_score": null, "similarity": null } ], "count": 2, "max_results": 2, "min_confidence": 0.7, "include_unverified": false, "next_cursor": "MC45ODZ8NjM5MjgwMTUyNzk0NTAzMzgwfDAxYTE1MzQxODg3ZjdjOTZiMzY1NWRiNTU4Mjc0MmY1" } ``` ### GET /api/v1/company/people Company people People data for a company: the people of the company found on the public web, with the published title, executive roles and seniority read from it, and the page where each was seen. - **Order:** executives first, then by name. People listed as investors, advisors or board members come last, with `affiliation`, no roles and no seniority, and are never executives. - **Current:** only the people from the latest check; a person known only from their own profile page stays for 12 months after it was last read. - **No e-mail:** use `/work-email/lookup`. - **New companies:** a company never seen before behaves as in `/company/details` (partial data or `202`). - **Credits:** 1 + 1 per person returned. An empty list costs 1, or 0 while the status is `partial`; 0 when queued. When the balance pays less than a full page, the page is cut to what it pays, with `notice` and the next cursor. To have a person removed, e-mail support@innerfetch.com with the page URL, or the full name and the company domain; the person is deleted and kept out of later crawls. Parameters: - `domain` (query, required): Company website, URL or email domain, e.g. linear.app - `seniority` (query): Only this band: c_suite, vp, director, manager or ic. - `role` (query): Only people with this executive role: ceo, cto, cfo, coo, cmo, cro, cpo, ciso, founder, co_founder, president, chair or other. - `title` (query): Only titles that contain this text (no case). - `limit` (query): People per page (1–100, default 25). Bounds the cost. - `cursor` (query): next_cursor of the previous page; omit for the first page. Send the same filters. - `fresh` (query): Queue a background re-crawl now, even within the 30-day window. - `cache` (query): Set to only to answer from stored data, also stale, and never start a crawl or a refresh. A company not in the graph is 404 not_in_graph at no cost. Cannot be combined with fresh=true. Responses: - `200`: OK - `202`: Accepted - `429`: Too Many Requests - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "company": { "domain": "example.com", "name": "Example" }, "people": [ { "full_name": "Jane Doe", "first_name": "Jane", "last_name": "Doe", "title": "Co-founder \u0026 CEO", "roles": [ "co_founder", "ceo" ], "seniority": "c_suite", "affiliation": null, "profile_url": "https://www.example.com/about/team", "linkedin": "https://www.linkedin.com/in/example-jane-doe", "evidence": { "quote": null, "url": "https://www.example.com/about/team" }, "last_verified_at": "2026-10-22T15:18:22.056499\u002B00:00" }, { "full_name": "John Roe", "first_name": "John", "last_name": "Roe", "title": "CTO", "roles": [ "cto" ], "seniority": "c_suite", "affiliation": null, "profile_url": "https://www.example.com/about/team", "linkedin": null, "evidence": { "quote": null, "url": "https://www.example.com/about/team" }, "last_verified_at": "2026-10-22T15:18:22.056499\u002B00:00" }, { "full_name": "Alex Poe", "first_name": "Alex", "last_name": "Poe", "title": "VP of Engineering", "roles": [], "seniority": "vp", "affiliation": null, "profile_url": "https://www.example.com/about/team", "linkedin": null, "evidence": { "quote": null, "url": "https://www.example.com/about/team" }, "last_verified_at": "2026-10-22T15:18:22.056499\u002B00:00" } ], "count": 3, "limit": 3, "next_cursor": "MXwwMTk5YTFiMmMzZDRlNWY2MDcxODI5M2E0YjVjNmQ3ZXxBbGV4IFBvZQ" } ``` ### GET /api/v1/profile Public profile People data enrichment from one public page: the person described by an openly published page (company bio, speaker page, personal site): name, title, seniority, company, location, and an address only when the page prints it. - **Pages:** pages that need a sign-in are rejected. - **Credits:** 2 (+1 with `fresh=true`); 0 when no person is found. Parameters: - `url` (query, required): Absolute http(s) URL of a public page about the person. - `fresh` (query): Re-fetch the page even if it was read within 30 days (+1 credit). Responses: - `200`: OK - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "full_name": "Jane Doe", "first_name": "Jane", "last_name": "Doe", "title": "Co-founder \u0026 CEO", "seniority": "c_suite", "company": { "domain": "example.com", "name": "Example" }, "location": null, "email": null, "profile_url": "https://www.example.com/about/team", "last_verified_at": "2026-10-21T21:30:21.3121618\u002B00:00" } ``` ### POST /api/v1/work-email/lookup Work e-mail lookup Enriches a person with a work e-mail: finds and checks the work e-mail of a person at a company. No e-mail is sent. - **Credits by verdict:** `valid` 2; `likely_valid_pattern` or `catch_all_domain` 1; `unknown` or `invalid` 0. JSON body: - `first_name`: Given name as published, e.g. Jane. - `last_name`: Family name as published, e.g. Doe. - `domain`: Company domain, URL or any address at the company. Responses: - `200`: OK - `402`: Payment required: an API key without enough credits (insufficient_credits), or a call without a key, which gets a pay-per-call quote (payment_required, payment_invalid, payment_failed). Example response: ```json { "domain": "stripe.com", "email": null, "deliverability": "unknown", "confidence": 0, "evidence": "no conclusive evidence", "domain_has_mx": true, "provider": "innerfetch" } ``` ### GET /api/v1/credits Credit balance The balance, split into plan credits (spent first) and pay-as-you-go credits, with their expiry dates. Free. Responses: - `200`: OK Example response: ```json { "credits_remaining": 34579, "plan_credits": 0, "payg_credits": 34579, "plan_credits_expire_at": null, "payg_next_expiry_at": null } ``` ### GET /api/v1/usage Usage per day Requests and credits per UTC day and endpoint: requests by status class, counted as they finish (they show within about 5 s), and the net credits charged. - **Range:** at most 92 days; the last 30 by default. - **Credits:** free. Its own per-key rate limit (default 60 a minute). Parameters: - `from` (query): First UTC day, yyyy-MM-dd (default: 29 days before to). - `to` (query): Last UTC day, yyyy-MM-dd (default: today). Responses: - `200`: OK - `400`: Bad Request Example response: ```json { "from": "2026-10-21", "to": "2026-10-21", "days": [ { "day": "2026-10-21", "operation": "GET /api/v1/company/competitors", "requests": 3, "succeeded": 3, "client_errors": 0, "server_errors": 0, "credits": 1485 }, { "day": "2026-10-21", "operation": "GET /api/v1/company/customers", "requests": 3, "succeeded": 3, "client_errors": 0, "server_errors": 0, "credits": 24095 }, { "day": "2026-10-21", "operation": "GET /api/v1/company/details", "requests": 9112, "succeeded": 9112, "client_errors": 0, "server_errors": 0, "credits": 36365 } ], "requests": 9118, "credits": 61945 } ``` ### GET /api/v1/pricing Pricing Plans and credit packs with their list price (in minor units, such as cents), the credits of every endpoint and of each item a call returns, the limits of a paid workspace, and the free trial with its limits. - **Pay per call:** the terms for agents without an account (x402), or `null` when off. - **Access:** no API key needed; cached for 5 minutes. Free. Responses: - `200`: OK Example response: ```json { "plans": [ { "code": "pack_2k", "name": "2,000 credits (PAYG)", "kind": "credit_pack", "credits": 2000, "price": 2900, "currency": "USD", "interval": null, "rank": 0 }, { "code": "pack_10k", "name": "10,000 credits (PAYG)", "kind": "credit_pack", "credits": 10000, "price": 11900, "currency": "USD", "interval": null, "rank": 0 }, { "code": "pack_50k", "name": "50,000 credits (PAYG)", "kind": "credit_pack", "credits": 50000, "price": 49900, "currency": "USD", "interval": null, "rank": 0 }, { "code": "starter_monthly", "name": "Starter \u2014 5,000 credits / month", "kind": "subscription", "credits": 5000, "price": 4900, "currency": "USD", "interval": "month", "rank": 1 }, { "code": "growth_monthly", "name": "Growth \u2014 25,000 credits / month", "kind": "subscription", "credits": 25000, "price": 19900, "currency": "USD", "interval": "month", "rank": 2 }, { "code": "scale_monthly", "name": "Scale \u2014 100,000 credits / month", "kind": "subscription", "credits": 100000, "price": 59900, "currency": "USD", "interval": "month", "rank": 3 } ], "endpoints": [ { "endpoint": "\u0060GET /api/v1/company/details\u0060", "credits": "2 (\u002B1 with \u0060fresh=true\u0060); 1 for \u0060404 company_not_found\u0060", "free_when": "\u0060202\u0060 queued, \u0060404 not_in_graph\u0060 with \u0060cache=only\u0060" }, { "endpoint": "\u0060GET /api/v1/company/customers\u0060", "credits": "1 \u002B 2 per row returned over all lists (\u0060include=customers,partners\u0060), at most 401 a page per list", "free_when": "\u0060202\u0060 queued" }, { "endpoint": "\u0060GET /api/v1/company/vendors\u0060", "credits": "1 \u002B 2 per vendor returned, at most 401 a page", "free_when": "\u0060404 not_in_graph\u0060, an empty list held back because the company\u0027s domain is not verified" }, { "endpoint": "\u0060GET /api/v1/company/people\u0060", "credits": "1 \u002B 1 per person returned, at most 101 a page; executives in details are free", "free_when": "\u0060202\u0060 queued, an empty list while \u0060partial\u0060" }, { "endpoint": "\u0060GET /api/v1/company/competitors\u0060", "credits": "3", "free_when": "\u0060202\u0060 queued, an empty list while \u0060partial\u0060" }, { "endpoint": "\u0060POST /api/v1/company/batch\u0060", "credits": "2 per distinct found domain (up to 50 entries)", "free_when": "\u0060queued\u0060, \u0060queue_full\u0060, \u0060not_found\u0060, \u0060invalid\u0060 and duplicate entries" }, { "endpoint": "\u0060GET /api/v1/company/search\u0060", "credits": "1 per started 50 rows returned", "free_when": "an empty answer" }, { "endpoint": "\u0060GET /api/v1/company/status\u0060", "credits": "0", "free_when": "" }, { "endpoint": "\u0060GET /api/v1/profile\u0060", "credits": "2 (\u002B1 with \u0060fresh=true\u0060)", "free_when": "no person found" }, { "endpoint": "\u0060POST /api/v1/work-email/lookup\u0060", "credits": "\u0060valid\u0060 2, \u0060likely_valid_pattern\u0060 or \u0060catch_all_domain\u0060 1, \u0060unknown\u0060 or \u0060invalid\u0060 0", "free_when": "" }, { "endpoint": "\u0060GET /api/v1/credits\u0060, \u0060GET /api/v1/usage\u0060, \u0060GET /api/v1/pricing\u0060, \u0060GET /api/v1/billing/plans\u0060, \u0060POST /api/v1/billing/checkout\u0060", "credits": "0", "free_when": "" }, { "endpoint": "\u0060POST /mcp\u0060 (each tool)", "credits": "the price of the endpoint the tool calls", "free_when": "the same cases" } ], "trial": { "credits": 200, "valid_days": 30, "eligibility": "A console sign-up with a work e-mail that Google or Microsoft verifies, once per e-mail domain.", "limits": { "requests_per_minute": 60, "never_seen_waiting": 3, "never_seen_bulk_waiting": 3, "never_seen_per_day": 25, "active_keys": 2 } } } ``` ### GET /api/v1/billing/plans Plans and credit packs The plans and credit packs you can buy: `code`, name, `kind` (`subscription` or `credit_pack`) and the credits each grants. Pass a `code` to checkout. Free. Responses: - `200`: OK Example response: ```json [ { "code": "pack_2k", "name": "2,000 credits (PAYG)", "kind": "credit_pack", "credit_grant": 2000 }, { "code": "pack_10k", "name": "10,000 credits (PAYG)", "kind": "credit_pack", "credit_grant": 10000 }, { "code": "pack_50k", "name": "50,000 credits (PAYG)", "kind": "credit_pack", "credit_grant": 50000 }, { "code": "starter_monthly", "name": "Starter \u2014 5,000 credits / month", "kind": "subscription", "credit_grant": 5000 }, { "code": "growth_monthly", "name": "Growth \u2014 25,000 credits / month", "kind": "subscription", "credit_grant": 25000 }, { "code": "scale_monthly", "name": "Scale \u2014 100,000 credits / month", "kind": "subscription", "credit_grant": 100000 } ] ``` ### POST /api/v1/billing/checkout Create a checkout Returns a hosted Dodo Payments checkout URL for a subscription or a pay-as-you-go credit pack. Credits are granted once the payment is confirmed. - **One subscription:** a workspace holds one subscription. A second subscription checkout answers `409 subscription_exists`; change the plan in the console instead. - **Credits:** free. JSON body: - `plan`: - `quantity`: - `customer_email`: - `customer_name`: - `return_url`: Responses: - `201`: Created Example response: ```json { "session_id": "cks_example", "checkout_url": "https://checkout.example.com/cks_example", "plan": "pack_10k", "credit_grant_amount": 10000 } ``` ### GET /api/v1/callbacks/secret Callback secret The `whsec_` secret of your organization. Every callback carries `webhook-id`, `webhook-timestamp` and `webhook-signature` (v1, HMAC-SHA256 over `id.timestamp.body`), so any Standard Webhooks library verifies it. Free. Responses: - `200`: OK - `503`: Service Unavailable ### POST /api/v1/callbacks/secret/rotate Rotate the callback secret Makes a new secret and returns it. The old secret stops at once: callbacks sent after this call, retries too, carry only the new signature. Free. Responses: - `200`: OK - `503`: Service Unavailable ## Docs - [Full reference: prices, MCP tools, every endpoint with an example answer](https://api.innerfetch.com/llms-full.txt) - [Agent skill (SKILL.md; its version changes with its content)](https://api.innerfetch.com/skills/innerfetch/SKILL.md) - [OpenAPI document](https://api.innerfetch.com/openapi/v1.json) - [Interactive reference](https://api.innerfetch.com/docs) ## Status codes - 400 invalid input · 401 missing/invalid key · 402 insufficient credits (a list or batch that the balance pays only in part is cut instead: see Prices), or pay per call without a key · 404 not found (free, except `company_not_found` from `/company/details`: 1 credit) · 202 queued for enrichment (free; retry after `Retry-After`) · 429 rate limited or too many new companies queued (free; honour `Retry-After`) · 503 at capacity (free; honour `Retry-After`) ## Error codes and headers Every error body has a stable `code` (schema `ErrorCode` in the OpenAPI document) and a `trace_id`. Branch on `code`, not on `title`. Codes: `invalid_request`, `invalid_domain`, `invalid_url`, `conflicting_parameters`, `batch_too_large`, `invalid_api_key`, `invalid_signature`, `insufficient_credits`, `payment_required`, `payment_invalid`, `payment_failed`, `organization_suspended`, `not_found`, `company_not_found`, `profile_not_found`, `not_in_graph`, `payload_too_large`, `rate_limited`, `queue_full`, `callback_limit`, `callbacks_disabled`, `subscription_exists`, `internal_error`, `upstream_error`, `busy`. A `402 insufficient_credits` body also has `required_credits`. `403 organization_suspended` means the organization's API access is suspended (for example during a payment dispute); contact support@innerfetch.com. Every authenticated response carries `X-RateLimit-Limit` (requests per minute you can make now, the lower of your key's and your address's limits), `X-RateLimit-Remaining` (requests left now) and `X-RateLimit-Reset` (seconds until both are full again), plus the credit headers. Company answers (200) carry `X-Data-Age-Days` (whole days since the data was last collected) and `Last-Modified`.