{
  "openapi": "3.1.0",
  "info": {
    "title": "OpenFunnel Agent Primitives",
    "version": "1.0.0"
  },
  "servers": [
    {
      "url": "https://api.openfunnel.dev"
    }
  ],
  "paths": {
    "/api/v1/account/search-lookalikes": {
      "get": {
        "tags": [
          "Company Search"
        ],
        "summary": "Search Lookalikes",
        "description": "Find lookalike companies from either a natural-language description **or** a set of seed company websites, with optional firmographic filters.\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>\n\nUse it for queries such as:\n- `Healthcare companies building with voice AI`\n- `Companies that provide customer support software for B2C`\n- `Companies that build workflow automation for non technical industries`\n\nOr supply 1-10 seed company websites and let OpenFunnel synthesize the query for you:\n- `seed_domains=datadog.com&seed_domains=grafana.com&seed_domains=dynatrace.com`\n\nYou can also restrict results to your ICP by applying filters for company size, funding stage, and headquarters country.\n\n## Two input modes\n\nAt least one of `query` or `seed_domains` is required. Both can be combined for a hybrid trait.\n\n- **Natural language** (`query`): describe the kind of company you want. Best when you can articulate the trait in 1-2 sentences.\n- **Seed-based discovery** (`seed_domains`): pass up to 10 company websites and OpenFunnel uses them to synthesize the lookalike query for you. Best when you can name a few example customers but struggle to phrase what they have in common.\n- **Both together**: pass `seed_domains` AND `query` - the query becomes a must-have angle layered on top of the seeds (e.g. seeds=[datadog.com, grafana.com] + query=\"cloud monitoring and observability tools\" → synthesized trait targets cloud monitoring tools).\n\nWith `seed_domains`, the response echoes back the LLM-synthesized `derived_query`, plus `resolved_seed_domains` (seeds that landed in our index) and `unresolved_seed_domains` (seeds we couldn't find - skipped silently as long as at least one resolved).\n\n## Omitting filters\nIf you omit filters, search runs across all company sizes, funding stages, and supported locations. Use the optional firmographic filters below to restrict results to your ICP.\n\n## Discovering available firmographics\nThe `funding_stages`, `locations`, `min_employees`, and `max_employees` filters accept a fixed set of values. Fetch the current set via **`GET /api/v1/account/firmographic-options`** it returns the accepted `funding_stages` labels, the supported HQ `locations` (code + name), and the min/max employee bounds.\n\n## Credits\n**1 credit per company returned.** Empty result sets are free. The exact amount billed is echoed back in `credits_consumed` on the response. The LLM trait synthesis used by `seed_domains` is included - no extra charge.\n\nThe response includes a `derived_query` field with the LLM-synthesized trait so you can see exactly what was searched.",
        "operationId": "search_lookalikes_get_api_v1_account_search_lookalikes_get",
        "parameters": [
          {
            "name": "query",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 1,
                  "maxLength": 200
                },
                {
                  "type": "null"
                }
              ],
              "title": "Query"
            },
            "description": "Describe the kinds of companies you want to find. Required unless `seed_domains` is provided. When both are supplied, the query is used as a must-have angle on top of the trait synthesized from the seeds."
          },
          {
            "name": "seed_domains",
            "in": "query",
            "required": false,
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "maxItems": 10,
              "title": "Seed Domains"
            },
            "description": "Up to 10 seed company websites/domains (e.g. `datadog.com`, `https://www.grafana.com`). When provided, OpenFunnel uses them to synthesize the lookalike query for you. Protocol / `www.` prefixes are normalized automatically. Unresolved seeds are skipped silently as long as at least one resolves. Repeat the parameter to send multiple values."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1.0,
              "maximum": 100,
              "default": 10,
              "title": "Limit"
            },
            "description": "Maximum number of companies to return. Default `10`, max `100`. For larger result sets (up to 10000), use the async bulk endpoint `POST /api/v1/account/search-lookalikes-bulk`."
          },
          {
            "name": "min_employees",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0.0,
              "title": "Min Employees"
            },
            "description": "Minimum employee count, inclusive."
          },
          {
            "name": "max_employees",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 0.0,
              "title": "Max Employees"
            },
            "description": "Maximum employee count, inclusive."
          },
          {
            "name": "funding_stages",
            "in": "query",
            "required": false,
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string",
                "enum": [
                  "Pre-Seed",
                  "Seed",
                  "Series A",
                  "Series B",
                  "Series C",
                  "Series D",
                  "Series E",
                  "Series F",
                  "Series G",
                  "Series H",
                  "Acquired",
                  "Private",
                  "Public"
                ]
              },
              "title": "Funding Stages"
            },
            "description": "Funding stages to include. Repeat the parameter to send multiple values."
          },
          {
            "name": "locations",
            "in": "query",
            "required": false,
            "style": "form",
            "explode": true,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "title": "Locations"
            },
            "description": "Headquarters country filter using ISO 3166-1 alpha-3 codes (e.g. `USA`, `GBR`, `IND`, `DEU`). Top 30 by company volume: USA, GBR, CHN, FRA, IND, DEU, BRA, BEL, ESP, CAN, AUS, NLD, ITA, NOR, ZAF, MEX, TUR, CHE, ARE, POL, SWE, IDN, ARG, PAK, COL, PRT, JPN, CHL, NGA, AUT. Pass `EU` to expand to all 27 EU member states. Repeat the parameter to send multiple values. Full list of 250 codes via `GET /api/v2/tech/country-options` (under Agent Helpers)."
          },
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/InstantTraitSearchResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached for this API key or user."
          },
          "503": {
            "description": "Lookalike search is temporarily unavailable. Please try again shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceUnavailableResponse"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "Query based",
            "source": "curl -sS -G \"https://api.openfunnel.dev/api/v1/account/search-lookalikes\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  --data-urlencode \"query=Healthcare companies building with voice AI\" \\\n  --data-urlencode \"limit=100\" \\\n  --data-urlencode \"min_employees=50\" \\\n  --data-urlencode \"max_employees=500\" \\\n  --data-urlencode \"funding_stages=Series A\" \\\n  --data-urlencode \"funding_stages=Series B\" \\\n  --data-urlencode \"locations=USA\""
          },
          {
            "lang": "bash",
            "label": "Seed-based",
            "source": "curl -sS -G \"https://api.openfunnel.dev/api/v1/account/search-lookalikes\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  --data-urlencode \"seed_domains=datadog.com\" \\\n  --data-urlencode \"seed_domains=grafana.com\" \\\n  --data-urlencode \"seed_domains=dynatrace.com\" \\\n  --data-urlencode \"limit=25\""
          },
          {
            "lang": "bash",
            "label": "Seeds + query",
            "source": "curl -sS -G \"https://api.openfunnel.dev/api/v1/account/search-lookalikes\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  --data-urlencode \"seed_domains=datadog.com\" \\\n  --data-urlencode \"seed_domains=dynatrace.com\" \\\n  --data-urlencode \"query=cloud monitoring tools\" \\\n  --data-urlencode \"limit=25\""
          }
        ]
      }
    },
    "/api/v1/account/search-lookalikes-bulk": {
      "post": {
        "tags": [
          "Company Search"
        ],
        "summary": "Search Lookalikes Bulk",
        "description": "**Asynchronous, high-volume variant of [Search Lookalikes](/agent-primitives/search-lookalikes).** Same lookalike engine and inputs, but it accepts up to **10000** companies and runs as a background job: the call returns a `job_id` immediately, and you poll a separate endpoint for status + paginated results.\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>\n\nFind lookalike companies from either a natural-language description **or** a set of seed company websites, with optional firmographic filters.\n\nUse it for queries such as:\n- `Healthcare companies building with voice AI`\n- `Companies that provide customer support software for B2C`\n- `Companies that build workflow automation for non technical industries`\n\nOr supply 1-10 seed company websites and let OpenFunnel synthesize the query for you.\n\n## Two input modes\n\nAt least one of `query` or `seed_domains` is required. Both can be combined for a hybrid trait.\n\n- **Natural language** (`query`): describe the kind of company you want.\n- **Seed-based discovery** (`seed_domains`): pass up to 10 company websites and OpenFunnel uses them to synthesize the lookalike query for you. The response manifest echoes back the LLM-synthesized `derived_query`, plus `resolved_seed_domains` and `unresolved_seed_domains`.\n- **Both together**: pass `seed_domains` AND `query` - the query becomes a must-have angle layered on top of the seeds.\n\n## Omitting filters\nIf you omit filters, search runs across all company sizes, funding stages, and supported locations. Use the optional firmographic filters (`min_employees`, `max_employees`, `funding_stages`, `locations`) to restrict to your ICP.\n\n## Discovering available firmographics\nThe `funding_stages`, `locations`, `min_employees`, and `max_employees` filters accept a fixed set of values. Fetch the current set via **`GET /api/v1/account/firmographic-options`** - it returns the accepted `funding_stages` labels, the supported HQ `locations` (code + name), and the min/max employee bounds.\n\n## Polling for results\nThis endpoint responds immediately with a `job_id` and `status: \"pending\"`. **Poll `GET /api/v1/account/search-lookalikes-bulk/{job_id}`** (\"Search Lookalikes Bulk Results\", listed under Agent Helpers) until `status` is `completed`, then walk the result pages using `next_cursor`. A large `limit` can take minutes - polling avoids a long-held connection.\n\n## Credits\n**1 credit per company returned**, charged when the job completes. Empty result sets are free. The amount is echoed in the manifest's `credits_consumed`.\n\n## Cancelling a job\nIf you need to stop a running or pending job early, call **`POST /api/v1/account/search-lookalikes-bulk/{job_id}/cancel`**. The job moves to a transient `cancelling` state and stops at its next checkpoint; whatever companies were already delivered stay readable via the poll endpoint, and you are charged **only for those** - nothing for the abandoned remainder.\n\nThe response returns a `job_id`; then poll `GET /api/v1/account/search-lookalikes-bulk/{job_id}` for status and results.",
        "operationId": "search_lookalikes_bulk_api_v1_account_search_lookalikes_bulk_post",
        "parameters": [
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BulkLookalikeSearchRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Job accepted. Returns a `job_id` to poll for status and results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkLookalikeSubmitResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit reached for this API key or user."
          },
          "503": {
            "description": "Bulk lookalike search is temporarily unavailable. Please try again shortly.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ServiceUnavailableResponse"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "Submit a bulk job",
            "source": "curl -sS -X POST \"https://api.openfunnel.dev/api/v1/account/search-lookalikes-bulk\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"query\": \"Healthcare companies building with voice AI\", \"limit\": 2000, \"min_employees\": 50, \"max_employees\": 500, \"funding_stages\": [\"Series A\", \"Series B\"], \"locations\": [\"USA\"]}'"
          }
        ]
      }
    },
    "/api/v2/tech/companies": {
      "get": {
        "tags": [
          "Company Search"
        ],
        "summary": "Search Companies By Tech Stack",
        "description": "Returns companies whose recent job postings mention a given technology.\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>\n\nResults are ranked by matching post count and paginated 50 per page; walk pages with `?page=1`, `?page=2`, ... using `has_more`. Well-known LinkedIn job aggregators (`jobs-via-*`, `lensa`, `ziprecruiter`, etc.) are excluded by default.\n\nSet `enrich=true` to hydrate each hit with firmographics (employee count, HQ, website, LinkedIn, industries, funding, revenue). The firmographic filters (`hq_country_code`, `min_employee_count`, `max_employee_count`) auto-enable enrichment, so you don't need to set both.\n\nCredits: charged per company returned. The exact amount charged is returned in `credits_consumed` on every response.\n\nExample - US-HQ companies of 1,000+ employees hiring with `Snowflake`:\n`GET /api/v2/tech/companies?tech=Snowflake&hq_country_code=USA&min_employee_count=1000`",
        "operationId": "tech_companies",
        "parameters": [
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          },
          {
            "name": "tech",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 128,
              "title": "Tech"
            },
            "description": "Technology to search for in job descriptions (e.g. 'Snowflake', 'Kubernetes')."
          },
          {
            "name": "variations",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "title": "Variations"
            },
            "description": "Optional alternate phrasings (e.g. 'Snowflake Data Cloud'). Each is OR-matched as a phrase."
          },
          {
            "name": "lookback_days",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 365,
              "default": 365,
              "title": "Lookback Days"
            },
            "description": "Lookback window in days (1–365). Defaults to a full year so coverage estimates aren't artificially clipped; narrow this for recency-sensitive queries."
          },
          {
            "name": "page",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 1000,
              "default": 1,
              "title": "Page"
            },
            "description": "1-indexed page number. Page size is fixed at 50. Walk pages with `has_more`. Sanity-bounded at 1,000 pages (50,000 results) - the long tail is rarely useful for ICP discovery. When firmographic filters are set, the addressable pool reflects the filtered set."
          },
          {
            "name": "exclude_vendor",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true,
              "title": "Exclude Vendor"
            },
            "description": "When true (default), excludes job posts from the vendor of the technology being searched (e.g. searching `Snowflake` won't return Snowflake's own job posts). Set false to include the vendor."
          },
          {
            "name": "exclude_aggregators",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true,
              "title": "Exclude Aggregators"
            },
            "description": "Exclude well-known LinkedIn job aggregators and reposters (`jobs-via-*`, `get-it-recruit-*`, `lensa`, `jobot`, `ziprecruiter`, `clearancejobs`, `cybercoders`, etc.) which otherwise dominate raw counts without representing actual tech adopters. ON by default."
          },
          {
            "name": "exclude_company_slugs",
            "in": "query",
            "required": false,
            "schema": {
              "type": "array",
              "items": {
                "type": "string"
              },
              "title": "Exclude Company Slugs"
            },
            "description": "Additional LinkedIn company slugs to exclude on top of the default aggregator block-list. Useful for blocking consultancies (`deloitte`, `pwc`, `accenture`), the caller's own company, or known competitors."
          },
          {
            "name": "include_evidence",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": true,
              "title": "Include Evidence"
            },
            "description": "Include one top-hit job per company with a highlighted snippet showing the tech phrase in context. Set false for the leanest payload (~6× faster)."
          },
          {
            "name": "enrich",
            "in": "query",
            "required": false,
            "schema": {
              "type": "boolean",
              "default": false,
              "title": "Enrich"
            },
            "description": "When true, hydrates each company on this page with firmographics: employee count, website, LinkedIn URL, HQ, industries, funding, and revenue. Adds ~150–200ms per page. Auto-enabled when any of `hq_country_code`, `min_employee_count`, or `max_employee_count` is set."
          },
          {
            "name": "hq_country_code",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string",
                  "minLength": 3,
                  "maxLength": 3
                },
                {
                  "type": "null"
                }
              ],
              "title": "Hq Country Code"
            },
            "description": "Filter by company HQ ISO 3166-1 alpha-3 code (e.g. `USA`, `GBR`, `IND`, `DEU`). Top 30 by company volume: USA, GBR, CHN, FRA, IND, DEU, BRA, BEL, ESP, CAN, AUS, NLD, ITA, NOR, ZAF, MEX, TUR, CHE, ARE, POL, SWE, IDN, ARG, PAK, COL, PRT, JPN, CHL, NGA, AUT. Full list of 250 codes via `GET /api/v2/tech/country-options` (under Agent Helpers). Auto-enables `enrich`. Companies missing a country code are dropped from the page when this filter is active."
          },
          {
            "name": "min_employee_count",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "null"
                }
              ],
              "title": "Min Employee Count"
            },
            "description": "Inclusive lower bound on company employee count. Auto-enables `enrich`. Companies with no employee count are dropped when this filter is active."
          },
          {
            "name": "max_employee_count",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": 0
                },
                {
                  "type": "null"
                }
              ],
              "title": "Max Employee Count"
            },
            "description": "Inclusive upper bound on company employee count. Auto-enables `enrich`. Companies with no employee count are dropped when this filter is active."
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompaniesResponse"
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/research/deep": {
      "post": {
        "tags": [
          "Company Research"
        ],
        "summary": "Deep Research",
        "description": "Decide whether a single, known company fits your criteria in terms of recent activity and organizational structure as a qualifier.\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>\n\nCombine an activity question (answered from job postings) and a team-composition question (answered from LinkedIn people) in a single call, with cited evidence per stage. Provide a `domain` plus at least one of `activity_question` or `qualifier_question`.\n\nIf you are not sure which domain OpenFunnel has indexed for a company, call [Lookup Companies](/helpers/lookup-companies) first. It accepts company names, domains, and LinkedIn URLs and returns the normalized company/domain records you can pass into Deep Research.\n\nEach parameter is hard-wired to a specific data source - pick the one that matches where the answer lives:\n- **`activity_question`** → answered from the company's **recent job postings**. Use for hiring intent, team expansion, tech-stack adoption visible in JDs, geographic hiring footprint, count/spike questions, first-time hiring or first-time JD mentions, and recent-hire intent.\n- **`qualifier_question`** → answered from **LinkedIn people data** (current-employee profiles). Use for team composition, roles already filled, org structure (e.g. \"has a Head of Security\", \"has ≥5 SDRs and a Sales Director\", \"has a dedicated AppSec team\").\n\nActivity question examples by category:\n- **Single-role hiring:** \"Is this company hiring SDRs based on recent job postings?\"; \"Do their recent job postings show developer relations engineer hiring?\"\n- **Initiative/team buildout:** \"Do their job descriptions suggest they are building an AI customer support automation team?\"; \"Do recent postings indicate they are staffing a data governance program?\"\n- **Tech/vendor/concept mentions:** \"Are they posting roles that mention Snowflake in at least 5 job descriptions in the last 90 days?\"; \"Do their job postings mention Salesforce more than 3 times this month?\"\n- **Role-count hiring:** \"Have they posted more than 5 SDR roles in the last 90 days?\"; \"Are they expanding platform engineering with at least 4 platform engineer postings in the last 60 days?\"\n- **Concept-count hiring:** \"Are they building around AI automation, with at least 4 related job postings this quarter?\"; \"Are they staffing generative AI work, with 6 or more related job postings in the last six months?\"\n- **Relative spikes:** \"Does their recent job activity show a spike in SDR hiring?\"; \"Are data engineer postings up 40% week over week?\"; \"Are Kubernetes mentions in their job descriptions up 80%?\"\n- **Recent-hire intent:** \"Does recent hiring activity suggest they brought someone in to build outbound sales?\"; \"Does recent hiring suggest they brought in a leader to stand up product security?\"\n- **First-time signals:** \"Is this the first time their job postings mention Kubernetes?\"; \"Are they hiring Solutions Engineers for the first time?\"; \"Does this look like their first Head of Data hire?\"\n\nQualifier question examples by category:\n- **Named team / product ownership:** \"Which engineers are in the Dropbox Dash team?\"; \"Which VP + sales people are in the AWS Textract team?\"\n- **Recent people changes:** \"New joiners in sales in the last 60 days\"; \"Any recent GTM engineering hires in sales in the last 90 days?\"\n- **Team-size / count checks:** \"Do they have more than 2 customer support people?\"; \"Do they have at least 5 SDRs/BDRs?\"\n- **Seniority / role coverage:** \"Do they have a VP or Head of Data Platform?\"; \"Do they have a Head of Security?\"\n- **Specialized teams / skills:** \"Do they have a dedicated AppSec / product-security team?\"; \"Do they employ Kubernetes-experienced platform engineers?\"\n\nPass both `activity_question` and `qualifier_question` when a question spans both signals - each stage runs against its own data source and returns independent evidence.\n\nEach stage returns a natural-language `answer` suitable for inline display, a boolean `qualified` verdict, and structured source evidence. Activity evidence is returned in `activity.sources`; qualifier evidence is returned in `qualifier.sources`. Job sources include title, URL, location, created timestamp, and why-relevant reason. People sources include name, title, LinkedIn URL, seniority, department, and start date when available.\n\nCredits: charged **per call**, not per source returned. 1 successful call (at least one stage produced a finding) = 1 charge at the per-call price configured for your workspace. A 404 (domain could not be resolved) is free. The exact amount billed is returned in `credits_consumed` on every response.",
        "operationId": "deep_research_api_v2_research_deep_post",
        "parameters": [
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            },
            "description": "Your OpenFunnel API key. Get one via [Agent Auth](/agent-primitives/authentication)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DeepResearchRequest"
              },
              "example": {
                "domain": "usepylon.com",
                "activity_question": "Have they posted more than 5 SDR roles in the last 90 days?",
                "qualifier_question": "Has at least 5 SDRs/BDRs and a Sales Director",
                "timeframe_days": 90,
                "max_jobs_to_check": 25
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Research findings with natural-language answers and source evidence.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeepResearchResponse"
                }
              }
            }
          },
          "400": {
            "description": "Insufficient credits to run the call."
          },
          "404": {
            "description": "Domain could not be resolved to a company in the OpenFunnel companies index. Check the domain or try the primary corporate domain."
          },
          "422": {
            "description": "Request validation error (e.g. neither `activity_question` nor `qualifier_question` provided, invalid domain format, out-of-range `timeframe_days`).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        }
      }
    },
    "/api/v2/tech/country-options": {
      "get": {
        "tags": [
          "Country Codes"
        ],
        "summary": "List Country Codes",
        "description": "Returns the ISO 3166-1 **alpha-3** country codes accepted by the Agent Primitive search endpoints. Ordered by company volume so the highest-coverage codes (USA, GBR, CHN, FRA, IND, DEU, ...) come first.\n\n**Where these codes are used:**\n- `GET /api/v2/tech/companies` - `hq_country_code` parameter.\n\nOne-shot discovery call - static, safe to cache.",
        "operationId": "tech_country_options",
        "parameters": [],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CountryOptionsResponse"
                }
              }
            }
          }
        }
      }
    },
    "/api/v1/account/firmographic-options": {
      "get": {
        "tags": [
          "Firmographic Options"
        ],
        "summary": "List Firmographic Options",
        "description": "Returns the firmographic filter values accepted by the lookalike search endpoints\nOne-shot discovery call - static, safe to cache.",
        "operationId": "account_firmographic_options",
        "parameters": [
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/FirmographicOptionsResponse"
                },
                "example": {
                  "min_employees": {
                    "minimum": 0,
                    "maximum": null
                  },
                  "max_employees": {
                    "minimum": 0,
                    "maximum": null
                  },
                  "funding_stages": [
                    "Pre-Seed",
                    "Seed",
                    "Series A",
                    "Series B",
                    "Series C",
                    "Series D",
                    "Series E",
                    "Series F",
                    "Series G",
                    "Series H",
                    "Acquired",
                    "Private",
                    "Public"
                  ],
                  "locations": [
                    {
                      "code": "USA",
                      "name": "United States"
                    },
                    {
                      "code": "CAN",
                      "name": "Canada"
                    }
                  ]
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "List firmographic options",
            "source": "curl -sS \"https://api.openfunnel.dev/api/v1/account/firmographic-options\" \\\n  -H \"X-API-Key: YOUR_API_KEY\""
          }
        ]
      }
    },
    "/api/v1/credits/balance": {
      "get": {
        "tags": [
          "Credits"
        ],
        "summary": "Get Credit Balance",
        "description": "Returns your account's credit balance: the total allowance, credits consumed and credits remaining.\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>",
        "operationId": "credits_balance",
        "parameters": [
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Successful Response",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreditBalanceResponse"
                },
                "example": {
                  "total_credits": 200,
                  "credits_used": 37.5,
                  "credits_left": 162.5
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "Get credit balance",
            "source": "curl -sS \"https://api.openfunnel.dev/api/v1/credits/balance\" \\\n  -H \"X-API-Key: YOUR_API_KEY\""
          }
        ]
      }
    },
    "/api/v1/account/search-lookalikes-bulk/{job_id}": {
      "get": {
        "tags": [
          "Search Lookalikes Bulk Results"
        ],
        "summary": "Search Lookalikes Bulk Results",
        "description": "Poll a bulk lookalike job and page through its results. Submit a job first via **`POST /api/v1/account/search-lookalikes-bulk`** (\"Search Lookalikes Bulk\", under Company Search), which returns a `job_id`.\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>\n\nThis single endpoint returns both the job `status` (`pending` → `running` → `completed` / `failed`), a `manifest` summary once complete, **and** one page of result `rows`. Walk pages by passing the returned `next_cursor` as `?cursor=`.\n\n**Check `status` for completion - not `next_cursor`.** A still-running job can return a null `next_cursor` simply because later pages haven't been written yet; only `status: \"completed\"` means the full result set is available.\n\nNo credits are charged for polling - the job's credits are billed once, when it completes.",
        "operationId": "search_lookalikes_bulk_status_api_v1_account_search_lookalikes_bulk__job_id__get",
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Job Id"
            },
            "description": "The `job_id` returned by the bulk submit endpoint."
          },
          {
            "name": "cursor",
            "in": "query",
            "required": false,
            "schema": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "null"
                }
              ],
              "title": "Cursor"
            },
            "description": "Page to read, e.g. `page_0003` (use the `next_cursor` from the previous response). Omit for the first page."
          },
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 100,
              "title": "Limit"
            },
            "description": "Accepted for forward-compatibility; page size is fixed at write time, so this is currently ignored."
          },
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Job status, manifest, and one page of results.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkLookalikeJobResponse"
                },
                "example": {
                  "job_id": "d8a1bddb-44f1-4c7f-87a7-f37496354ff6",
                  "status": "completed",
                  "manifest": {
                    "count": 600,
                    "pages": 6,
                    "page_size": 100,
                    "credits_consumed": 600,
                    "derived_query": null,
                    "resolved_seed_domains": null,
                    "unresolved_seed_domains": null,
                    "rerank_degraded": false,
                    "partial": false
                  },
                  "error_message": null,
                  "rows": [
                    {
                      "name": "Acme Observability",
                      "domain": "acme.com",
                      "linkedin_url": "https://www.linkedin.com/company/acme",
                      "headquarters": "San Francisco, United States",
                      "match_reason": "Describes itself as an \"observability platform\" for engineering teams.",
                      "employee_count": 120
                    }
                  ],
                  "page": "page_0000",
                  "next_cursor": "page_0001",
                  "count": 100,
                  "processed": 600,
                  "progress_message": "completed"
                }
              }
            }
          },
          "404": {
            "description": "Job not found for this API key."
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "Poll results",
            "source": "# first page (also shows status)\ncurl -sS \"https://api.openfunnel.dev/api/v1/account/search-lookalikes-bulk/JOB_ID\" \\\n  -H \"X-API-Key: YOUR_API_KEY\"\n\n# next page\ncurl -sS \"https://api.openfunnel.dev/api/v1/account/search-lookalikes-bulk/JOB_ID?cursor=page_0001\" \\\n  -H \"X-API-Key: YOUR_API_KEY\""
          }
        ]
      }
    },
    "/api/v1/account/search-lookalikes-bulk/{job_id}/cancel": {
      "post": {
        "tags": [
          "Cancel Bulk Lookalike Job"
        ],
        "summary": "Cancel Bulk Lookalike Job",
        "description": "Cancel a running or pending bulk lookalike job. Submit a job first via **`POST /api/v1/account/search-lookalikes-bulk`** (\"Search Lookalikes Bulk\", under Company Search), which returns a `job_id`.\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>\n\nThe job moves to a transient `cancelling` state and stops at its next checkpoint. Whatever pages were already produced are kept and remain readable via the poll endpoint, and you are charged **only for the companies actually delivered** before the cancel - nothing for the abandoned remainder. The job then settles to a terminal `cancelled` status.\n\nReturns **409** if the job has already finished (`completed` / `failed` / `cancelled`), and **404** if no job with that id exists for your API key. Poll **`GET /api/v1/account/search-lookalikes-bulk/{job_id}`** for the final `cancelled` state and the partial results.\n\n## When to cancel\nCancel when finishing the job is no longer worth it - e.g. you requested a large `limit` but already have enough companies from the pages delivered so far, the query turned out too broad, or you simply want to stop spend. Because credits are billed **only for companies actually delivered** before the cancel (never the abandoned remainder), cancelling early is how you cap credit usage on an over-scoped job.",
        "operationId": "search_lookalikes_bulk_cancel_api_v1_account_search_lookalikes_bulk__job_id__cancel_post",
        "parameters": [
          {
            "name": "job_id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "title": "Job Id"
            },
            "description": "The `job_id` returned by the bulk submit endpoint."
          },
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Cancellation accepted; the job is now `cancelling`.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BulkLookalikeCancelResponse"
                },
                "example": {
                  "job_id": "d8a1bddb-44f1-4c7f-87a7-f37496354ff6",
                  "status": "cancelling",
                  "message": "Cancellation requested. Poll GET /api/v1/account/search-lookalikes-bulk/d8a1bddb-44f1-4c7f-87a7-f37496354ff6 for the final status and the companies processed before cancellation."
                }
              }
            }
          },
          "404": {
            "description": "Job not found for this API key."
          },
          "409": {
            "description": "Job already finished (completed / failed / cancelled); cannot cancel."
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "Cancel job",
            "source": "curl -sS -X POST \"https://api.openfunnel.dev/api/v1/account/search-lookalikes-bulk/JOB_ID/cancel\" \\\n  -H \"X-API-Key: YOUR_API_KEY\""
          }
        ]
      }
    },
    "/api/v1/account/lookup-companies": {
      "post": {
        "tags": [
          "Company Lookup"
        ],
        "summary": "Lookup Companies",
        "description": "Resolve companies to their canonical OpenFunnel record(s) by any subset of **name / domain / linkedin_url**. Batch up to **100** per call. **No credits are charged.**\n\n<Info>**No API key yet?** [Sign up via Agent Auth](/agent-primitives/authentication) to get your `X-API-Key` - the only required header for this endpoint.</Info>\n\nEach item may carry any combination of identifiers:\n- **domain** → exact match on any of the company's domains. Returns all matching records.\n- **linkedin_url** → exact match on the company's LinkedIn slug (parsed from the URL).\n- **name** → fuzzy match; returns up to 10 ranked candidates with a relevance `score`.\n\nIf an item includes a `domain` or `linkedin_url`, it is resolved by that exact identifier and the `name` is ignored - the fuzzy name path runs only when `name` is the sole identifier. Matches are deduped by `company_id` (precedence: linkedin > domain > name). A returned `company_id` can be passed straight back into Search Lookalikes as a seed. Unknown inputs return `status: \"not_found\"` with an empty `matches` array.",
        "operationId": "lookup_companies_api_v1_account_lookup_companies_post",
        "parameters": [
          {
            "name": "X-API-Key",
            "in": "header",
            "required": true,
            "schema": {
              "type": "string",
              "title": "X-Api-Key"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CompanyLookupRequest"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "One result per input company, in request order.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CompanyLookupResponse"
                },
                "example": {
                  "results": [
                    {
                      "input": {
                        "name": null,
                        "domain": "stripe.com",
                        "linkedin_url": null
                      },
                      "status": "resolved",
                      "matches": [
                        {
                          "company_id": "ww_39dde6f3035d07e7e55e882f05ec6d7e",
                          "name": "Stripe",
                          "domain": "stripe.com",
                          "linkedin_url": "https://www.linkedin.com/company/stripe",
                          "linkedin_slug": "stripe",
                          "employee_count_min": 5000,
                          "employee_count_max": 10000,
                          "stage": "private_equity",
                          "industries": "Financial Services, FinTech",
                          "location_city": "San Francisco",
                          "location_country": "United States",
                          "location_country_code": "us",
                          "matched_on": "domain",
                          "score": null
                        }
                      ]
                    },
                    {
                      "input": {
                        "name": "Plaid",
                        "domain": null,
                        "linkedin_url": null
                      },
                      "status": "resolved",
                      "matches": [
                        {
                          "company_id": "ww_55c8eee3a23eab95f8cc650641deea75",
                          "name": "Plaid",
                          "domain": "plaid.com",
                          "linkedin_url": "https://www.linkedin.com/company/plaid",
                          "linkedin_slug": "plaid",
                          "employee_count_min": 1200,
                          "employee_count_max": 1200,
                          "stage": "series_unknown",
                          "industries": "Finance, Banking, FinTech",
                          "location_city": "San Francisco",
                          "location_country": "United States",
                          "location_country_code": "us",
                          "matched_on": "name",
                          "score": 0.97
                        }
                      ]
                    },
                    {
                      "input": {
                        "name": null,
                        "domain": "this-domain-does-not-exist-xyz.com",
                        "linkedin_url": null
                      },
                      "status": "not_found",
                      "matches": []
                    }
                  ]
                }
              }
            }
          },
          "422": {
            "description": "Validation Error",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/HTTPValidationError"
                }
              }
            }
          }
        },
        "x-codeSamples": [
          {
            "lang": "bash",
            "label": "Lookup companies",
            "source": "curl -sS -X POST \"https://api.openfunnel.dev/api/v1/account/lookup-companies\" \\\n  -H \"X-API-Key: YOUR_API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"companies\": [{\"domain\": \"stripe.com\"}, {\"linkedin_url\": \"https://www.linkedin.com/company/adyen\"}, {\"name\": \"Plaid\"}]}'"
          }
        ]
      }
    }
  },
  "components": {
    "schemas": {
      "InstantTraitSearchResponse": {
        "properties": {
          "query": {
            "type": "string",
            "title": "Query",
            "description": "Original user query. May be an empty string when `seed_domains` was provided without a `query`."
          },
          "results": {
            "items": {
              "$ref": "#/components/schemas/InstantTraitSearchResult"
            },
            "type": "array",
            "title": "Results",
            "description": "Matching companies"
          },
          "total": {
            "type": "integer",
            "title": "Total",
            "description": "Number of results returned"
          },
          "credits_consumed": {
            "type": "integer",
            "default": 0,
            "title": "Credits Consumed",
            "description": "Credits charged for this response. 1 credit per company returned. The LLM trait synthesis used by `seed_domains` is included - no extra charge."
          },
          "derived_query": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Derived Query",
            "description": "When `seed_domains` was provided, this is the LLM-synthesized trait query that was actually run against the lookalike pipeline. Null for pure-query requests."
          },
          "resolved_seed_domains": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Resolved Seed Domains",
            "description": "Seed input strings that matched a company in the OpenFunnel companies index. Null when `seed_domains` was not provided."
          },
          "unresolved_seed_domains": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Unresolved Seed Domains",
            "description": "Seed input strings that could not be resolved. Skipped silently as long as at least one seed resolved. Null when `seed_domains` was not provided."
          }
        },
        "type": "object",
        "required": [
          "query",
          "results",
          "total"
        ],
        "title": "InstantTraitSearchResponse",
        "description": "Response for the public lookalike search."
      },
      "InstantTraitSearchResult": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Company name"
          },
          "domain": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Domain",
            "description": "Primary company domain"
          },
          "linkedin_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Url",
            "description": "Company LinkedIn URL"
          },
          "headquarters": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Headquarters",
            "description": "Best available headquarters location"
          },
          "match_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Match Reason",
            "description": "One-sentence LLM justification for why this company matches the trait query, with the supporting phrases from the company's description / industries wrapped in double quotes. Useful for human review of why a result was returned. May be null if the validator did not attach a reason."
          },
          "employee_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Employee Count",
            "description": "Estimated employee count. ClickHouse firmographic enrichment; null when the company did not resolve in ClickHouse."
          },
          "funding_stage": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Funding Stage",
            "description": "Latest funding stage, e.g. 'Series C'."
          },
          "revenue_usd": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Revenue Usd",
            "description": "Estimated annual revenue, in USD."
          },
          "industries": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Industries",
            "description": "Industry tags."
          },
          "hq_city": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Hq City",
            "description": "HQ city."
          },
          "hq_region": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Hq Region",
            "description": "HQ state / region."
          },
          "hq_country": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Hq Country",
            "description": "HQ country."
          },
          "linkedin_follower_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Follower Count",
            "description": "LinkedIn follower count."
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description",
            "description": "Short company description."
          },
          "tagline": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Tagline",
            "description": "Company tagline / LinkedIn headline."
          }
        },
        "type": "object",
        "title": "InstantTraitSearchResult",
        "description": "Single company returned by the public lookalike search endpoint, including best-effort ClickHouse firmographic enrichment (by domain)."
      },
      "ServiceUnavailableResponse": {
        "type": "object",
        "properties": {
          "detail": {
            "type": "string",
            "description": "Human-readable explanation of why the service is unavailable."
          }
        },
        "title": "ServiceUnavailableResponse"
      },
      "HTTPValidationError": {
        "type": "object",
        "properties": {
          "detail": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/ValidationError"
            }
          }
        }
      },
      "ValidationError": {
        "type": "object",
        "required": [
          "loc",
          "msg",
          "type"
        ],
        "properties": {
          "loc": {
            "type": "array",
            "items": {
              "anyOf": [
                {
                  "type": "string"
                },
                {
                  "type": "integer"
                }
              ]
            }
          },
          "msg": {
            "type": "string"
          },
          "type": {
            "type": "string"
          }
        }
      },
      "CompaniesResponse": {
        "type": "object",
        "title": "CompaniesResponse",
        "description": "One page of companies that mention a tech in recent job postings. Companies are ranked by `post_count` descending; firmographics are populated when `enrich=true` or when any firmographic filter is set.",
        "required": [
          "tech",
          "lookback_days",
          "page",
          "page_size",
          "total_results",
          "has_more",
          "total_companies_matched",
          "companies"
        ],
        "properties": {
          "tech": {
            "type": "string",
            "title": "Tech",
            "description": "The technology phrase searched."
          },
          "variations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "default": [],
            "title": "Variations",
            "description": "Alternate phrasings that were OR-matched alongside `tech`."
          },
          "lookback_days": {
            "type": "integer",
            "title": "Lookback Days",
            "description": "Lookback window applied to the search, in days."
          },
          "page": {
            "type": "integer",
            "title": "Page",
            "description": "1-indexed page number returned."
          },
          "page_size": {
            "type": "integer",
            "title": "Page Size",
            "description": "Items per page. Fixed at 50."
          },
          "total_results": {
            "type": "integer",
            "title": "Total Results",
            "description": "Size of the addressable result set this page slices from. Without firmographic filters: bounded by the 50,000-result sanity cap (1,000 pages × 50). With filters: equals the size of the filtered set after firmographic hydration, which is what `has_more` and page indexing are computed against."
          },
          "has_more": {
            "type": "boolean",
            "title": "Has More",
            "description": "True when at least one more page is available (`page * page_size < total_results`). Loop while this is true to walk the full result set."
          },
          "total_companies_matched": {
            "type": "integer",
            "title": "Total Companies Matched",
            "description": "Approximate total distinct companies whose recent job postings match the search, BEFORE firmographic filters are applied. Compare with `total_results` to see how aggressively your filters narrowed the result."
          },
          "filters_applied": {
            "$ref": "#/components/schemas/FiltersApplied",
            "description": "Echo of the firmographic filters that produced this page."
          },
          "companies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyHit"
            },
            "title": "Companies",
            "description": "The ranked companies on this page."
          },
          "credits_consumed": {
            "type": "integer",
            "default": 0,
            "title": "Credits Consumed",
            "description": "Credits charged for this response. Computed as `companies_returned × per-company-cost`: 5 credits per company without enrichment, 10 credits per company with enrichment (or any firmographic filter set). The charge is recorded in the background after the response is sent, so it never adds to your request latency."
          },
          "latency_ms": {
            "type": "integer",
            "default": 0,
            "title": "Latency Ms",
            "description": "End-to-end request latency including cache lookup, OS, CH, and serialization."
          }
        }
      },
      "CompanyHit": {
        "type": "object",
        "title": "CompanyHit",
        "description": "One company in a `/api/v2/tech/companies` page. The hiring-signal block (slug, post_count, evidence, first/last posted dates) is always populated; the firmographics block (website, employee_count, funding, HQ, industries) is only populated when `enrich=true`.",
        "required": [
          "company_slug",
          "post_count"
        ],
        "properties": {
          "company_slug": {
            "type": "string",
            "title": "Company Slug",
            "description": "LinkedIn company slug (lowercased), e.g. 'stripe', 'snowflake-inc'."
          },
          "company_name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company Name",
            "description": "Display name. When `enrich=true` this is the canonical company name; otherwise it's the name as it appeared on the most recent matching job posting."
          },
          "post_count": {
            "type": "integer",
            "title": "Post Count",
            "description": "Number of matching job postings within the lookback window."
          },
          "first_posted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "First Posted At",
            "description": "ISO timestamp of the earliest matching posting."
          },
          "last_posted_at": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Last Posted At",
            "description": "ISO timestamp of the most recent matching posting - useful for detecting stale vs active hiring."
          },
          "evidence": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CompanyEvidence"
              },
              {
                "type": "null"
              }
            ],
            "description": "Top-hit job evidence for this company. Null when `include_evidence=false`."
          },
          "enriched": {
            "type": "boolean",
            "default": false,
            "title": "Enriched",
            "description": "True iff this hit carries firmographic data. False if `enrich=false` was passed, or if no firmographic record was found for this company."
          },
          "website": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Website",
            "description": "Primary company website (domain)."
          },
          "linkedin_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "LinkedIn URL",
            "description": "Canonical LinkedIn company page URL."
          },
          "linkedin_org_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "LinkedIn Org Id",
            "description": "Internal LinkedIn organization ID for the company."
          },
          "linkedin_follower_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "LinkedIn Follower Count",
            "description": "LinkedIn follower count at last refresh."
          },
          "employee_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Employee Count",
            "description": "Estimated current employee count."
          },
          "industries": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Industries",
            "description": "Industry tags associated with the company."
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description",
            "description": "Short company description, truncated to 500 characters."
          },
          "hq_city": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "HQ City",
            "description": "HQ city."
          },
          "hq_region": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "HQ Region",
            "description": "HQ state / region."
          },
          "hq_country": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "HQ Country",
            "description": "HQ country English name. Use `hq_country_code` for filtering / equality checks."
          },
          "hq_country_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "HQ Country Code",
            "description": "ISO 3166-1 alpha-3 (USA, GBR, IND, ...). The reliable, normalized country source - use this for filtering rather than `hq_country`."
          },
          "funding_stage": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Funding Stage",
            "description": "Normalized stage label, e.g. 'Seed', 'Series A', 'Series E', 'Public', 'Acquired', 'No Funding Yet'."
          },
          "total_funding_usd": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Total Funding USD",
            "description": "Cumulative funding raised, in USD."
          },
          "latest_funding_usd": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Latest Funding USD",
            "description": "Size of the most recent funding round, in USD."
          },
          "revenue_usd": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Revenue USD",
            "description": "Estimated annual revenue, in USD."
          }
        }
      },
      "CompanyEvidence": {
        "type": "object",
        "title": "CompanyEvidence",
        "description": "Evidence of the tech mention for a company - the most recent matching job posting, with a highlighted description snippet showing the phrase in context.",
        "properties": {
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Title",
            "description": "Job title of the evidence posting."
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "URL",
            "description": "Direct LinkedIn URL of the evidence job posting."
          },
          "created": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Created",
            "description": "ISO timestamp the evidence posting was created."
          },
          "snippet": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Snippet",
            "description": "Highlighted text fragment showing the tech phrase in context inside the job description."
          }
        }
      },
      "FiltersApplied": {
        "type": "object",
        "title": "FiltersApplied",
        "description": "Echo of the firmographic filters applied to this page. When any of these are set, the page is guaranteed to return up to `page_size` filter-matching hits (rather than 50 raw hits trimmed); walk further pages with `has_more`.",
        "properties": {
          "hq_country_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Hq Country Code",
            "description": "ISO 3166-1 alpha-3 HQ country code applied to this page, if any."
          },
          "min_employee_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Employee Count",
            "description": "Inclusive lower bound on employee count applied to this page, if any."
          },
          "max_employee_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Employee Count",
            "description": "Inclusive upper bound on employee count applied to this page, if any."
          }
        }
      },
      "DeepResearchRequest": {
        "properties": {
          "domain": {
            "type": "string",
            "title": "Domain",
            "description": "Company website domain to research (e.g. 'stripe.com'). Protocol and `www.` prefixes are stripped automatically. The domain is resolved to a LinkedIn org via the internal companies index before either stage runs. If this does not resolve, use [Lookup Companies](/helpers/lookup-companies) to find the normalized indexed domain.",
            "examples": [
              "stripe.com",
              "notion.so",
              "databricks.com"
            ]
          },
          "activity_question": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Activity Question",
            "description": "**Answered from the company's recent job postings.** Natural-language description of the activity signal to look for in open roles. Use this for hiring intent, team expansion, tech-stack or vendor adoption visible in JDs, geographic hiring footprint, count/spike questions, first-time hiring, first-time job-post mentions, or recent-hire intent. Examples: `Is this company hiring SDRs based on recent job postings?`, `Do their job descriptions suggest they are building an AI customer support automation team?`, `Are they posting roles that mention Snowflake in at least 5 job descriptions in the last 90 days?`, `Have they posted more than 5 SDR roles in the last 90 days?`, `Are data engineer postings up 40% week over week?`, `Is this the first time their job postings mention Kubernetes?`. Pass `null` or omit to skip this stage.",
            "examples": [
              "Is this company hiring SDRs based on recent job postings?",
              "Do their job descriptions suggest they are building an AI customer support automation team?",
              "Have they posted more than 5 SDR roles in the last 90 days?",
              "Are data engineer postings up 40% week over week?",
              "Is this the first time their job postings mention Kubernetes?"
            ]
          },
          "qualifier_question": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Qualifier Question",
            "description": "**Answered from LinkedIn people data** (current-employee profiles). Natural-language criteria about who already works at the company or how the org is shaped. Use this for team composition, named teams/products, roles already filled, seniority coverage, team-size/count checks, recent joiners or start-date questions, and skill/team specialization. Examples: `Which engineers are in the Dropbox Dash team?`, `New joiners in sales in the last 60 days`, `Do they have more than 2 customer support people?`, `Has at least 5 SDRs/BDRs and a Sales Director`, `Has a dedicated AppSec / product-security team`. Pass `null` or omit to skip this stage.",
            "examples": [
              "Which engineers are in the Dropbox Dash team?",
              "New joiners in sales in the last 60 days",
              "Do they have more than 2 customer support people?",
              "Has at least 5 SDRs/BDRs and a Sales Director",
              "Has a dedicated AppSec / product-security team",
              "Which VP + sales people are in the AWS Textract team?"
            ]
          },
          "timeframe_days": {
            "type": "integer",
            "maximum": 365.0,
            "minimum": 1.0,
            "title": "Timeframe Days",
            "description": "Lookback window (days) used by the activity stage. Has no effect when `activity_question` is omitted.",
            "default": 90
          },
          "max_jobs_to_check": {
            "type": "integer",
            "maximum": 200.0,
            "minimum": 1.0,
            "title": "Max Jobs To Check",
            "description": "Upper bound on the number of recent job postings the activity stage scores with the LLM relevance pass. Higher values raise recall but add latency / token cost (each job is judged by an LLM call). Default 25 is tuned for interactive use; bump it for batch / data-extraction workflows where you want maximum recall. Has no effect when `activity_question` is omitted.",
            "default": 25
          }
        },
        "type": "object",
        "required": [
          "domain"
        ],
        "title": "DeepResearchRequest",
        "description": "Inputs for a single Deep Research call.\n\nProvide a `domain` plus at least one of:\n- `activity_question` - answered from the company's recent **job postings** (hiring intent, team expansion, JD-visible tech/vendor signals, count/spike signals, first-time hiring or first-time JD mentions).\n- `qualifier_question` - answered from **LinkedIn people data** (team composition, named teams/products, roles already filled, recent joiners, team-size checks, org shape).\n\nPick the param whose backing data source contains the answer; pass both when the question spans both signals. If the company domain is ambiguous or does not resolve, use `POST /api/v1/account/lookup-companies` first to find the normalized domain OpenFunnel has indexed.",
        "examples": [
          {
            "domain": "usepylon.com",
            "activity_question": "Have they posted more than 5 SDR roles in the last 90 days?",
            "timeframe_days": 90,
            "max_jobs_to_check": 25
          },
          {
            "domain": "dropbox.com",
            "qualifier_question": "Which engineers are in the Dropbox Dash team?"
          },
          {
            "domain": "reducto.ai",
            "qualifier_question": "New joiners in sales in the last 60 days"
          },
          {
            "domain": "mintlify.com",
            "qualifier_question": "Do they have more than 2 customer support people?"
          },
          {
            "domain": "databricks.com",
            "activity_question": "Are they posting data roles that require Snowflake and dbt?",
            "qualifier_question": "Has a VP or Head of Data Platform",
            "timeframe_days": 60
          }
        ]
      },
      "QualifierFinding": {
        "properties": {
          "question": {
            "type": "string",
            "title": "Question",
            "description": "Echo of the qualifier question."
          },
          "qualified": {
            "type": "boolean",
            "title": "Qualified",
            "description": "True iff the company's team matches the criteria."
          },
          "answer": {
            "type": "string",
            "title": "Answer",
            "description": "One-sentence natural-language summary of the verdict - suitable for inline use in a CRM note or alert."
          },
          "detailed_reasoning": {
            "type": "string",
            "title": "Detailed Reasoning",
            "description": "Longer LLM-written reasoning: which signals matched, which didn't, and any caveats. May reference specific people."
          },
          "people_evaluated": {
            "type": "integer",
            "title": "People Evaluated",
            "description": "Number of LinkedIn profiles the LLM evaluated (after department / seniority pre-filtering)."
          },
          "sources": {
            "items": {
              "$ref": "#/components/schemas/PersonSource"
            },
            "type": "array",
            "title": "Sources",
            "description": "Profiles the LLM cited as matching the criteria. Empty when `qualified` is False or the criteria was about absence rather than presence."
          },
          "elapsed_ms": {
            "type": "integer",
            "title": "Elapsed Ms",
            "description": "Stage latency in milliseconds."
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error",
            "description": "Set when the stage failed mid-flight (e.g. company has no people indexed). `qualified` is False and `sources` is empty in this case."
          }
        },
        "type": "object",
        "required": [
          "question",
          "qualified",
          "answer",
          "detailed_reasoning",
          "people_evaluated",
          "elapsed_ms"
        ],
        "title": "QualifierFinding",
        "description": "Result of the people-qualifier stage."
      },
      "JobSource": {
        "properties": {
          "title": {
            "type": "string",
            "title": "Title",
            "description": "Job title as posted."
          },
          "url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Url",
            "description": "Canonical URL to the job posting."
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location",
            "description": "Posted location string."
          },
          "created": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Created",
            "description": "ISO timestamp of when the posting first appeared in the index."
          },
          "why_relevant": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Why Relevant",
            "description": "Short LLM-generated explanation of why this posting is evidence for the activity question."
          }
        },
        "type": "object",
        "required": [
          "title"
        ],
        "title": "JobSource",
        "description": "A job posting cited by the activity stage."
      },
      "DeepResearchResponse": {
        "properties": {
          "domain": {
            "type": "string",
            "title": "Domain",
            "description": "Echo of the requested domain (normalized)."
          },
          "company": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/CompanyInfo"
              },
              {
                "type": "null"
              }
            ],
            "description": "Resolved firmographic context for the company. Null only if the domain could not be resolved (in which case the endpoint returns 404 instead)."
          },
          "activity": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/ActivityFinding"
              },
              {
                "type": "null"
              }
            ],
            "description": "Findings for the activity question. Null when `activity_question` was not provided. Matching job source links are returned in `activity.sources`."
          },
          "qualifier": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/QualifierFinding"
              },
              {
                "type": "null"
              }
            ],
            "description": "Findings for the qualifier question. Null when `qualifier_question` was not provided. Matching people source links are returned in `qualifier.sources`."
          },
          "credits_consumed": {
            "type": "integer",
            "title": "Credits Consumed",
            "description": "Credits charged for this call. Charge is recorded in the background after the response is sent. 0 when the call failed before producing any findings.",
            "default": 0
          },
          "latency_ms": {
            "type": "integer",
            "title": "Latency Ms",
            "description": "End-to-end request latency in milliseconds."
          }
        },
        "type": "object",
        "required": [
          "domain",
          "latency_ms"
        ],
        "title": "DeepResearchResponse",
        "description": "Top-level Deep Research response. Stage-level `activity.sources` and `qualifier.sources` contain cited evidence."
      },
      "CompanyInfo": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Canonical company name."
          },
          "domain": {
            "type": "string",
            "title": "Domain",
            "description": "Echo of the requested domain (normalized)."
          },
          "linkedin_slug": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Slug",
            "description": "LinkedIn company slug (lowercased)."
          },
          "employee_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Employee Count",
            "description": "Estimated current employee count, if known."
          },
          "stage": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stage",
            "description": "Funding stage label (e.g. 'Series B', 'Public', 'Acquired')."
          },
          "industry": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Industry",
            "description": "Primary industry tag."
          },
          "location": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location",
            "description": "HQ city/region/country string."
          },
          "description": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Description",
            "description": "Short company description from the companies index, truncated."
          }
        },
        "type": "object",
        "required": [
          "domain"
        ],
        "title": "CompanyInfo",
        "description": "Resolved firmographic context for the researched company."
      },
      "PersonSource": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Full name from the profile."
          },
          "title": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Title",
            "description": "Current job title."
          },
          "linkedin_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Url",
            "description": "Canonical LinkedIn profile URL."
          },
          "seniority": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Seniority",
            "description": "Precomputed seniority bucket: FOUNDER, CXO, PARTNER, VP, HEAD, DIRECTOR, MANAGER, SENIOR, ENTRY, INTERN."
          },
          "department": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Department",
            "description": "Precomputed department bucket: ENGINEERING, PRODUCT, DATA, SALES, MARKETING, OPERATIONS, FINANCE, HR, LEGAL, EXECUTIVE, CUSTOMER_SUCCESS, SECURITY, IT, OTHER."
          },
          "started_on": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Started On",
            "description": "ISO start date of the person's current role (populated only when the qualifier question implied tenure / recency, e.g. 'joined in the last 90 days')."
          }
        },
        "type": "object",
        "title": "PersonSource",
        "description": "A person cited by the qualifier stage."
      },
      "ActivityFinding": {
        "properties": {
          "question": {
            "type": "string",
            "title": "Question",
            "description": "Echo of the activity question."
          },
          "qualified": {
            "type": "boolean",
            "title": "Qualified",
            "description": "True iff the company's recent job-posting activity matches the question with high enough signal to qualify."
          },
          "answer": {
            "type": "string",
            "title": "Answer",
            "description": "Natural-language answer (1-3 sentences) summarizing the verdict and the key evidence the LLM relied on."
          },
          "confidence": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Confidence",
            "description": "LLM-reported confidence label, typically 'low' / 'medium' / 'high'. May be empty when the qualifier returned a structured-recipe verdict without an explicit confidence."
          },
          "jobs_searched": {
            "type": "integer",
            "title": "Jobs Searched",
            "description": "Number of recent job postings considered before LLM relevance scoring."
          },
          "jobs_matched": {
            "type": "integer",
            "title": "Jobs Matched",
            "description": "Number of job postings the LLM judged relevant."
          },
          "sources": {
            "items": {
              "$ref": "#/components/schemas/JobSource"
            },
            "type": "array",
            "title": "Sources",
            "description": "Up to ~10 matching job postings used as evidence, ordered by recency."
          },
          "elapsed_ms": {
            "type": "integer",
            "title": "Elapsed Ms",
            "description": "Stage latency in milliseconds."
          },
          "error": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error",
            "description": "Set when the stage failed mid-flight (e.g. company missing from the jobs index). `qualified` is False and `sources` is empty in this case."
          }
        },
        "type": "object",
        "required": [
          "question",
          "qualified",
          "answer",
          "jobs_searched",
          "jobs_matched",
          "elapsed_ms"
        ],
        "title": "ActivityFinding",
        "description": "Result of the activity-question stage."
      },
      "BulkLookalikeSearchRequest": {
        "type": "object",
        "title": "BulkLookalikeSearchRequest",
        "description": "Request body for the async bulk lookalike search. Same fields as Search Lookalikes, but `limit` accepts up to 10000. At least one of `query` or `seed_domains` is required.",
        "properties": {
          "query": {
            "anyOf": [
              {
                "type": "string",
                "minLength": 1,
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "title": "Query",
            "description": "Describe the kinds of companies you want to find. Required unless `seed_domains` is provided. When both are supplied, used as a must-have angle on top of the seeds."
          },
          "seed_domains": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                },
                "maxItems": 10
              },
              {
                "type": "null"
              }
            ],
            "title": "Seed Domains",
            "description": "Up to 10 seed company websites/domains. When provided, OpenFunnel synthesizes the lookalike query for you."
          },
          "limit": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10000,
            "default": 100,
            "title": "Limit",
            "description": "Maximum number of companies to return. Default 100, max 10000. Large input set can take a few minutes to process."
          },
          "min_employees": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Min Employees",
            "description": "Minimum employee count, inclusive."
          },
          "max_employees": {
            "anyOf": [
              {
                "type": "integer",
                "minimum": 0
              },
              {
                "type": "null"
              }
            ],
            "title": "Max Employees",
            "description": "Maximum employee count, inclusive."
          },
          "funding_stages": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string",
                  "enum": [
                    "Pre-Seed",
                    "Seed",
                    "Series A",
                    "Series B",
                    "Series C",
                    "Series D",
                    "Series E",
                    "Series F",
                    "Series G",
                    "Series H",
                    "Acquired",
                    "Private",
                    "Public"
                  ]
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Funding Stages",
            "description": "Funding stages to include."
          },
          "locations": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Locations",
            "description": "Headquarters country filter using ISO 3166-1 alpha-3 codes (e.g. USA, GBR, IND, DEU). Pass EU to expand to all 27 EU member states."
          }
        }
      },
      "BulkLookalikeSubmitResponse": {
        "type": "object",
        "title": "BulkLookalikeSubmitResponse",
        "description": "Returned immediately when a bulk lookalike job is submitted.",
        "required": [
          "job_id",
          "message",
          "requested_top_k"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "title": "Job Id",
            "description": "Async job id. Poll `GET /api/v1/account/search-lookalikes-bulk/{job_id}` for status and results."
          },
          "status": {
            "type": "string",
            "default": "pending",
            "title": "Status",
            "description": "Job status at submit time (always `pending`)."
          },
          "message": {
            "type": "string",
            "title": "Message",
            "description": "Human-readable next-step hint."
          },
          "requested_top_k": {
            "type": "integer",
            "title": "Requested Top K",
            "description": "The requested result limit."
          }
        }
      },
      "CountryOptionsResponse": {
        "type": "object",
        "title": "CountryOptionsResponse",
        "description": "Static catalog of country codes accepted by the `country_code` filter on `/api/v2/tech/companies`.",
        "required": [
          "total",
          "countries"
        ],
        "properties": {
          "total": {
            "type": "integer",
            "title": "Total",
            "description": "Total number of supported country codes."
          },
          "countries": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CountryOption"
            },
            "title": "Countries",
            "description": "Country options ordered by company-data volume (most-populated first)."
          }
        }
      },
      "CountryOption": {
        "type": "object",
        "title": "CountryOption",
        "required": [
          "code",
          "name"
        ],
        "properties": {
          "code": {
            "type": "string",
            "title": "Code",
            "description": "ISO 3166-1 alpha-3 country code (e.g. 'USA', 'GBR', 'DEU')."
          },
          "name": {
            "type": "string",
            "title": "Name",
            "description": "Canonical English country name."
          }
        }
      },
      "FirmographicEmployeeBound": {
        "type": "object",
        "title": "FirmographicEmployeeBound",
        "description": "Accepted range for an employee-count filter on lookalike search.",
        "required": [
          "minimum"
        ],
        "properties": {
          "minimum": {
            "type": "integer",
            "title": "Minimum",
            "description": "Smallest accepted value (inclusive)."
          },
          "maximum": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Maximum",
            "description": "Largest accepted value (inclusive), or null if unbounded."
          }
        }
      },
      "FirmographicLocationOption": {
        "type": "object",
        "title": "FirmographicLocationOption",
        "description": "A supported HQ-country choice for the lookalike `locations` filter.",
        "required": [
          "code",
          "name"
        ],
        "properties": {
          "code": {
            "type": "string",
            "title": "Code",
            "description": "ISO 3166-1 alpha-3 code to pass in the `locations` parameter."
          },
          "name": {
            "type": "string",
            "title": "Name",
            "description": "Canonical country name."
          }
        }
      },
      "FirmographicOptionsResponse": {
        "type": "object",
        "title": "FirmographicOptionsResponse",
        "description": "Acceptable firmographic filter values for the lookalike search endpoints (`GET /search-lookalikes` and `POST /search-lookalikes-bulk`).",
        "required": [
          "min_employees",
          "max_employees",
          "funding_stages",
          "locations"
        ],
        "properties": {
          "min_employees": {
            "$ref": "#/components/schemas/FirmographicEmployeeBound",
            "description": "Bounds for the `min_employees` filter."
          },
          "max_employees": {
            "$ref": "#/components/schemas/FirmographicEmployeeBound",
            "description": "Bounds for the `max_employees` filter."
          },
          "funding_stages": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "title": "Funding Stages",
            "description": "Exact funding-stage labels accepted by `funding_stages`. Input is matched case-insensitively and canonicalized to these values."
          },
          "locations": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/FirmographicLocationOption"
            },
            "title": "Locations",
            "description": "Supported HQ-country filters. Pass the `code` in the `locations` parameter."
          }
        }
      },
      "CreditBalanceResponse": {
        "type": "object",
        "title": "CreditBalanceResponse",
        "description": "Your account's credit balance",
        "required": [
          "total_credits",
          "credits_used",
          "credits_left"
        ],
        "properties": {
          "total_credits": {
            "type": "number",
            "title": "Total Credits",
            "description": "The credit allowance for your billing account."
          },
          "credits_used": {
            "type": "number",
            "title": "Credits Used",
            "description": "Credits consumed"
          },
          "credits_left": {
            "type": "number",
            "title": "Credits Left",
            "description": "Remaining credits"
          }
        }
      },
      "BulkLookalikeManifest": {
        "type": "object",
        "title": "BulkLookalikeManifest",
        "description": "Summary of a completed (or partial) bulk lookalike job, returned on the poll endpoint once the job finishes.",
        "properties": {
          "count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Count",
            "description": "Total result rows produced. Null on a partial/failed job."
          },
          "pages": {
            "type": "integer",
            "title": "Pages",
            "description": "Number of result pages available to walk via the poll endpoint."
          },
          "page_size": {
            "type": "integer",
            "title": "Page Size",
            "description": "Rows per page."
          },
          "credits_consumed": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Credits Consumed",
            "description": "Credits charged for the job (1 per returned company)."
          },
          "derived_query": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Derived Query",
            "description": "LLM-synthesized trait when `seed_domains` was used. Null for pure-query requests."
          },
          "resolved_seed_domains": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Resolved Seed Domains"
          },
          "unresolved_seed_domains": {
            "anyOf": [
              {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              {
                "type": "null"
              }
            ],
            "title": "Unresolved Seed Domains"
          },
          "rerank_degraded": {
            "type": "boolean",
            "default": false,
            "title": "Rerank Degraded",
            "description": "True if relevance ranking partially fell back to retrieval order (results are still complete; ordering past the head is approximate)."
          },
          "partial": {
            "type": "boolean",
            "default": false,
            "title": "Partial",
            "description": "True if the job did not finish in full - failed OR cancelled after writing some pages; the pages that landed are still readable."
          }
        }
      },
      "BulkLookalikeJobResponse": {
        "type": "object",
        "title": "BulkLookalikeJobResponse",
        "description": "Combined job status + one page of results. Check `status` for completion (not `next_cursor`), and walk pages with `next_cursor`.",
        "required": [
          "job_id",
          "status"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "title": "Job Id"
          },
          "status": {
            "type": "string",
            "title": "Status",
            "description": "`pending`, `running`, `cancelling`, `cancelled`, `completed`, or `failed`. `cancelling` is transient (a cancel was requested); the job then settles to terminal `cancelled` with a `partial` manifest of the pages already delivered."
          },
          "manifest": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/BulkLookalikeManifest"
              },
              {
                "type": "null"
              }
            ],
            "description": "Job summary; populated once the job completes."
          },
          "error_message": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Error Message",
            "description": "Failure reason when `status` is `failed`."
          },
          "rows": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BulkLookalikeResultRow"
            },
            "title": "Rows",
            "description": "Companies in the requested page (same shape as Search Lookalikes results)."
          },
          "page": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Page",
            "description": "Name of the page returned, e.g. `page_0003`."
          },
          "next_cursor": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Next Cursor",
            "description": "Pass as `?cursor=` to fetch the next page; null on the last available page."
          },
          "count": {
            "type": "integer",
            "default": 0,
            "title": "Count",
            "description": "Number of rows in this page."
          },
          "processed": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Processed",
            "description": "Live progress: qualified companies delivered so far toward the requested limit. Null until the job starts producing progress."
          },
          "progress_message": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Progress Message",
            "description": "Coarse stage label, e.g. `retrieving_companies` / `qualifying_companies` / `reranking_companies` / `completed` / `cancelled`."
          }
        }
      },
      "BulkLookalikeCancelResponse": {
        "type": "object",
        "title": "BulkLookalikeCancelResponse",
        "description": "Acknowledgement returned by the bulk-lookalike cancel endpoint. Cancellation is asynchronous - poll the GET endpoint for the final `cancelled` state and the companies processed before the cancel.",
        "required": [
          "job_id",
          "message"
        ],
        "properties": {
          "job_id": {
            "type": "string",
            "title": "Job Id"
          },
          "status": {
            "type": "string",
            "default": "cancelling",
            "title": "Status",
            "description": "Always `cancelling` - the cancel request was accepted."
          },
          "message": {
            "type": "string",
            "title": "Message",
            "description": "Next step: where to fetch the companies processed before the cancel."
          }
        }
      },
      "CompanyLookupItem": {
        "type": "object",
        "title": "CompanyLookupItem",
        "description": "One company to resolve, identified by any subset of name/domain/linkedin_url. At least one is required.",
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string",
                "maxLength": 200
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Company name. Fuzzy - resolves to ranked candidates."
          },
          "domain": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Domain",
            "description": "Company website/domain (e.g. stripe.com). Matched on the primary domain."
          },
          "linkedin_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Url",
            "description": "Company LinkedIn URL. Matched on the company slug parsed from the URL."
          }
        }
      },
      "CompanyLookupRequest": {
        "type": "object",
        "title": "CompanyLookupRequest",
        "description": "Batch company lookup: resolve up to 100 companies by identifier.",
        "required": [
          "companies"
        ],
        "properties": {
          "companies": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyLookupItem"
            },
            "minItems": 1,
            "maxItems": 100,
            "title": "Companies",
            "description": "1-100 companies to resolve."
          }
        }
      },
      "CompanyLookupInput": {
        "type": "object",
        "title": "CompanyLookupInput",
        "description": "Echo of the identifiers supplied for a lookup item.",
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "domain": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Domain"
          },
          "linkedin_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Url"
          }
        }
      },
      "CompanyLookupMatch": {
        "type": "object",
        "title": "CompanyLookupMatch",
        "description": "A company matched against one lookup item.",
        "required": [
          "matched_on"
        ],
        "properties": {
          "company_id": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Company Id",
            "description": "Canonical company id, reusable as a seed in Search Lookalikes."
          },
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name"
          },
          "domain": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Domain"
          },
          "linkedin_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Url"
          },
          "linkedin_slug": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Slug"
          },
          "employee_count_min": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Employee Count Min"
          },
          "employee_count_max": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Employee Count Max"
          },
          "stage": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Stage"
          },
          "industries": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Industries"
          },
          "location_city": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location City"
          },
          "location_country": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location Country"
          },
          "location_country_code": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Location Country Code"
          },
          "matched_on": {
            "type": "string",
            "title": "Matched On",
            "description": "Which identifier matched: `domain`, `linkedin`, or `name`."
          },
          "score": {
            "anyOf": [
              {
                "type": "number"
              },
              {
                "type": "null"
              }
            ],
            "title": "Score",
            "description": "Relevance score (0-1) for `name` matches; null for exact domain/linkedin matches."
          }
        }
      },
      "CompanyLookupItemResult": {
        "type": "object",
        "title": "CompanyLookupItemResult",
        "description": "Resolution result for one input company.",
        "required": [
          "input",
          "status"
        ],
        "properties": {
          "input": {
            "$ref": "#/components/schemas/CompanyLookupInput",
            "description": "The identifiers supplied for this item."
          },
          "status": {
            "type": "string",
            "title": "Status",
            "description": "`resolved` if any match, else `not_found`."
          },
          "matches": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyLookupMatch"
            },
            "title": "Matches",
            "description": "Matched companies, deduped by company_id."
          }
        }
      },
      "CompanyLookupResponse": {
        "type": "object",
        "title": "CompanyLookupResponse",
        "description": "Response for the batch company lookup endpoint (one result per input, in request order).",
        "required": [
          "results"
        ],
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CompanyLookupItemResult"
            },
            "title": "Results"
          }
        }
      },
      "BulkLookalikeResultRow": {
        "properties": {
          "name": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Name",
            "description": "Company name"
          },
          "domain": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Domain",
            "description": "Primary company domain"
          },
          "linkedin_url": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Linkedin Url",
            "description": "Company LinkedIn URL"
          },
          "headquarters": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Headquarters",
            "description": "Best available headquarters location"
          },
          "match_reason": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ],
            "title": "Match Reason",
            "description": "One-sentence LLM justification for why this company matches the trait query, with the supporting phrases from the company's description / industries wrapped in double quotes. Useful for human review of why a result was returned. May be null if the validator did not attach a reason."
          },
          "employee_count": {
            "anyOf": [
              {
                "type": "integer"
              },
              {
                "type": "null"
              }
            ],
            "title": "Employee Count",
            "description": "Estimated employee count. ClickHouse firmographic enrichment; null when the company did not resolve in ClickHouse."
          }
        },
        "type": "object",
        "title": "InstantTraitSearchResult",
        "description": "Single company returned by the public lookalike search endpoint."
      }
    }
  }
}
