# Website Technology Detector — Tech Stack Lookup (`insight.solutions/website-tech-detector`) Actor

Find out what any website runs on. One row per site: CMS, e-commerce platform, JavaScript framework, analytics, ad pixels, live chat, payments, CDN, hosting, web server and security, each with the evidence that proved it. 232 signatures, no API key, no browser.

- **URL**: https://apify.com/insight.solutions/website-tech-detector.md
- **Developed by:** [Insight Solutions](https://apify.com/insight.solutions) (community)
- **Categories:** Lead generation, Marketing, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.60 / 1,000 site analyseds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## Website Technology Detector — Tech Stack Lookup

**Find out what any website runs on.** Give this Actor a list of domains and get back one row per site: the CMS, the store platform, the JavaScript framework, every analytics tag and ad pixel, the chat widget, the payment providers, the CDN, the host, the web server and the bot-management vendor sitting in front of it — each detection carrying the evidence that proved it, so you can check any answer yourself.

No API key. No browser. No JavaScript execution. **$1.00 per 1,000 sites**, proxy included, dead domains are free, and a run that reads nothing costs nothing at all.

### Try it in 30 seconds

```json
{
  "websites": ["allbirds.com", "vercel.com", "techcrunch.com", "webflow.com", "hubspot.com"]
}
```

A bare domain is enough. Each one is tried on `https://`, then `https://www.`, then plain `http://`, so a host that only answers on one of them still gets read.

### What comes back

```jsonc
{
  "ok": true,
  "rowType": "site",                    // "site" | "technology" | "diagnostic"

  "input": "allbirds.com",
  "domain": "allbirds.com",
  "url": "https://www.allbirds.com/",   // where the request actually landed
  "statusCode": 200,
  "title": "Allbirds: Comfortable, Sustainable Shoes & Apparel",
  "description": "The world's most comfortable shoes, made with natural materials…",
  "language": "en-us",
  "generator": null,                    // the site's own <meta name="generator">
  "server": "cloudflare",
  "partial": false,                     // true only when a bot wall answered instead

  "technologies": [
    {
      "name": "Shopify",
      "category": "E-commerce",
      "website": "https://www.shopify.com",
      "version": "[DNAM Theme July 2026]",   // Shopify publishes the theme, not a version
      "confidence": 100,
      "evidence": [
        "headers: header powered-by: Shopify",
        "cookies: cookie _shopify_y=2a063abf-e67c-4dd1-9c31-79d91f9eb39c",
        "meta: meta shopify-checkout-api-token: 1bd27af8770b91f6f7cab7cc85cba04d"
      ]
    },
    { "name": "Cloudflare",         "category": "CDN",         "confidence": 100, "evidence": ["headers: header cf-ray: a38f37e6bc395711-PHX"] },
    { "name": "Shop Pay",           "category": "Payments",    "confidence": 95,  "evidence": ["assets: asset: https://shop.app/checkouts/internal/preloads.js…"] },
    { "name": "Google Tag Manager", "category": "Tag manager", "confidence": 95,  "evidence": ["assets: asset: https://www.googletagmanager.com/ns.html?id=GTM-TH8KRSBJ"] }
  ],
  "technologyNames": ["Cloudflare", "Shopify", "Shop Pay", "Google Tag Manager", "Alpine.js", "…"],
  "categories": {
    "E-commerce": ["Shopify"],
    "CDN": ["Cloudflare"],
    "Payments": ["Shop Pay"]
  },

  // the flat columns, for filtering and for spreadsheets
  "cms": null,
  "ecommercePlatform": "Shopify",
  "jsFramework": "Alpine.js",
  "analytics": ["Amplitude", "Google Tag Manager"],
  "advertising": [],
  "chat": [],
  "payments": ["Shop Pay"],
  "cdn": ["Cloudflare"],
  "hosting": null,
  "security": [],
  "hasCookieBanner": true,
  "scriptCount": 38,
  "externalDomains": ["cdn.shopify.com", "www.googletagmanager.com", "shop.app"],
  "pagesFetched": ["https://www.allbirds.com/"],

  "error": null,
  "errorType": null,
  "fetchedAt": "2026-09-10T14:45:46.000Z",
  "scrapedAt": "2026-09-10T14:45:46.120Z",
  "source": "website",
  "sourceUrl": "https://www.allbirds.com/"
}
```

With `"rowPerTechnology": true`, every detection *also* gets its own flat row carrying `name`, `category`, `version`, `confidence` and `evidence` — the shape you want for a pivot table or a warehouse load.

### Use cases

- **Lead qualification and prospecting** — filter a list of domains down to the ones running Shopify, HubSpot, Klaviyo or whatever your product plugs into. `ecommercePlatform`, `cms` and `technologyNames` are the columns to sort on.
- **Competitive and market research** — count how many sites in a segment run WordPress versus Webflow, or which analytics stack a whole industry has settled on.
- **Partner and integration targeting** — find the sites already running the platform you build extensions for, and the ones running the competitor you displace.
- **Agency audits and pitches** — walk into a meeting knowing the prospect's CMS, page weight, ad pixels and consent platform before anyone opens a laptop.
- **Security and vendor mapping** — which of your suppliers sit behind Cloudflare, DataDome or Akamai, and which have no bot management at all.
- **Compliance sweeps** — `hasCookieBanner` plus the `Cookie consent` category against a portfolio of domains, in one run.
- **Monitoring a migration** — re-run a list weekly and diff `technologyNames` to catch the day a customer moves off your platform.

### How it works, and why it keeps working

A website tells you what it runs on in five places, and this Actor reads all five out of a single HTTP response:

| Where | What it gives away | Confidence |
|---|---|---|
| **Response headers** | `Server: Squarespace`, `x-powered-by: Next.js`, `x-hs-hub-id`, `cf-ray`, `x-wix-request-id`, `x-vercel-id` | 100 |
| **Cookies the server set** | `_shopify_y`, `__cf_bm`, `datadome`, `PHPSESSID`, `laravel_session` | 100 |
| **Script and asset URLs** | `cdn.shopify.com`, `/_next/static/`, `js.stripe.com`, `widget.intercom.io`, and every preconnect and preload alongside them | 95–100 |
| **Meta tags** | `<meta name="generator" content="WordPress 6.9.7">`, `shopify-checkout-api-token`, `og:*` | 90 |
| **The markup and inline JS** | `data-wf-page`, `_nghost-`, `svelte-1fusfkr`, `wp-content/themes/…`, `window.intercomSettings` | 60–80 |

Three decisions are worth knowing about, because they are what separates a useful answer from a noisy one:

**Evidence is scored, and it is shown to you.** Confidence is not a guess about how popular a product is. It is how good the evidence was: a header the server itself set outranks a string somewhere in the body, and both outrank a global name mentioned in inline JavaScript. Every detection carries up to four `evidence` lines naming exactly what matched, truncated to 120 characters — so a detection you doubt takes ten seconds to check rather than a support ticket.

**First-party assets only, where it matters.** A news site that embeds one image from a WordPress blog's CDN is not running WordPress. Platform signatures are restricted to assets on the site's own registrable domain, which is why nytimes.com does not come back as WordPress even though `wp-content/` appears in its markup. Signatures where that restriction would be wrong — a Stripe script is a Stripe script wherever it is hosted — do not use it.

**A blocked page is still an answer.** When a site serves an anti-bot interstitial, that interstitial names the vendor the site pays for. Rather than throwing the response away, the row comes back with `partial: true`, the security vendor and the CDN filled in, and an `error` saying what refused. That is worth more than an empty diagnostic, and it is charged as one site. A challenge page that revealed *nothing* is a free diagnostic row.

The fingerprint database — **232 signatures across 26 categories** — was written from scratch for this Actor and is in `src/fingerprints.js`, one readable object per product. It is not derived from any third-party fingerprint set.

### How it compares

- **The evidence ships with the answer.** Most technology lookups hand you a label. This hands you the label and the header, cookie or URL it came from, so you can audit a list of 10,000 domains by spot-checking twenty.
- **Bulk by design, priced by the domain.** No monthly minimum, no per-seat licence, no rate card that changes at 10,000 lookups. One JSON array in, one flat table out.
- **Live, not a database lookup.** Every run reads the site at that moment. Nothing here is a cached crawl from six months ago, which is the failure mode of the databases these tools usually sit on.
- **Failures are free and legible.** A dead domain, a URL that answers with a PDF, or a site that ran out the clock produces a diagnostic row with an `errorType` you can branch on — and no charge. A run that reads nothing at all finishes **FAILED** with the reason in its status message, never a green run containing an apology.
- **No browser.** Detection is pure HTTP and regular expressions, which is why a thousand domains cost dollars rather than hundreds of dollars — and why a site whose stack only appears after client-side rendering is a documented limitation rather than a hidden one.

### Input reference

| Field | Type | Default | What it does |
|---|---|---|---|
| `websites` **(required)** | array of strings | five example domains | Bare domains or full URLs. Each is tried `https://` → `https://www.` → `http://`. A URL with a real path is fetched exactly as given. Duplicates are read, and billed, once |
| `extraPages` | integer 0–3 | `0` | Internal pages to read *in addition to* the entry page, shallowest links first. Costs requests and time; never costs an extra `site` charge |
| `rowPerTechnology` | boolean | `false` | Also write one flat row per detection, at $0.0001 each. The `site` rows are still written, first |
| `categories` | array | `[]` (all) | Restrict the output to chosen categories. The flat columns follow the filter |
| `minConfidence` | integer 0–100 | `50` | Drop detections scored below this. 100 = a header or cookie; 90 = a generator meta; 60 = an inline-JS global |
| `maxConcurrency` | integer 1–20 | `5` | Sites in parallel. Each worker keeps its own proxy session |
| `maxRunSecs` | integer 30–3600 | `240` | Whole-run wall-clock budget. When it runs out the Actor keeps what it has and files a free diagnostic row for each site it never reached |
| `proxyConfiguration` | object | Apify datacenter | Switch the group to `RESIDENTIAL` for sites behind an aggressive bot wall. Datacenter is the default because it is cheaper and ordinary websites accept it |

### Output reference

Every row carries the same keys. `ok: true` is a `site` or `technology` row; `ok: false` is a free diagnostic row.

| Field | What it is |
|---|---|
| `rowType` | `site`, `technology` or `diagnostic` |
| `input`, `domain`, `url`, `statusCode` | The entry you supplied, its registrable domain, the URL the request landed on, and the status |
| `title`, `description`, `language`, `generator`, `server` | Page metadata, read straight off the entry page |
| `partial` | True when a bot wall answered instead of the site. The row then holds the wall, not the stack |
| `technologies` | Every detection: `name`, `category`, `website`, `version`, `confidence`, `evidence[]` |
| `technologyNames`, `categories` | The same list flattened, and grouped by category |
| `cms`, `ecommercePlatform`, `jsFramework`, `hosting` | The single best answer in each of those four categories |
| `analytics`, `advertising`, `chat`, `payments`, `cdn`, `security` | Every detection in each of those categories |
| `hasCookieBanner` | A named consent platform, or an unmistakable consent-banner marker |
| `scriptCount`, `externalDomains`, `pagesFetched` | How heavy the front end is, the top 25 third-party hosts, and which pages went into this row |
| `name`, `category`, `version`, `confidence`, `evidence` | Filled in on a `technology` row; null on a `site` row |
| `ok`, `error`, `errorType` | Whether this row is a result, and if not, why not |
| `fetchedAt`, `scrapedAt`, `source`, `sourceUrl` | When the page was read, when the row was written, and from where |

`errorType` on a diagnostic row is one of:

| Value | Meaning | Charged? |
|---|---|---|
| `unreachable` | Nothing answered on any of the four addresses. A dead domain, a parked domain, or a host with no web server | No |
| `blocked` | The site refused the request, or served an interstitial that revealed nothing identifiable | No |
| `not-html` | The URL answered with a file rather than a page — a PDF, an image, a JSON feed | No |
| `http-error` | An error status with no readable page behind it | No |
| `timeout` | The run's `maxRunSecs` budget ran out before this site was reached | No |
| `invalid-input` | The entry was not a fetchable domain — an email address, an IP, a bare word | No |

### Pricing

**$1.00 per 1,000 sites.** Pay-per-event, with the proxy already inside that number — there is no separate proxy line on your bill for this Actor.

| Event | What triggers it | FREE | Starter | Scale | Business |
|---|---|---|---|---|---|
| **Site analysed** *(primary)* | One website written to your dataset | $0.001 | $0.001 | $0.0008 | $0.0006 |
| Technology row | One extra flat row, only when `rowPerTechnology` is on | $0.0001 | $0.0001 | $0.0001 | $0.0001 |
| Run started | Once per run, after the first site is analysed | $0.001 | $0.001 | $0.001 | $0.001 |

One site is one charge whether it returned three detections or thirty, and whether `extraPages` made the Actor read one page or four.

**Worked example.** 500 domains from a scraped lead list, of which 40 turn out to be dead:

- 460 sites × $0.001 = **$0.46**
- 1 run start = **$0.001**
- 40 dead domains = **$0.00**
- **Total: $0.461**

What you are never charged for: a domain that resolves nowhere, a URL that answers with a file, an entry that was not a website, a site the run never reached before `maxRunSecs`, or a challenge page that revealed nothing. If a whole run comes back empty it finishes FAILED and bills **nothing at all**, start fee included.

Set `ACTOR_MAX_TOTAL_CHARGE_USD` on a run and the Actor stops fetching once the ceiling is in sight, rather than handing you rows it cannot bill or billing you for rows it cannot hand over. It finishes SUCCEEDED with the ceiling named in its status message, and everything already delivered is yours.

### Limits, and the ones that might bite

**No browser, so nothing that only exists after rendering is seen.** This Actor reads the HTML a server returns. A single-page app that injects its analytics tag from JavaScript at runtime, a chat widget loaded by a tag manager three hops deep, a payment button that only mounts on the checkout page — none of those are in the initial document, and none of them will be detected from a home page. `extraPages` helps with the second and third; nothing helps with the first, and a tool that claims otherwise is running a browser and charging accordingly.

**Absence is not evidence of absence.** A row that does not name Klaviyo means Klaviyo was not visible on the pages that were read. It does not mean the site is not a Klaviyo customer. Treat the detections as a floor, never as a complete inventory — this matters most when you are counting *how many* sites in a list lack something.

**Detection is only as good as the fingerprint.** 232 signatures is a lot of the web, not all of it. Products with no public marker — a self-hosted backend, a first-party analytics pipeline, a white-labelled widget served from the site's own domain — leave nothing to match on. Regional platforms outside the English-speaking web are thinner than the ones listed here.

**Tailwind CSS is a heuristic, and it is labelled as one.** Compiled Tailwind leaves no file to point at, so it is detected from the utility-class dialect itself and only when six of eight distinct class families are present. It scores 60, and its evidence line says exactly that. Raise `minConfidence` above 60 to exclude it.

**Versions are reported only where a site publishes one.** `generator` metas, versioned asset URLs and CDN paths give real numbers; most modern stacks give none. A null `version` means the page did not say, not that the version is unknown to anyone.

**A `www.` and an apex host are two entries.** Sending both reads and bills both. That is deliberate — a caller sending both is usually testing exactly that difference — but it is worth knowing before you paste a list with duplicates in two spellings.

**Sites behind aggressive bot management may refuse the default proxy.** The datacenter default is right for the overwhelming majority of the web and wrong for the small slice that fingerprints every request. Those sites come back as `partial: true` rows naming their security vendor; switch `proxyConfiguration` to `RESIDENTIAL` and re-run them if you need what is behind the wall.

**The upstream format may change.** Websites are rebuilt, vendors rename their CDN hosts, and a signature that worked last month can go quiet. When that happens a technology stops appearing — it does not turn into a wrong answer — and the `evidence` column is how you find out which one.

### Use it from an AI agent, or from code

One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with **limited permissions**, uses **pay-per-event** pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The **Integrations** tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.

```bash
curl -X POST "https://api.apify.com/v2/acts/insight.solutions~website-tech-detector/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"websites":["allbirds.com","gymshark.com"],"minConfidence":90}'
```

```python
## pip install apify-client
from apify_client import ApifyClient

client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/website-tech-detector").call(run_input={
    "websites": ["allbirds.com", "vercel.com", "techcrunch.com"],
    "categories": ["E-commerce", "Marketing automation", "Analytics"],
    "minConfidence": 90,
})

for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    if not row.get("ok"):
        print("skipped:", row["input"], row["errorType"])
        continue
    print(row["domain"], "→", ", ".join(row["technologyNames"]) or "nothing detected")
```

Switch `rowPerTechnology` on when the destination is a table rather than a document — one row per detection loads into a warehouse without a JSON parser.

### FAQ

**How accurate is it?**
Accurate enough to act on, and honest about where it is not. Every detection carries the evidence that produced it and a confidence score derived from *what kind* of evidence that was. Every detection on all twenty real homepages in the test suite was read by hand during development and traced back to something the page genuinely publishes, and the suite then pins the traps down site by site: nytimes.com is not WordPress, github.com is not GitHub Pages, bigcommerce.com's marketing site is Next.js on Vercel and not a BigCommerce storefront, allbirds.com is not running hCaptcha because a script mentions the field name.

**Why does a site I know runs X not show X?**
Almost always because X is not in the initial HTML: it is injected by a tag manager, mounted by client-side JavaScript, or only present on a page this run did not read. Try `extraPages: 2`. If it still does not appear, the product left no public marker on that page, and no HTTP-only tool will find it.

**What does `confidence` actually mean?**
How strong the evidence was, not how likely the product is. 100 = a response header or a cookie the server set. 95–100 = a script or asset URL. 90 = a `<meta name="generator">`. 80 = a string in the page body. 75 = an attribute or class-name shape. 70 = added because another detection implies it. 60 = a global named in inline JavaScript.

**What is `implied by` in the evidence?**
Some technologies cannot run without others. Next.js implies React and Node.js; WooCommerce implies WordPress, which implies PHP and MySQL. Those are added at confidence 70 with an evidence line naming what implied them, so "we saw it" and "it cannot work without it" are never confused.

**Does it respect robots.txt?**
This Actor reads a site's home page the way a browser does — one request, no crawl. It does not walk a site, does not follow paginated listings, and by default does not go past the entry page at all. With `extraPages` on it reads at most three more linked pages, spaced 250–600 ms apart.

**Can I check 50,000 domains?**
Yes. Raise `maxConcurrency` and `maxRunSecs`, and bound the spend with `ACTOR_MAX_TOTAL_CHARGE_USD`. At $0.001 per site that is $50 for the list, and the run stops cleanly at whatever ceiling you set.

**What happens if one domain fails?**
The others still run. The failed one produces a free diagnostic row and the run finishes `SUCCEEDED`. If *every* domain fails, the run finishes `FAILED` and you are billed nothing at all.

**Do I need my own proxy?**
No. Apify's datacenter proxy is configured by default and its cost is inside the per-site price. Switch the group to `RESIDENTIAL` in the same field if a set of targets refuses datacenter addresses.

**Is the data fresh?**
Live. Every run reads the site at that moment; nothing is cached, and there is no lookup database behind this.

### Legal and data-protection notes

- **Public pages only.** Every source is a page a browser would load without signing in. The Actor never logs in, never accepts cookies or session tokens, never takes an API key belonging to anyone else, and never touches anything behind authentication.
- **This is not personal data.** A technology profile describes a website's infrastructure, not a person. Nothing in the output identifies an individual, and no contact details, names or user records are collected.
- **The fingerprint database is our own work.** Every signature in `src/fingerprints.js` was written from general knowledge of how these products identify themselves on the wire and verified against real captured homepages. Nothing in it is copied from, generated from, or derived from Wappalyzer, webappanalyzer or any other third-party fingerprint set.
- **Detections are observations, not endorsements or contracts.** "Site X loads Stripe's script" is what was seen; it is not a claim about a commercial relationship, and it should not be presented as one.
- **Not affiliated with any of the vendors named in the output.** All product names and trademarks belong to their respective owners and are used only to describe which public artefacts this Actor recognises.

### Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

**Video, audio & social**

- [YouTube Transcript API](https://apify.com/insight.solutions/youtube-transcript-api) — captions as timed segments, text, SRT or VTT, with language fallback and translation.
- [YouTube Comments API](https://apify.com/insight.solutions/youtube-comments-api) — comments and replies with likes, pinned and hearted flags, newest or top sort.
- [YouTube Channel API](https://apify.com/insight.solutions/youtube-channel-api) — a channel's videos, Shorts and live streams, plus YouTube search.
- [Podcast Search, Episodes & Charts API](https://apify.com/insight.solutions/podcast-api) — Apple Podcasts search, charts and full episode feeds.
- [Bluesky Scraper](https://apify.com/insight.solutions/bluesky-scraper) — profiles, posts, followers and follows from the public AT Protocol API.
- [Telegram Channel Scraper](https://apify.com/insight.solutions/telegram-channel-scraper) — posts, views and channel stats from public Telegram channels.
- [Substack Scraper](https://apify.com/insight.solutions/substack-scraper) — posts with full free text, comments and publication profiles.

**News, documents & the web**

- [Google News Search, Topics & Real Article URLs](https://apify.com/insight.solutions/google-news-api) — news search and topic feeds with the publisher's real URL decoded.
- [Website to Markdown — Content Extractor for LLMs & RAG](https://apify.com/insight.solutions/website-content-extractor) — any site as clean Markdown, text and heading-aware chunks.
- [Internet Archive API](https://apify.com/insight.solutions/internet-archive-api) — archive.org search, item metadata, files and reviews.
- [Wayback Machine Toolkit](https://apify.com/insight.solutions/wayback-toolkit) — archived URL inventories, snapshots and text diffs between dates.
- [Domain Intelligence API](https://apify.com/insight.solutions/domain-intelligence-api) — DNS, RDAP registration, TLS certificate and HTTP facts in one row per domain.
- [SEO Page Audit](https://apify.com/insight.solutions/seo-page-audit) — sitemap crawl with on-page checks, structured data and broken-link reports.
- [Keyword Suggestions API](https://apify.com/insight.solutions/keyword-suggestions-api) — Google, YouTube, Bing, Amazon and eBay autocomplete with alphabet and question expansions.
- [Website Contact Extractor](https://apify.com/insight.solutions/website-contact-extractor) — emails, phone numbers and social profiles from any list of websites.

**Business, finance & jobs**

- [Congress & Insider Trades API](https://apify.com/insight.solutions/congress-insider-trades-api) — STOCK Act periodic transaction reports and SEC Form 4 insider trades in one schema.
- [SEC EDGAR API](https://apify.com/insight.solutions/sec-edgar-api) — filings, XBRL financials and full-text search by ticker or CIK.
- [Y Combinator Companies, Batches & Founders](https://apify.com/insight.solutions/yc-companies-directory) — the YC directory with founders and social links, filterable by batch, industry and hiring status.
- [Career Site Jobs API](https://apify.com/insight.solutions/ats-jobs-api) — jobs straight from Greenhouse, Lever, Ashby, Workable and 10+ other ATS career sites.
- [New Job Postings Monitor](https://apify.com/insight.solutions/job-postings-monitor) — new, closed and changed postings on the career sites you watch.
- [Shopify Products API](https://apify.com/insight.solutions/shopify-products-api) — any Shopify store's catalogue, variants, prices and stock signals.

**Apps & games**

- [App Store & Google Play Reviews API](https://apify.com/insight.solutions/app-reviews-api) — reviews from both stores with ratings, versions and developer replies.
- [App Store Top Charts & App Search API](https://apify.com/insight.solutions/app-charts-api) — Apple top charts by country and genre, plus app search and details.
- [Steam Reviews API](https://apify.com/insight.solutions/steam-reviews-api) — Steam reviews with playtime, helpfulness and game details.
- [Steam Game Data API](https://apify.com/insight.solutions/steam-store-stats-api) — prices, tags, review scores, live player counts and top charts.

# Actor input Schema

## `websites` (type: `array`):

One entry per site. A bare domain is enough — `allbirds.com` — and so is a full URL. Each domain is tried on `https://`, then `https://www.`, then plain `http://`, so a host that only answers on one of them still gets read. A URL with a real path is fetched exactly as given, which is how you point the Actor at a product page instead of a home page. Email addresses and IP addresses are rejected for free. Duplicates are read, and billed, once.

## `extraPages` (type: `integer`):

How many internal pages to read *in addition to* the entry page, taking the shallowest internal links first. Zero — the default — reads the home page only, which is where the overwhelming majority of a site's stack declares itself. Raise it to 1 or 2 when you are looking for something that only loads deeper: a review widget on a product page, a checkout provider, a booking form. Each extra page costs a request and a little time; it never costs an extra `site` charge.

## `rowPerTechnology` (type: `boolean`):

Off by default: each site is one row, with the full detection list inside `technologies`. Turn it on and every detection also gets its own flat row — `name`, `category`, `version`, `confidence`, `evidence` — which is the shape you want for a pivot table, a warehouse load, or "show me every site in this list running Klaviyo". The `site` rows are still written, first. Per-technology rows are billed at $0.0001 each.

## `categories` (type: `array`):

Leave empty for everything. Pick one or more categories and the rows carry only those — useful when you are qualifying leads on one axis ("which of these 4,000 shops are on Shopify") and do not want the other 200 signatures in your export. The flat columns follow the filter: filter to CMS only and `security`, `payments` and the rest come back empty.

## `minConfidence` (type: `integer`):

Drop detections scored below this. Confidence is not a guess about how popular a product is — it is how good the evidence was. A response header or a cookie the server set scores 100; a script URL 95–100; a `<meta name="generator">` 90; an attribute or class-name shape 75; a string in the page body 80; a global named in inline JavaScript 60. Something added only because another detection implies it scores 70. The default of 50 keeps everything; raise it to 90 for a list you are going to act on without reading.

## `maxConcurrency` (type: `integer`):

How many sites are read at once. Each parallel worker keeps its own proxy session, so a site that refuses one exit IP does not burn the address the others are using. Five is a good citizen against a list of unrelated domains; raise it for a large list, lower it if you are sending many hostnames that share one origin.

## `maxRunSecs` (type: `integer`):

Wall-clock budget for the whole run. When it is reached the Actor stops starting new sites, keeps and bills for every row it already wrote, and files a free diagnostic row for each site it never reached. Nothing is charged for work that did not happen.

## `proxyConfiguration` (type: `object`):

Ordinary websites do not refuse datacenter address ranges the way the big platforms do, so the default is Apify's datacenter proxy and its cost is already inside the per-site price. If you are fingerprinting sites behind an aggressive bot wall, switch the group to RESIDENTIAL here and nothing else changes — sessions still rotate automatically when an exit IP is refused. Clearing the field sends requests from the run's own address.

## Actor input object example

```json
{
  "websites": [
    "shopify.com",
    "https://www.nytimes.com/"
  ],
  "extraPages": 0,
  "rowPerTechnology": false,
  "categories": [],
  "minConfidence": 50,
  "maxConcurrency": 5,
  "maxRunSecs": 240,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One row per website: its CMS, e-commerce platform, JavaScript framework, analytics, ad pixels, chat, payments, CDN, hosting and security, each detection carrying the evidence that proved it. Websites that could not be read get a free diagnostic row saying why. Delivered as JSON items in the default dataset.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "websites": [
        "allbirds.com",
        "vercel.com",
        "techcrunch.com",
        "webflow.com",
        "hubspot.com"
    ],
    "extraPages": 0,
    "rowPerTechnology": false,
    "categories": [],
    "minConfidence": 50,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("insight.solutions/website-tech-detector").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "websites": [
        "allbirds.com",
        "vercel.com",
        "techcrunch.com",
        "webflow.com",
        "hubspot.com",
    ],
    "extraPages": 0,
    "rowPerTechnology": False,
    "categories": [],
    "minConfidence": 50,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("insight.solutions/website-tech-detector").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "websites": [
    "allbirds.com",
    "vercel.com",
    "techcrunch.com",
    "webflow.com",
    "hubspot.com"
  ],
  "extraPages": 0,
  "rowPerTechnology": false,
  "categories": [],
  "minConfidence": 50,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call insight.solutions/website-tech-detector --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,insight.solutions/website-tech-detector"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/5yJc5UBL7bYC6U0fo/builds/ZBDsZfIaVT4Vka68b/openapi.json
