> ## 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.

# Competitive switch API

> Find domains that replaced one technology with a competing technology in the same category, with a destination flow chart. Costs 1 credit per request.

## Frequently asked questions

<AccordionGroup>
  <Accordion title="What counts as a switch?">
    A domain dropped technology A and adopted a competing technology B on the same host, with both events falling inside the window. Competitors are defined as technologies sharing the same *primary* category, so there is no such thing as a cross-category switch. That is deliberate, and it doubles as a guard: a web server moving behind a CDN is two different categories, so it can never be misread as a migration.
  </Accordion>

  <Accordion title="What is the flow array?">
    The destination distribution for your scope, corpus-wide and not capped by `limit` — the "247 left this technology, 69 went to WooCommerce" chart. Under a firmographic filter the flow shrinks to the matched cohort too. It is best-effort: on very large categories it can degrade to `null` rather than failing the request.
  </Accordion>

  <Accordion title="How do I read gap_days?">
    It is the absolute number of days between the churn and the adoption. A genuine in-place swap shows a tiny gap, usually 0 or 1 day, because the same re-crawl that first saw A gone also saw B present. A large gap suggests the two events are unrelated rather than a migration. Row `confidence` is graded from `gap_days` together with the churn side's detection rate.
  </Accordion>

  <Accordion title="What is the difference between direction=from and direction=to?">
    On a technology scope, `from` returns who left your anchor technology and where they went — the save-play list. `to` returns who arrived at your anchor and which competitor they left — the win list and case-study source. Direction is ignored on a category scope, which returns every migration inside the category.
  </Accordion>

  <Accordion title="Why is a switch I expected missing?">
    Three common reasons. The two technologies may not share a primary category, in which case they are not competitors here. Both events must fall inside `window`, so widen it. Or the churn side may fall below `min_confidence`. Note also that a switch whose from-side is a known flapper on that host is downgraded to `low` confidence rather than dropped, so check the confidence field before assuming it is absent.
  </Accordion>
</AccordionGroup>


## OpenAPI

````yaml GET /v1/signals/switch
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/switch:
    get:
      tags:
        - Signals
      summary: Competitive switch API
      description: >-
        Domains that dropped one technology and adopted a competing technology
        in the same category inside the window. The sharpest in-market signal:
        who left your competitor for you, and who is leaving you.


        Competitors are defined by shared *primary* category, so there is no
        cross-category switch. Alongside the rows, `flow` returns the
        corpus-wide destination distribution — the "247 left X, 69 went to Y"
        chart.


        Costs 1 credit.
      operationId: getCompetitiveSwitches
      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: direction
          in: query
          required: false
          description: >-
            Technology scope only. `from` returns who left the anchor and where
            they went; `to` returns who came to the anchor and from where.
            Ignored for category scope.
          schema:
            type: string
            enum:
              - from
              - to
            default: from
        - name: window
          in: query
          required: false
          description: >-
            Look-back window in days. Both the churn and the adoption must fall
            inside it. Clamped to 1–365.
          schema:
            type: string
            default: '30'
          example: '90'
        - name: min_confidence
          in: query
          required: false
          description: Minimum `detection_rate` on the churn side.
          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
        - $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: Switch events and the destination distribution
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                    example: true
                  data:
                    type: object
                    properties:
                      signal:
                        type: object
                        properties:
                          type:
                            type: string
                            example: switch
                          verb:
                            type: string
                            example: switched
                      scope:
                        type: object
                        properties:
                          kind:
                            type: string
                            enum:
                              - technology
                              - category
                          direction:
                            type: string
                            nullable: true
                            enum:
                              - from
                              - to
                              - null
                      technology:
                        type: object
                        nullable: true
                      category:
                        type: object
                        nullable: true
                      window_days:
                        type: integer
                        example: 30
                      min_confidence:
                        type: number
                        format: float
                        example: 0.5
                      flow:
                        type: array
                        nullable: true
                        description: >-
                          Distinct sites per destination technology, corpus-wide
                          and not capped by `limit`. Best-effort — `null` if the
                          query timed out.
                        items:
                          type: object
                          properties:
                            to_id:
                              type: integer
                              example: 402
                            to_name:
                              type: string
                              example: WooCommerce
                            sites:
                              type: integer
                              example: 69
                      switches:
                        type: array
                        items:
                          $ref: '#/components/schemas/SwitchEvent'
                      firmographics:
                        $ref: '#/components/schemas/FirmographicsEcho'
                      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:
    SwitchEvent:
      type: object
      description: >-
        One competitive switch: a domain dropped technology A and adopted a
        competing technology B in the same category.
      properties:
        domain:
          type: string
          example: indigo.ca
        subdomain:
          type: string
          example: ''
        detection_url:
          type: string
          example: https://indigo.ca
        from:
          type: object
          properties:
            id:
              type: integer
              example: 12
            name:
              type: string
              example: Salesforce Commerce Cloud
        to:
          type: object
          properties:
            id:
              type: integer
              example: 2184
            name:
              type: string
              example: Shopify
        category:
          type: string
          example: Ecommerce Platforms
        churned_at:
          type: string
          example: '2026-05-20 14:02:11'
        adopted_at:
          type: string
          example: '2026-05-20 14:02:11'
        gap_days:
          type: integer
          description: >-
            Absolute days between the churn and the adoption. A genuine in-place
            swap shows a tiny gap (0–1 days).
          example: 0
        detection_rate:
          type: number
          format: float
          example: 0.5
        confidence:
          type: string
          enum:
            - high
            - medium
            - low
          description: Graded from `gap_days` and the churn side's detection rate.
          example: medium
        company:
          $ref: '#/components/schemas/CompanyCard'
    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).
    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
    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)

````