# Tech Stack Lookup by Domain: Website Technology, CMS, CRM (`datagleaner/tech-stack-by-domain`) Actor

Website tech stack detector by domain: CMS, Shopify or other shop platform, payments, CRM and marketing tools, chat, analytics, ad pixels, hosting and email provider, each with evidence. Plain domains work. $2.50 per 1,000 domains; unreachable is free.

- **URL**: https://apify.com/datagleaner/tech-stack-by-domain.md
- **Developed by:** [Data Gleaner](https://apify.com/datagleaner) (community)
- **Categories:** Lead generation, Integrations, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.50 / 1,000 domain tech stacks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## Website Tech Stack Detector: CMS, shop platform, CRM and email provider by domain

**Paste domains, get back what each website runs on: CMS or site builder, shop platform (Shopify, WooCommerce), payments, CRM and marketing tools (HubSpot, Klaviyo), chat, analytics, ad pixels, hosting and the email provider behind the domain.** Plain domains are fine (`allbirds.com`, no `https://` needed). No API key, no browser.

**Price: $2.50 per 1,000 domains** ($0.0025 each), charged only for a domain where at least one technology is found. Unreachable, parked and invalid domains are free. See Pricing below.

### Why use this one

- **Every technology comes with its evidence** (`html: cdn.shopify.com`, `header cf-ray`, `MX aspmx.l.google.com`), so you can trust or discard a result.
- **Yes/no questions in one field.** Put `Shopify` or `HubSpot` in `checkFor` and each row says true or false.
- **Email provider from DNS**, so Google Workspace or Microsoft 365 is reported even when the website itself cannot be read.
- **Misses cost nothing.** A domain with nothing detected, or that cannot be reached, is returned with its status and not billed.
- **Honest limit:** it reads the home page, headers and DNS only, with no JavaScript rendering, so tools that appear only on inner pages or behind a login are not seen.

### Minimal input

```json
{"domains": ["allbirds.com", "hubspot.com"], "checkFor": ["Shopify", "HubSpot"]}
```

### Sample output row (real platform run, shortened)

```json
{
  "input": "kyliecosmetics.com",
  "domain": "kyliecosmetics.com",
  "finalUrl": "https://kyliecosmetics.com/",
  "status": "ok",
  "detected": true,
  "pageTitle": "Kylie Cosmetics by Kylie Jenner | Kylie Jenner Fragrances | Kylie Skin",
  "technologyCount": 13,
  "platform": "Shopify",
  "ecommerce": [
    "Shopify"
  ],
  "crmMarketing": [
    "Klaviyo"
  ],
  "analytics": [],
  "hosting": [
    "Cloudflare"
  ],
  "emailProvider": [
    "Google Workspace"
  ],
  "checkFor": {
    "Shopify": true,
    "WooCommerce": false,
    "Klaviyo": true
  },
  "scrapedAt": "2026-10-11T07:16:08+00:00"
}
```

### What it does

For each domain it reads the home page and its response headers and cookies, and looks up the domain's MX, TXT and name-server DNS records. It matches about 270 technology fingerprints across 24 categories. No browser is used, and no page beyond the home page is requested.

- **Website:** CMS (WordPress, Drupal, Ghost, Contentful), builders (Wix, Squarespace, Webflow, Framer), shop platforms (Shopify, WooCommerce, BigCommerce, Magento, PrestaShop), JavaScript frameworks (React, Next.js, Vue, Nuxt, Angular).
- **Money and marketing:** payments (Stripe, PayPal, Klarna, Shop Pay), CRM and automation (HubSpot, Salesforce, Marketo, Klaviyo, Mailchimp, ActiveCampaign, Brevo), chat and support (Intercom, Zendesk, Drift, Crisp), A/B testing.
- **Tracking:** analytics (Google Analytics, Plausible, Mixpanel, Hotjar, Sentry), ad pixels (Meta, TikTok, LinkedIn, Google Ads), tag managers, cookie consent tools.
- **Infrastructure:** CDN and hosting (Cloudflare, Vercel, Netlify, AWS, Fastly), DNS provider, security services.
- **Email, from DNS:** mailbox provider (Google Workspace, Microsoft 365, Zoho, Proton) read from MX records, sending services (SendGrid, Amazon SES, Postmark, Mailgun) and workplace tools (Atlassian, Slack) named in SPF and verification TXT records.

Every detected technology comes with the **evidence** that proved it (for example `html: cdn.shopify.com`, `header cf-ray`, `MX aspmx.l.google.com`), so an agent can trust or discard it. A link to a tool in the page text is not treated as usage.

### Use cases

- **AI agents and SDR pipelines** that need to qualify a lead: "which of these 500 domains run Shopify and Klaviyo?"
- **Technographic lead lists:** find companies that use a competitor's product, or lack the product you sell.
- **Competitor and market research:** which stack do the leaders in a niche run?
- **Agencies and freelancers:** spot WordPress sites on old themes, stores without analytics, sites without a cookie banner.
- **Email deliverability and sales ops:** which mailbox provider and sending tools a prospect uses.

### Use it from n8n, Make, Zapier or an AI agent

Actor ID: `datagleaner/tech-stack-by-domain`

Minimal input:

```
{"domains": ["allbirds.com", "hubspot.com"], "checkFor": ["Shopify", "HubSpot"]}
```

Each tool below runs this Actor with your own Apify API token.

- **n8n:** add the **Apify** node (`@apify/n8n-nodes-apify`). Choose **Run an Actor and get dataset**, set Actor to `datagleaner/tech-stack-by-domain` and paste the input above.

- **Make:** use the Apify app's **Run an Actor** module, then **Get Dataset Items**.

- **Zapier:** use the Apify action **Run Actor**, then the search **Fetch dataset items**.

- **AI agents (MCP):** connect to `https://mcp.apify.com/?tools=datagleaner/tech-stack-by-domain`. In Claude Code:

  ```
  claude mcp add --transport http apify "https://mcp.apify.com/?tools=datagleaner/tech-stack-by-domain"
  ```

  Then run `/mcp` to sign in to Apify in your browser. Other clients can sign in with OAuth or send the header `Authorization: Bearer YOUR_APIFY_TOKEN`. Then ask the agent in plain words, for example:

  > Which of acme.com, globex.com and initech.io use HubSpot, and what email provider does each use?

- **LangChain (Python):**

```python
## pip install langchain-apify, then set APIFY_TOKEN in your environment
import json
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("datagleaner/tech-stack-by-domain")
result = tool.invoke({"run_input": json.loads('{"domains": ["allbirds.com"], "checkFor": ["Shopify"]}')})
```

Through the API, one call returns the rows:

```bash
curl -X POST "https://api.apify.com/v2/acts/datagleaner~tech-stack-by-domain/run-sync-get-dataset-items?token=<APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"domains": ["allbirds.com", "plausible.io"], "checkFor": ["Shopify"]}'
```

Each domain is one row with the same fields every time, so an agent can read `platform`, `crmMarketing` or `checkFor.HubSpot` without parsing. A typical domain takes 1 to 3 seconds.

### Input

```json
{
  "domains": ["allbirds.com", "https://www.hubspot.com", "plausible.io"],
  "checkFor": ["Shopify", "HubSpot", "Klaviyo"],
  "maxConcurrency": 10
}
```

| Field | Meaning |
|---|---|
| `domains` | Domains or URLs, one per line. Any path is ignored: the lookup reads the home page. |
| `datasetId`, `datasetUrlField`, `maxDatasetItems` | Read websites from another Actor's dataset (see below). `maxDatasetItems` caps the distinct domains read (default 1000). |
| `checkFor` | Optional technology names to answer yes or no for. Matched case-insensitively against the detected names. |
| `maxConcurrency` | Domains looked up in parallel (default 10). |
| `requestTimeoutSecs`, `respectRobotsTxt`, `proxyConfiguration` | Network options. Leave the proxy empty: sites are read directly, and a site that blocks with HTTP 403 or 429 is retried through Apify Proxy automatically. |

Leave `domains` empty to run a 3-domain example.

### Run it after Google Maps Scraper or any scraper

Open the source Actor's **Integrations** tab, add this Actor (`datagleaner/tech-stack-by-domain`) and set its input to:

```json
{"datasetId": "{{resource.defaultDatasetId}}"}
```

Every time the source run succeeds, this Actor reads the websites from its results (`website`, `url`, `domain`, `site`, `homepage`, `link`, `companyWebsite` or `web`, the first one present), skips items with no website, social-media or map links, and duplicates, and detects the tech stack of each (at most `maxDatasetItems`, default 1000). Each row also carries `sourceName`, the `title` or `name` of the source item (for example the Google Maps place name). Billing is the same: one event per domain with a detected stack.

The same works from code, for one finished run:

```python
run = client.actor("datagleaner/tech-stack-by-domain").call(run_input={"datasetId": "SOURCE_DATASET_ID"})
```

The status message tells how many domains were read from the dataset and how many items had no website.

It also works as an integration without setting any input: in the source Actor's Integrations tab choose Add integration and pick this Actor, and the dataset is passed automatically.

### Output

One row per domain. Lists that found nothing are empty, never guessed.

```json
{
  "input": "allbirds.com",
  "domain": "allbirds.com",
  "finalUrl": "https://www.allbirds.com/",
  "status": "ok",
  "detected": true,
  "pageTitle": "Allbirds | Sustainable shoes",
  "language": "en",
  "technologyCount": 8,
  "platform": "Shopify",
  "technologies": [
    {"name": "Shopify", "category": "Ecommerce platform", "evidence": "html: cdn.shopify.com"},
    {"name": "Microsoft 365", "category": "Email provider", "evidence": "MX allbirds-com.mail.protection.outlook.com"}
  ],
  "ecommerce": ["Shopify"],
  "payments": ["Shop Pay"],
  "crmMarketing": [],
  "analytics": ["Microsoft Clarity"],
  "hosting": ["Cloudflare"],
  "emailProvider": ["Microsoft 365"],
  "emailSending": [],
  "checkFor": {"Shopify": true, "HubSpot": false},
  "error": null,
  "scrapedAt": "2026-10-11T09:00:00+00:00"
}
```

The full row also has `cms`, `websiteBuilder`, `chatSupport`, `advertising`, `tagManagers`, `abTesting`, `frameworks`, `security`, `cookieConsent`, `forms`, `workplaceTools`, `dns`, `other` and `byCategory`. `status` is `ok`, `unreachable` or `blockedByRobots` (only DNS records could be read, which can still return an email provider), `parked`, `invalidDomain` or `error`. `detected` tells you whether the row was billed.

### Pricing

Pay per event, one event:

- **`tech-stack-found`: $0.0025** ($2.50 per 1,000) for one domain where at least one technology was detected.
- Unreachable, parked, invalid and empty domains are **free**, and still returned so you can see why.

**Worked example.** A 2,000-domain lead list, of which about 90% return a detected stack: 1,800 x $0.0025 = **$4.50**. Platform usage is small: a domain takes about five light requests (robots.txt, the home page and three DNS lookups).

Set a maximum total charge on the run and the Actor stops cleanly when it is reached.

### Limits

- **Fingerprints, not a database.** Detection is based on what the home page, its headers and the domain's DNS show. It does not know technologies used only on inner pages or behind a login, and it is not a historical index.
- **No JavaScript rendering.** A tool injected only by a tag manager after the page loads may be missed; the tag manager itself is reported.
- **Sites that block bots.** A site that answers HTTP 403 or 429 is retried once through Apify Proxy. Sites behind a hard bot wall come back `unreachable`, with whatever the DNS records show.
- **DNS evidence is indirect.** A sending service or workplace tool in a TXT record shows the company has set it up for the domain, not that it is the only one in use.
- The fingerprint list covers the common tools. If one you need is missing, tell us in the Issues tab.

### FAQ

**Can I get a list of all sites using a technology?** No. You bring the domains; it returns each one's stack. There is no subscription or API key, and a miss costs nothing.

**How do I check if a company uses HubSpot (or Shopify, Klaviyo, Stripe)?** Put the domain in `domains` and the name in `checkFor`; the row has `checkFor.HubSpot` true or false, with the evidence in `technologies`.

**How do I find a domain's email provider?** Read `emailProvider`. It comes from the MX records, so it works even when the website itself cannot be read.

**Do I need an API key or login?** No. Only public pages and public DNS records are read.

**Can I get the contacts too?** Use [Company Enrichment by Domain](https://apify.com/datagleaner/company-enrichment-by-domain) for the profile and socials, or [Website Contact Details Scraper](https://apify.com/datagleaner/website-contact-details-scraper) for every email and phone on the site.

### Related Actors

- [Company Enrichment by Domain](https://apify.com/datagleaner/company-enrichment-by-domain): name, logo, official social profiles and contacts from a domain.
- [Website Contact Details Scraper](https://apify.com/datagleaner/website-contact-details-scraper): every email, phone, social profile and contact form on a website.

### Responsible use

This Actor reads publicly available web pages and public DNS records only, and returns facts about technologies, not about people. You are responsible for complying with the law that applies to you, including anti-spam and data protection rules, when you use the results to contact companies.

# Actor input Schema

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

Company domains or website URLs, one per line. `allbirds.com`, `www.allbirds.com` and `https://allbirds.com/products` all work; the lookup always reads the home page. Leave empty to run a 3-domain example.

## `datasetId` (type: `string`):

Optional. Read company websites from this dataset, for example Google Maps Scraper results, and add them to the domains above. Each item's `website` (or `url`, `domain`, `site`, `homepage`, `link`, `companyWebsite`, `web`) is used; items without one are skipped. In an integration use `{{resource.defaultDatasetId}}`.

## `datasetUrlField` (type: `string`):

Optional. Name of the field that holds the website when it is not one of the usual ones. Tried first.

## `maxDatasetItems` (type: `integer`):

Stops reading the dataset after this many distinct domains. Only used with a dataset ID.

## `checkFor` (type: `array`):

Technology names to answer yes or no for, e.g. `Shopify`, `HubSpot`, `Klaviyo`, `Google Analytics`. Each row gets a `checkFor` object such as {"HubSpot": true}. Names are matched case-insensitively against the detected technologies.

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

How many domains are looked up at the same time.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for the home page before retrying.

## `respectRobotsTxt` (type: `boolean`):

Skip the home page when the site's robots.txt disallows it. The DNS records are still read.

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

Optional. Leave empty: sites are read directly, and a site that blocks with HTTP 403 or 429 is retried through Apify Proxy automatically. Set a proxy here only to send every request through it.

## Actor input object example

```json
{
  "domains": [
    "allbirds.com",
    "plausible.io",
    "hubspot.com"
  ],
  "maxDatasetItems": 1000,
  "maxConcurrency": 10,
  "requestTimeoutSecs": 15,
  "respectRobotsTxt": true
}
```

# Actor output Schema

## `results` (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": [
        "allbirds.com",
        "plausible.io",
        "hubspot.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datagleaner/tech-stack-by-domain").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": [
        "allbirds.com",
        "plausible.io",
        "hubspot.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("datagleaner/tech-stack-by-domain").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": [
    "allbirds.com",
    "plausible.io",
    "hubspot.com"
  ]
}' |
apify call datagleaner/tech-stack-by-domain --silent --output-dataset

```

## MCP server setup

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

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/dFJ68uOgRYqQdeQOV/builds/3hjIpeFWP0ga1djtq/openapi.json
