# Tech Stack Lookup: BuiltWith & Wappalyzer Alternative (`vant47/tech-stack-lookup`) Actor

Check the technology stack behind online shops, SaaS sites and company websites. Find CMS, ecommerce, analytics, ads, payments, chat and frameworks, with versions and public contact links. About $0.0045 per website: 100 cost $0.45. Optional detailed checks: $0.02 each. No login.

- **URL**: https://apify.com/vant47/tech-stack-lookup.md
- **Developed by:** [fortbrand](https://apify.com/vant47) (community)
- **Categories:** Developer tools, Lead generation
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.60 / 1,000 websites

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Tech Stack Lookup: BuiltWith & Wappalyzer Alternative

For agencies, SaaS teams and researchers checking the technology behind a list of websites. Inspect current public pages for CMS, ecommerce, analytics, advertising, payments, chat, frameworks and infrastructure.

**About $0.0045 per website: checking 100 websites costs $0.45**, plus the small Apify start charge. Only websites with a supported detection are charged. Duplicate websites, blocked pages and checks with no detections have no Website charge. Optional detailed checks total $0.02 when at least two pages succeed. No website login or separate data-service key is needed.

### What you get

Each useful website produces one row. The first field explains how many technologies were directly observed and how many dependencies were inferred. Detailed objects preserve categories, an exposed version when available, and the specific response signal behind the result. Public contact, support and about page links are included when found.

| Field | What it means |
| --- | --- |
| `verdict` | Readable result, with direct and inferred detection counts |
| `domain` | Requested hostname, normalized without a leading `www` |
| `url`, `finalUrl` | Input page and the final public page after redirects |
| `title`, `description`, `language` | Metadata from the fetched page, or null when absent |
| `status`, `statusCode` | Coverage status and successful first-page HTTP code |
| `technologyNames` | Flat list for spreadsheets and CRM tags |
| `technologies` | Named technologies, categories, version, confidence and evidence |
| `technologyGroups` | CMS, ecommerce, analytics, ads, payments, chat, frameworks and infrastructure groups |
| `directDetections` | Count supported by an observed public response signal |
| `technologyCount` | Total returned technology count, including enabled inference |
| `contactLinks` | Same-site contact, support and about page links, with their source page |
| `pageCount`, `pages` | Pages successfully inspected, sizes, status codes and redirect paths |
| `failedPages` | Optional deeper pages that could not be inspected |
| `coverage` | Detection scope and the bundled fingerprint snapshot used |
| `scrapedAt` | UTC observation time for this website check |
| `recordType` | `WEBSITE` for a charged result, `NOTE` for an uncharged explanation |
| `requestStats` | Cumulative request counters at this observation, preserved with the row |

The `SUMMARY` key-value record reports requested websites, results, free notes, duplicates, unprocessed domains, request attempts and the spending-limit outcome. It includes no website charges for failures. Use it alongside the dataset when a workflow needs to distinguish a successful check from an unavailable source.

### Why evidence matters

A website mentioning “Shopify” in a blog article does not prove it runs on Shopify. This Actor evaluates structured fingerprints in response headers, generator metadata, script URLs, inline scripts and HTML markup. Ordinary product names in prose are insufficient. Where a site provides a specific software version, that version travels with the matched signal; missing versions remain null.

WordPress can imply PHP and MySQL without exposing the actual backend. Dependencies have `inferred: true`, null confidence, and evidence naming their parent technology. Set `includeInferred` to false to omit them from the detailed list and category groups.

Direct-match confidence is a fingerprint-rule score, not a calibrated probability. Scores do not verify deployment across every page.

### Three steps to use it

1. Enter website domains, such as `wordpress.org`, `ghost.org`, `nextjs.org`, or a shop you want to research. Full public URLs also work.
2. Choose the maximum number of websites. Keep the standard one-page check, or enable `includeDetails` for up to three pages at $0.02 total.
3. Start the Actor, review the Overview and Technology evidence views, then export JSON, CSV or Excel through Apify.

The prefilled example checks three websites. A live cloud feasibility run on 29 October 2026 delivered all three in 11.675 seconds. That is a measured example, not a speed guarantee for arbitrary websites. Sites that redirect several times, throttle requests or refuse access can take longer and receive a free note.

### Inputs

| Input | Default | Details |
| --- | --- | --- |
| `domains` | Three example websites when no URL field is supplied | Array of domains or full HTTP/HTTPS URLs |
| `startUrls` | Empty | Optional public page links, including Apify request-list objects |
| `maxItems` | 100 | Maximum unique websites to process, from 1 to 10,000 |
| `includeDetails` | false | Up to 3 public pages; $0.02 total when at least two succeed |
| `includeContactLinks` | true | Return public same-site contact, support and about page URLs |
| `includeInferred` | true | Include explicitly labelled implied dependencies |
| `proxyConfiguration` | Apify datacenter proxy | Disable proxy or supply a supported proxy configuration when appropriate |

The API also accepts `urls`. Lists are combined and deduplicated before `maxItems`. Empty lists check no websites. Invalid inputs produce free notes; corrected optional settings are explained in `SUMMARY`.

#### Quick technology check

```json
{
  "domains": ["wordpress.org", "ghost.org", "nextjs.org"],
  "maxItems": 3,
  "includeInferred": true
}
```

#### More page evidence

```json
{
  "domains": ["wordpress.org"],
  "maxItems": 1,
  "includeDetails": true,
  "includeContactLinks": true,
  "includeInferred": false
}
```

Query strings and fragments are removed. `www.example.com` and `example.com` share one result; `shop.example.com` remains separate. Put your preferred starting page first.

### Example from a real cloud run

The following excerpt is from the public Ghost homepage check on 29 October 2026. A software brand's marketing site can run on a different platform from the product it sells. Here the page exposed Hugo in its generator metadata.

```json
{
  "recordType": "WEBSITE",
  "domain": "ghost.org",
  "status": "complete",
  "technologyNames": ["Alpine.js", "FirstPromoter", "HSTS", "Hugo", "Netlify"],
  "pageCount": 1,
  "technology": {
    "name": "Hugo",
    "version": "0.119.0",
    "inferred": false,
    "evidence": [
      {
        "source": "meta",
        "metaName": "generator",
        "matched": "Hugo 0.119.0",
        "pageUrl": "https://ghost.org/"
      }
    ]
  }
}
```

This excerpt shows one entry from the full `technologies` array. A live signed-out browser inspection confirmed the generator and FirstPromoter script. Missing fields remain unknown.

### Pricing and spending limits

**Website** costs $0.0045 normally, $0.004275 on Bronze, $0.00405 on Silver and $0.00360 on Gold and higher tiers. Versions and contact-page links are included. Optional **Detailed website** adds $0.0155 only when `includeDetails` is enabled and at least two pages succeed, making the standard total $0.02 ($2 for 100). Detailed totals are $0.019 on Bronze, $0.018 on Silver and $0.016 on Gold. One-page results have no detailed fee. The Apify start charge is $0.00005 per GB, minimum one unit.

| Delivered websites | Standard Website charges | Bronze Website charges |
| ---: | ---: | ---: |
| 3 | $0.0135 | $0.012825 |
| 100 | $0.45 | $0.4275 |
| 1,000 | $4.50 | $4.275 |

The start charge is additional. Platform usage is included in the Actor's pay-per-event arrangement. Check the current Apify Pricing tab for your tier before a large job. No separate credits are purchased from a technology lookup vendor.

Set `maxItems` to control volume and Apify's maximum charge to control spend. The Actor reserves room for all requested fees before checking a website. A result is saved before its charge is submitted. If the limit leaves no room for the requested check, processing stops cleanly and `SUMMARY.unprocessedSites` shows what remains. Notes and duplicate inputs do not consume Website charges.

### Monitor website stacks regularly

Save a domain list as an Apify Task, review the first dataset, then attach a schedule in Apify Console. Each new run fetches current public responses. Choose a frequency suited to your project.

Join snapshots by `domain`, preserving observation time, final URL and evidence. Keep starting pages and depth consistent. A missing signal can reflect consent or incomplete responses; failed checks do not establish removals. The Actor does not maintain a cross-run change baseline.

### Use cases

Agencies can qualify supplied prospects by CMS. SaaS teams can check integration scripts, and researchers can compare adoption within a defined sample.

Preserve before/after evidence for migrations. This Actor does not reverse-find technology users, estimate company revenue, produce personal contact lists or establish buying intent.

### API examples

Start an asynchronous run with an Apify token in your HTTP authorization header. Keep that credential in your automation's secret settings.

```bash
curl -X POST \
  'https://api.apify.com/v2/acts/vant47~tech-stack-lookup/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  --data '{"domains":["wordpress.org","ghost.org"],"maxItems":2}'
```

Poll the returned run ID, then read its default dataset. Re-reading saved results does not start another check.

```python
import os
import requests

headers = {"Authorization": "Bearer " + os.environ["APIFY_TOKEN"]}
run = requests.post(
    "https://api.apify.com/v2/acts/vant47~tech-stack-lookup/runs?waitForFinish=60",
    headers=headers,
    json={"domains": ["ghost.org"], "maxItems": 1},
    timeout=90,
).json()["data"]
if run["status"] == "SUCCEEDED":
    rows = requests.get(
        "https://api.apify.com/v2/datasets/" + run["defaultDatasetId"] + "/items",
        headers=headers,
        timeout=30,
    ).json()
    websites = [row for row in rows if row.get("recordType") == "WEBSITE"]
```

Make, Zapier and n8n can map technology and contact-link fields into downstream records. Poll long runs asynchronously and review free notes.

### Use with AI agents (MCP)

Connect your MCP-compatible agent to Apify's MCP server at `https://mcp.apify.com` and expose `vant47/tech-stack-lookup`. Ask it to check a supplied domain list, keep the run small, and return the technology evidence. For example: “Check wordpress.org and ghost.org. Show the observed CMS, exposed version and public contact-page links. Omit inferred dependencies.”

Agents should supply `domains`, inspect `SUMMARY`, retain `recordType=WEBSITE`, and explain free notes. Neither commercial technology lookup product is queried.

### Coverage and reliability

The detector bundles more than 5,800 technology definitions from the MIT-licensed ProjectDiscovery fingerprint collection. It evaluates supported passive signals with native RE2 regular expressions. Eight incompatible upstream patterns are excluded and disclosed in the coverage metadata. Runtime JavaScript properties, DOM-only rules and additional downloaded script contents are not evaluated.

Two websites can overlap; requests per hostname remain serial and 1.5 seconds apart. Retries use fresh sessions. Retry-After triggers a wait or a free note when a longer delay is required. Challenges are not accepted as results.

Saved results and acknowledged billing counts survive a restart. Delivery is serial, website identities are stable within a run, and an ambiguous saved-row/charge state stops further charging. This prevents duplicate output or a second Website charge when a run resumes after a platform reboot.

Contact links are page URLs. Extra pages stay on the starting origin. Local addresses, credentials and custom ports are rejected. No login or CAPTCHA solving is attempted.

### FAQ

#### Is this affiliated with BuiltWith or Wappalyzer?

No. This independent detector uses a bundled open fingerprint snapshot with license attribution.

#### Why is a technology missing?

It may load after browser execution, consent, interaction or login, or exist only on an unvisited page. It may also lack a supported public fingerprint. Missing evidence is not proof of absence. Try a more relevant public starting page or additional page depth.

#### Are versions and backend technologies guaranteed?

No. Versions are extracted only when a matching response exposes them. Dependencies are explicitly inferred, not server measurements. A reverse proxy can reveal its own server signature while hiding the application behind it.

#### Do blocked sites cost a Website charge?

No. They receive a free NOTE with the reason, and appear in the summary. The platform start charge still applies. A readable page with no supported detections is also free.

#### Can I use it without proxy?

Yes. Set `proxyConfiguration` to `{"useApifyProxy":false}`. Datacenter proxy is the default. Residential proxy is not silently selected; use an appropriate configuration only when your target list needs it.

#### Can it find companies by technology or provide traffic estimates?

No. Supply the websites you want checked. This Actor does not provide a reverse lead index, company financial estimates or website traffic statistics.

#### Where can I inspect evidence or get help?

Use the Technology evidence dataset view and the SUMMARY record. Report problems in Apify Issues with a public domain, run ID and expected signal.

### Fingerprint attribution

Passive fingerprint and category data are adapted from ProjectDiscovery's wappalyzergo collection under the MIT License. The bundled notice preserves the license and source snapshot information. Fetching, detection orchestration, evidence output and billing code are independently implemented. Website access conditions vary by target; the fingerprint license does not grant permission to crawl every website.

# Actor input Schema

## `domains` (type: `array`):

Websites to check, for example wordpress.org, ghost.org, nextjs.org or a public online shop. Full URLs also work. www variants are deduplicated; subdomains remain separate. No account needed.

## `startUrls` (type: `array`):

Optional public HTTP or HTTPS links. Their page is inspected as supplied; queries and fragments are removed. Each website is charged once per run. Local addresses, credentials and custom ports are excluded.

## `includeContactLinks` (type: `boolean`):

Include same-site contact, support and about page URLs found in page links. Email addresses, phone numbers and personal profiles are not collected.

## `includeInferred` (type: `boolean`):

Include dependencies implied by observed software, for example PHP behind WordPress. Each is marked inferred and names the observed technology; this does not verify the underlying server.

## `maxItems` (type: `integer`):

Stop after this many unique normalized websites. Default 100; maximum 10,000. Your run spending limit can stop earlier. Failed or empty checks receive a free note, with no Website charge.

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

Datacenter proxy is the default. You can disable proxy for websites you can read directly or choose your own supported proxy configuration. Requests stay serial, with fresh sessions on retries. No CAPTCHA solving.

## `includeDetails` (type: `boolean`):

Optional. Inspect the starting page and up to two same-origin contact or about pages. A detailed check costs $0.02 total at standard pricing, including the base Website charge. The extra $0.0155 is charged only when at least two pages return successfully. If only one page is available, only the base $0.0045 applies. About $2 for 100 detailed websites.

## Actor input object example

```json
{
  "domains": [
    "wordpress.org",
    "ghost.org",
    "nextjs.org"
  ],
  "startUrls": [],
  "includeContactLinks": true,
  "includeInferred": true,
  "maxItems": 100,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "includeDetails": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

No description

## `summary` (type: `string`):

No description

# 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 = {
    "domains": [
        "wordpress.org",
        "ghost.org",
        "nextjs.org"
    ],
    "startUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("vant47/tech-stack-lookup").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 = {
    "domains": [
        "wordpress.org",
        "ghost.org",
        "nextjs.org",
    ],
    "startUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("vant47/tech-stack-lookup").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 '{
  "domains": [
    "wordpress.org",
    "ghost.org",
    "nextjs.org"
  ],
  "startUrls": []
}' |
apify call vant47/tech-stack-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vant47/tech-stack-lookup"
        }
    }
}
```

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/bcp18PusaF6FVXZp9/builds/kPd18ayRGf7qZvEOj/openapi.json
