{
  "openapi": "3.0.1",
  "info": {
    "title": "Tech Stack & Buying Signal Enrichment",
    "description": "Detect what any website is built with: CMS, ecommerce, payments, analytics, tag managers, CDN, hosting, ATS, email and JavaScript frameworks, with evidence for every detection. Get a vendor_change record when a site swaps a tool. Pay per domain scanned, no subscription.",
    "version": "0.1",
    "x-build-id": "3bQwpAkhXaqCCsKsW"
  },
  "servers": [
    {
      "url": "https://api.apify.com/v2"
    }
  ],
  "paths": {
    "/acts/flux_pull~tech-stack-buying-signal-enrichment/run-sync-get-dataset-items": {
      "post": {
        "operationId": "run-sync-get-dataset-items-flux_pull-tech-stack-buying-signal-enrichment",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/acts/flux_pull~tech-stack-buying-signal-enrichment/runs": {
      "post": {
        "operationId": "runs-sync-flux_pull-tech-stack-buying-signal-enrichment",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor and returns information about the initiated run in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/runsResponseSchema"
                }
              }
            }
          }
        }
      }
    },
    "/acts/flux_pull~tech-stack-buying-signal-enrichment/run-sync": {
      "post": {
        "operationId": "run-sync-flux_pull-tech-stack-buying-signal-enrichment",
        "x-openai-isConsequential": false,
        "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
        "tags": [
          "Run Actor"
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/inputSchema"
              }
            }
          }
        },
        "parameters": [
          {
            "name": "token",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Enter your Apify token here"
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "inputSchema": {
        "type": "object",
        "properties": {
          "domains": {
            "title": "Domains",
            "maxItems": 200000,
            "uniqueItems": true,
            "type": "array",
            "description": "Domains or URLs to analyse. Bare hosts (example.com), full URLs, IDN and ports are all accepted. Duplicates are removed.",
            "items": {
              "type": "string"
            }
          },
          "domainsText": {
            "title": "Domains (pasted list)",
            "type": "string",
            "description": "Alternative to the list above: paste domains separated by newlines, commas, semicolons or spaces. Merged with 'Domains'."
          },
          "domainsUrl": {
            "title": "Domains file URL",
            "pattern": "^https?://",
            "type": "string",
            "description": "Public URL of a plain-text or CSV-first-column file, one domain per line. Fetched once at start-up (max 50 MB)."
          },
          "maxDomains": {
            "title": "Max domains",
            "minimum": 0,
            "type": "integer",
            "description": "Hard cap on domains processed this run. 0 means no cap. A safety net for oversized inputs.",
            "default": 0
          },
          "maxConcurrency": {
            "title": "Max concurrency",
            "minimum": 1,
            "maximum": 200,
            "type": "integer",
            "description": "Parallel domain scans. 20 is a good default; raise for large lists on 2 GB or more of memory.",
            "default": 20
          },
          "timeoutSecs": {
            "title": "Request timeout",
            "minimum": 3,
            "maximum": 60,
            "type": "integer",
            "description": "Per-HTTP-request timeout. A site that does not answer in time is cut off rather than holding up the run; DNS and TLS still run, and the row bills as domain_scanned if they detect anything, otherwise as domain_failed.",
            "default": 10
          },
          "followWww": {
            "title": "Try www. fallback",
            "type": "boolean",
            "description": "If the apex domain fails, retry the www. variant (and vice versa).",
            "default": true
          },
          "includeDns": {
            "title": "DNS lookups",
            "type": "boolean",
            "description": "TXT / MX / NS / CNAME / SOA records. Free (no bandwidth) and the source of email-provider and SaaS-verification signals. Keep this on.",
            "default": true
          },
          "includeDmarc": {
            "title": "DMARC lookup",
            "type": "boolean",
            "description": "One extra TXT query for _dmarc.<domain>. 'This company publishes a DMARC policy' is a free signal that they run real, managed email. Needs DNS lookups to be on.",
            "default": true
          },
          "includeTls": {
            "title": "TLS certificate",
            "type": "boolean",
            "description": "Certificate issuer, validity, SAN count, wildcard, TLS version. Free and useful for CDN/hosting attribution.",
            "default": true
          },
          "includeWellKnown": {
            "title": "Fetch /robots.txt and /sitemap.xml",
            "type": "boolean",
            "description": "Two extra small requests per domain. Adds robots-based detections, sitemap hints and some ATS signals.",
            "default": false
          },
          "inlineJsHeuristic": {
            "title": "Inline-JS heuristic",
            "type": "boolean",
            "description": "Approximate JavaScript-variable detections by searching inline script text, capped at 50 confidence and tagged as inlineJs evidence. Turn off for maximum precision.",
            "default": true
          },
          "minConfidence": {
            "title": "Minimum confidence",
            "minimum": 0,
            "maximum": 100,
            "type": "integer",
            "description": "Drop detections below this score. Set to 51 to exclude every inline-JS heuristic result.",
            "default": 50
          },
          "categoriesFilter": {
            "title": "Only these categories",
            "type": "array",
            "description": "Leave empty for the full stack. Filtering affects the output only; detection always runs in full so implied technologies stay correct.",
            "items": {
              "type": "string",
              "enum": [
                "CMS",
                "Ecommerce",
                "Analytics",
                "Payment processors",
                "CDN",
                "Hosting",
                "PaaS",
                "Tag managers",
                "Email",
                "Recruitment & staffing",
                "Live chat",
                "A/B Testing",
                "CRM",
                "Security",
                "JavaScript frameworks",
                "Web frameworks",
                "Cookie compliance",
                "Customer data platform",
                "Issue trackers",
                "Authentication",
                "Feature management",
                "Performance",
                "Static site generator",
                "Page builders",
                "Marketing automation",
                "Advertising",
                "Retargeting",
                "Personalisation",
                "Segmentation",
                "Reverse proxies",
                "Web servers",
                "SSL/TLS certificate authorities",
                "Widgets",
                "Video players",
                "Font scripts",
                "Miscellaneous"
              ]
            },
            "default": []
          },
          "renderJs": {
            "title": "Render JavaScript (Tier 2 — extra charge)",
            "type": "boolean",
            "description": "Launch headless Chromium to evaluate real JavaScript globals, DOM selectors and XHR hosts. Detects client-rendered tools that HTML alone cannot see. Billed as domain_rendered on top of domain_scanned.",
            "default": false
          },
          "renderConcurrency": {
            "title": "Render concurrency",
            "minimum": 1,
            "maximum": 16,
            "type": "integer",
            "description": "Parallel browser contexts. Each one needs about 1 GB of run memory on top of the first GB, so a run is capped at 1 render on 2 GB, 2 on 3 GB and 3 on 4 GB — a higher value here is clamped to what the run memory allows and the run log says so.",
            "default": 4
          },
          "renderTimeoutSecs": {
            "title": "Render timeout",
            "minimum": 5,
            "maximum": 120,
            "type": "integer",
            "description": "The browser's budget for one page: loading it and reading it, together. Separate from 'Request timeout', which bounds a single HTTP request — a browser loading a heavy page needs far longer than a plain fetch. A page that runs out stays on Tier 1 with renderSkipped: \"render_failed\" and nothing extra is charged. Heavy pages behind a proxy need 20–40 seconds.",
            "default": 25
          },
          "renderBlockResources": {
            "title": "Block resources while rendering",
            "enum": [
              "default",
              "aggressive",
              "none"
            ],
            "type": "string",
            "description": "Which bytes the browser refuses to download. 'default' blocks images, media and fonts — none of them carry a technology fingerprint, and they are most of a page's weight. 'aggressive' also blocks stylesheets, which is faster still but turns off stylesheet-based detections. 'none' downloads everything and is for debugging a render that looks wrong.",
            "default": "default"
          },
          "trackChanges": {
            "title": "Detect vendor changes",
            "type": "boolean",
            "description": "Compare each domain against your last scan and emit a vendor_change record when the stack differs. The first scan of a domain never produces a change.",
            "default": true
          },
          "snapshotStoreName": {
            "title": "Snapshot store name",
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]$",
            "type": "string",
            "description": "Named key-value store holding your scan history. Use different names to keep separate tracking sets.",
            "default": "tech-stack-snapshots"
          },
          "changesDatasetName": {
            "title": "Change feed dataset name",
            "pattern": "^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]$",
            "type": "string",
            "description": "Named dataset that collects every vendor_change record, run after run — your change feed. It is created the first time a change is confirmed. This run's own dataset keeps exactly one row per domain, so the two never mix.",
            "default": "tech-stack-changes"
          },
          "changeMinConfidence": {
            "title": "Change confidence floor",
            "minimum": 50,
            "maximum": 100,
            "type": "integer",
            "description": "A technology must score at least this to count as added or removed. Higher than the output floor on purpose, so low-confidence noise never bills you for a change.",
            "default": 75
          },
          "changeConfirmScans": {
            "title": "Confirm a change over N scans",
            "minimum": 1,
            "maximum": 5,
            "type": "integer",
            "description": "A technology must be seen in its new state this many scans in a row before it counts as a change and is billed. Ad-heavy sites load a different set of tags on every visit, and 2 is enough to tell that apart from a real move: the row shows the candidate under changes.pending meanwhile, and nothing is charged. Set to 1 to bill on the first scan that sees a difference.",
            "default": 2
          },
          "purgeSnapshots": {
            "title": "Delete stored scan history first",
            "type": "boolean",
            "description": "Empty the snapshot store before this run starts. Every domain then looks like a first scan, so no vendor_change is emitted and nothing is billed for one. This is how you honour a deletion request or start a tracking set over.",
            "default": false
          },
          "dryRun": {
            "title": "Dry run (do not save snapshots)",
            "type": "boolean",
            "description": "Compute changes but leave the stored history untouched, and charge nothing. Useful for testing and for costing a large run before you pay for it.",
            "default": false
          },
          "proxy": {
            "title": "Proxy configuration",
            "type": "object",
            "description": "Not needed for normal use: we fetch your own target domains and expect no anti-bot. Enable the default datacenter proxy if a target blocks Apify IPs, or supply your own proxy URLs. Residential proxies are NOT supported: this Actor's per-event price includes the bandwidth, and a run configured with the RESIDENTIAL group stops immediately with an explanation rather than scanning. Datacenter proxy usage is included in the event price and is never billed to you on top.",
            "default": {
              "useApifyProxy": false
            }
          },
          "userAgent": {
            "title": "Custom User-Agent",
            "type": "string",
            "description": "Leave empty to use a realistic Chrome fingerprint (recommended: some sites serve different markup to bots). Set your own to identify your crawler.",
            "default": ""
          }
        }
      },
      "runsResponseSchema": {
        "type": "object",
        "properties": {
          "data": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "actId": {
                "type": "string"
              },
              "userId": {
                "type": "string"
              },
              "startedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "finishedAt": {
                "type": "string",
                "format": "date-time",
                "example": "2025-01-08T00:00:00.000Z"
              },
              "status": {
                "type": "string",
                "example": "READY"
              },
              "meta": {
                "type": "object",
                "properties": {
                  "origin": {
                    "type": "string",
                    "example": "API"
                  },
                  "userAgent": {
                    "type": "string"
                  }
                }
              },
              "stats": {
                "type": "object",
                "properties": {
                  "inputBodyLen": {
                    "type": "integer",
                    "example": 2000
                  },
                  "rebootCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "restartCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "resurrectCount": {
                    "type": "integer",
                    "example": 0
                  },
                  "computeUnits": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "options": {
                "type": "object",
                "properties": {
                  "build": {
                    "type": "string",
                    "example": "latest"
                  },
                  "timeoutSecs": {
                    "type": "integer",
                    "example": 300
                  },
                  "memoryMbytes": {
                    "type": "integer",
                    "example": 1024
                  },
                  "diskMbytes": {
                    "type": "integer",
                    "example": 2048
                  }
                }
              },
              "buildId": {
                "type": "string"
              },
              "defaultKeyValueStoreId": {
                "type": "string"
              },
              "defaultDatasetId": {
                "type": "string"
              },
              "defaultRequestQueueId": {
                "type": "string"
              },
              "buildNumber": {
                "type": "string",
                "example": "1.0.0"
              },
              "containerUrl": {
                "type": "string"
              },
              "usage": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "integer",
                    "example": 1
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              },
              "usageTotalUsd": {
                "type": "number",
                "example": 0.00005
              },
              "usageUsd": {
                "type": "object",
                "properties": {
                  "ACTOR_COMPUTE_UNITS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATASET_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "KEY_VALUE_STORE_WRITES": {
                    "type": "number",
                    "example": 0.00005
                  },
                  "KEY_VALUE_STORE_LISTS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_READS": {
                    "type": "integer",
                    "example": 0
                  },
                  "REQUEST_QUEUE_WRITES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_INTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "DATA_TRANSFER_EXTERNAL_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                    "type": "integer",
                    "example": 0
                  },
                  "PROXY_SERPS": {
                    "type": "integer",
                    "example": 0
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}