> ## Documentation Index
> Fetch the complete documentation index at: https://technologychecker.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Category whitespace API

> Find domains that run a qualifying anchor technology but no tool at all in a target category — greenfield prospecting, the inverse of adoption. Costs 1 credit.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="What is whitespace, in one sentence?">
    Domains that visibly have not bought yet: they run an anchor technology you can qualify on, and run nothing at all in the category you sell into. "Shopify stores with no email marketing tool" is the canonical query.
  </Accordion>

  <Accordion title="Why is there no window parameter?">
    Whitespace is a current-state signal about presence and absence, not a transition between two states. There is nothing to look back over. This also means it works from day one and needs no monitoring-window maturity, unlike adoption and churn.
  </Accordion>

  <Accordion title="How conservative is the gap detection?">
    Very. Any active detection of a technology in the missing category disqualifies the domain, even a flaky one, and the check runs at domain level rather than per subdomain — if the company has the tool anywhere, they are excluded. We would rather drop a real prospect than surface a false gap. The anchor side is the opposite: it is gated by `min_confidence` so the qualifying presence is trustworthy.
  </Accordion>

  <Accordion title="How do I page through millions of matches?">
    Use `limit` with `offset`. Whitespace matches routinely run into the millions and results are ordered alphabetically by domain, so paging is stable within one corpus generation. This is the only signal endpoint with offset paging — the transition signals are recency-ordered, where the newest rows are the product.
  </Accordion>

  <Accordion title="Why do I need an anchor at all?">
    An unqualified "every domain without category C" query is rejected with a 400. It would return most of the internet and would not be a prospect list. The anchor is what makes the result actionable.
  </Accordion>
</AccordionGroup>


## OpenAPI

````yaml GET /v1/signals/whitespace
openapi: 3.1.0
info:
  title: TechnologyChecker API
  description: >-
    Discover technology stacks, track market trends, and enrich data with
    company intelligence.
  version: 1.0.0
  contact:
    name: TechnologyChecker Support
    email: support@technologychecker.io
    url: https://technologychecker.io
servers:
  - url: https://api.technologychecker.io
    description: Production
security:
  - bearerAuth: []
tags:
  - name: Domain technologies
    description: Look up technologies detected on any domain
  - name: Technology data
    description: Search, look up, and get details on tracked technologies
  - name: Market intelligence
    description: Technology stats, trends, and market share
  - name: Company data
    description: Firmographic and company intelligence
  - name: Live detection
    description: Real-time technology detection with browser rendering
  - name: Signals
    description: >-
      Intent signals derived from technology state transitions: who adopted, who
      churned, who switched, and who has a gap in their stack.
  - name: Alerts
    description: >-
      Saved signal queries delivered to your webhook. Every signal you can
      query, you can subscribe to.
  - name: Audiences
    description: >-
      Saved groups of domains — uploaded lists or computed recipes — that you
      can profile, slice and monitor.
  - name: Lead lists
    description: >-
      Build and export targeted domain lists combining technology detection with
      company data.
paths:
  /v1/signals/whitespace:
    get:
      tags:
        - Signals
      summary: Category whitespace API
      description: >-
        Greenfield prospecting: domains that **have** a qualifying anchor
        technology but run **no tool at all** in a target category. The
        structural inverse of adoption — not who just bought, but who visibly
        has not yet.


        This is a current-state signal rather than a transition, so there is no
        `window` parameter. It is also the cheapest signal to run, and supports
        offset pagination because matches routinely run into the millions.


        Costs 1 credit.
      operationId: getCategoryWhitespace
      parameters:
        - name: technology_id
          in: query
          required: false
          description: >-
            Scope by BuiltWith technology id. Provide exactly one of
            `technology_id`, `technology` or `category_id`.
          schema:
            type: integer
          example: 2184
        - name: technology
          in: query
          required: false
          description: Scope by exact technology name. The technology must be detectable.
          schema:
            type: string
          example: Shopify
        - name: category_id
          in: query
          required: false
          description: >-
            Scope by category — fans out to every detectable technology whose
            *primary* category is this one.
          schema:
            type: integer
          example: 202
        - name: missing_category_id
          in: query
          required: false
          description: >-
            **Required.** The category the domain must run no tool in. Must
            differ from the anchor category.
          schema:
            type: integer
          example: 202
        - name: min_confidence
          in: query
          required: false
          description: >-
            Minimum `detection_rate` gating the *anchor* presence. The gap side
            counts any active detection, which is deliberately conservative.
          schema:
            type: number
            format: float
            default: 0.5
        - name: limit
          in: query
          required: false
          description: Rows returned. Clamped to 1–1000.
          schema:
            type: integer
            default: 100
            minimum: 1
            maximum: 1000
        - name: offset
          in: query
          required: false
          description: >-
            Pagination offset, clamped to 0–1,000,000. Results are ordered
            alphabetically by domain, so paging is stable within one corpus
            generation.
          schema:
            type: integer
            default: 0
        - $ref: '#/components/parameters/FirmoCountry'
        - $ref: '#/components/parameters/FirmoCity'
        - $ref: '#/components/parameters/FirmoState'
        - $ref: '#/components/parameters/FirmoIndustry'
        - $ref: '#/components/parameters/FirmoIndustryCode'
        - $ref: '#/components/parameters/FirmoEmployees'
        - $ref: '#/components/parameters/FirmoCompanyType'
        - $ref: '#/components/parameters/FirmoFoundedMin'
        - $ref: '#/components/parameters/FirmoFoundedMax'
        - $ref: '#/components/parameters/FirmoCompany'
        - $ref: '#/components/parameters/WebHasEmail'
        - $ref: '#/components/parameters/WebHasPhone'
        - $ref: '#/components/parameters/WebSocial'
        - $ref: '#/components/parameters/WebLanguage'
        - $ref: '#/components/parameters/WebCountry'
        - $ref: '#/components/parameters/SegmentId'
      responses:
        '200':
          description: Domains with the anchor and no tool in the target category
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      signal:
                        type: object
                        properties:
                          type:
                            type: string
                            example: whitespace
                          verb:
                            type: string
                            example: lacks
                      anchor:
                        type: object
                        description: What the domains have.
                        properties:
                          kind:
                            type: string
                            enum:
                              - technology
                              - category
                          technology:
                            type: object
                            nullable: true
                          category:
                            type: object
                            nullable: true
                      missing_category:
                        type: object
                        description: What the domains lack.
                        properties:
                          id:
                            type: integer
                            example: 202
                          name:
                            type: string
                            example: Email Marketing
                          technology_count:
                            type: integer
                            example: 37
                      min_confidence:
                        type: number
                        format: float
                        example: 0.5
                      total_sites:
                        type: integer
                        nullable: true
                        description: >-
                          Corpus-wide count of whitespace domains, not capped by
                          `limit`.
                        example: 911747
                      domains:
                        type: array
                        items:
                          type: object
                          properties:
                            domain:
                              type: string
                              example: 0-5.fr
                            company:
                              $ref: '#/components/schemas/CompanyCard'
                      firmographics:
                        $ref: '#/components/schemas/FirmographicsEcho'
                      limit:
                        type: integer
                        example: 100
                      offset:
                        type: integer
                        example: 0
                      count:
                        type: integer
                        example: 1
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PaymentRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  parameters:
    FirmoCountry:
      name: country
      in: query
      required: false
      description: Filter to companies in this country (case-insensitive).
      schema:
        type: string
      example: united states
    FirmoCity:
      name: city
      in: query
      required: false
      description: Filter to companies in this city (case-insensitive).
      schema:
        type: string
    FirmoState:
      name: state
      in: query
      required: false
      description: Filter to companies in this state or region (case-insensitive).
      schema:
        type: string
    FirmoIndustry:
      name: industry
      in: query
      required: false
      description: Filter to companies in this industry (case-insensitive).
      schema:
        type: string
      example: retail
    FirmoIndustryCode:
      name: industry_code
      in: query
      required: false
      description: Filter by exact LinkedIn industry code.
      schema:
        type: integer
    FirmoEmployees:
      name: employees
      in: query
      required: false
      description: >-
        Filter by exact LinkedIn employee band. Unknown bands are rejected with
        `INVALID_EMPLOYEE_BAND` and the allowed list.
      schema:
        type: string
      example: 51-200
    FirmoCompanyType:
      name: company_type
      in: query
      required: false
      description: Filter by exact company type.
      schema:
        type: string
      example: Privately Held
    FirmoFoundedMin:
      name: founded_min
      in: query
      required: false
      description: Only companies founded in or after this year.
      schema:
        type: integer
        minimum: 1800
        maximum: 2025
    FirmoFoundedMax:
      name: founded_max
      in: query
      required: false
      description: Only companies founded in or before this year.
      schema:
        type: integer
        minimum: 1800
        maximum: 2025
    FirmoCompany:
      name: company
      in: query
      required: false
      description: >-
        Set to `true` to attach the company card without filtering results
        (enrich-only mode).
      schema:
        type: boolean
      example: true
    WebHasEmail:
      name: has_email
      in: query
      required: false
      description: >-
        Filter to domains where the crawler found (or did not find) an email
        address. `false` is a real filter, not a no-op.
      schema:
        type: boolean
    WebHasPhone:
      name: has_phone
      in: query
      required: false
      description: >-
        Filter to domains where the crawler found (or did not find) a phone
        number.
      schema:
        type: boolean
    WebSocial:
      name: social
      in: query
      required: false
      description: Filter to domains publishing a profile on this platform.
      schema:
        type: string
        enum:
          - linkedin
          - x
          - facebook
          - instagram
          - youtube
          - github
          - tiktok
          - discord
          - reddit
          - crunchbase
          - slack
      example: instagram
    WebLanguage:
      name: language
      in: query
      required: false
      description: Filter by the site's primary language.
      schema:
        type: string
      example: de
    WebCountry:
      name: web_country
      in: query
      required: false
      description: >-
        Filter by the country the site declares in its schema.org markup.
        Accepts ISO-2, English name or common variants.
      schema:
        type: string
      example: USA
    SegmentId:
      name: segment_id
      in: query
      required: false
      description: >-
        Apply a saved audience instead of inline filters. Filter audiences
        replay their firmographic predicates; list audiences restrict rows to
        their member domains. Mutually exclusive with inline firmographic
        parameters.
      schema:
        type: string
      example: seg_8f3a2c1b
  schemas:
    CompanyCard:
      type: object
      nullable: true
      description: >-
        LinkedIn company card attached when firmographic enrichment is on.
        `null` when the domain has no LinkedIn match (roughly 23–30% of
        technology domains match).
      properties:
        name:
          type: string
          example: Ghostcircusapparel
        industry:
          type: string
          example: Retail Apparel and Fashion
        industry_code:
          type: integer
          example: 18
        employees:
          type: string
          example: 51-200
        country:
          type: string
          example: united states
        city:
          type: string
          nullable: true
        state:
          type: string
          nullable: true
        founded:
          type: integer
          nullable: true
          example: 2015
        company_type:
          type: string
          example: Privately Held
    FirmographicsEcho:
      type: object
      description: Echo of the firmographic predicates that took effect on this request.
      properties:
        applied:
          type: object
          additionalProperties: true
          example:
            country: united states
            employees: 51-200
        filtered:
          type: boolean
          description: >-
            `true` when results were narrowed to matched companies; `false` for
            enrich-only (`company=true`).
          example: true
        note:
          type: string
          example: >-
            Results are limited to domains with a matched LinkedIn company that
            passes these filters (~23–30% of tech-domains have a company match).
    ApiError:
      type: object
      properties:
        success:
          type: boolean
          example: false
        error:
          type: object
          properties:
            code:
              type: string
              example: UNAUTHORIZED
            message:
              type: string
              example: Missing or invalid API key
  responses:
    BadRequest:
      description: Invalid or missing parameters
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            success: false
            error:
              code: VALIDATION_ERROR
              message: >-
                Provide exactly one scope: technology_id, technology or
                category_id.
    Unauthorized:
      description: Missing or invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    PaymentRequired:
      description: Not enough credits, or the plan does not include this feature
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            success: false
            error:
              code: INSUFFICIENT_CREDITS
              message: Insufficient credits for this request.
              details:
                required: 1
                available: 0
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
    RateLimited:
      description: Rate limit exceeded
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        API key in format `tapi_live_[32-char]` (live) or `tapi_test_[32-char]`
        (test)

````