# Tech Stack Detector & DNS Intelligence Scraper (`midnight_static/techstack-dns-intelligence`) Actor

Bulk tech stack detector and DNS lookup for any domain. Website technology: 150 fingerprints, 22 categories. Plus technographics from DNS TXT records, not the HTML: Salesforce, HubSpot, Atlassian, Slack. Email provider, SPF/DMARC, TLS certificate expiry. 45 fields. BuiltWith alternative.

- **URL**: https://apify.com/midnight\_static/techstack-dns-intelligence.md
- **Developed by:** [Midnight Static](https://apify.com/midnight_static) (community)
- **Categories:** Lead generation, Developer tools, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $35.00 / 1,000 domains

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 & DNS Intelligence

Point it at a domain and get back what the company actually runs — including the back-office software that never appears in the page source. A Wappalyzer/BuiltWith alternative with a second layer bolted on. **45 fields per domain**, as JSON, CSV or Excel.

### Four things that are different here

**1. It sees the software that isn't in the HTML.**
Every tech detector reads the page. This one also reads the domain's DNS `TXT` records, where companies leave verification tokens for the tools they log into every day: Salesforce, Atlassian, DocuSign, Zoom, Miro, Slack, Figma, Airtable. Measured on 16 domains: **120 pieces of software found in DNS that page-based detection never saw** — 7.5 per domain. That is the CRM and the ticketing system, not the front-end framework.

**2. It still returns a record when the site blocks you.**
DNS and TLS are public infrastructure; no WAF sits in front of them. In the measured run `trendyol.com` answered the page request with **HTTP 403** and still produced a **40-field record**. A blocked site is a data point, not a gap.

**3. Email and domain posture, which is what sales and security teams actually filter on.**
Email provider from the MX records (Google Workspace, Microsoft 365, Proofpoint, Mimecast, Zoho and more), DNS provider, SPF presence with its include count, and the real DMARC policy — `none`, `quarantine` or `reject`. Then the TLS certificate: issuer, expiry, days remaining, SAN count and negotiated protocol.

**4. Every claim in this README is a measurement, and the misses are listed too.**
See the table at the bottom. It includes the domain where we detected **zero** technologies and says why.

### What you get

**Identity and fetch**
Domain, URL, final URL after redirects, HTTP status, whether the page was fetched, the reason if it was not, page size.

**Technologies**
Detected technologies each with a name, a category and a version where the page exposes one. Plus the name list, the category list and both counts. 150 fingerprints across 22 categories: ecommerce platforms, CMS and site builders, frameworks, back ends, CDN and hosting, analytics, advertising pixels, marketing automation, support chat, payment, consent platforms, bot management, error monitoring, search, A/B testing, media, fonts and common libraries.

**Security headers**
HSTS with its max-age in days, CSP and whether it is report-only, X-Frame-Options, X-Content-Type-Options, Referrer-Policy, Permissions-Policy, plus the `Server` and `X-Powered-By` strings.

**DNS**
Email provider, MX host list and count, DNS provider, nameserver count, IPv4 count, SPF presence and include count, DMARC presence and policy, TXT record count, and the SaaS list derived from verification tokens with a category on each.

**Certificate**
Issuer, subject CN, expiry date, days remaining, SAN count, TLS protocol.

**Roll-ups**
`saasAll` merges everything found in both layers. `dnsOnlySaas` is the part page-based detection would have missed — the column worth sorting by.

### Typical uses

Qualifying a lead list before anyone calls it: who is on Shopify, who runs HubSpot, who already pays for Salesforce. Finding every prospect whose DMARC is still `none` if you sell email security. Watching a competitor's stack change over time by running on a schedule. Checking certificate expiry across a portfolio of domains. Filling the gap when a BuiltWith or Wappalyzer subscription lapses.

### Example record (trimmed)

```json
{
  "domain": "stripe.com",
  "httpStatus": 200,
  "technologyCount": 5,
  "technologyNames": ["Next.js", "Nginx", "Open Graph", "Twitter Cards", "JSON-LD"],
  "emailProvider": "Google Workspace",
  "mxRecordCount": 5,
  "dnsProvider": "Amazon Route 53",
  "hasSpf": true,
  "spfIncludeCount": 3,
  "dmarcPolicy": "reject",
  "dnsOnlySaas": ["Google Search Console", "Microsoft 365", "Atlassian", "Meta Business",
                  "Stripe", "DocuSign", "Canva", "Postman", "OpenAI", "Anthropic"],
  "hasHsts": true,
  "hstsMaxAgeDays": 730,
  "hasCsp": true,
  "certIssuer": "DigiCert Inc",
  "certDaysRemaining": 56,
  "tlsProtocol": "TLSv1.3",
  "filledFieldCount": 42
}
```

### Input

Paste domains one per line. A full URL works — everything after the host is ignored, and `example.com`, `www.example.com` and `https://www.example.com/pricing` collapse to one domain charged once.

Each layer has its own switch: `includeHtmlDetection`, `includeDns`, `includeTls`. Turn off what you do not need. `maxItems` caps how many domains are charged so a long list cannot produce an unexpected bill. It defaults to 100, which costs $5.00 — exactly the monthly credit on Apify's Free plan.

### Failures are loud

Counters are reconciled at the end of every run: domains processed must equal records written plus domains skipped, and the run **fails** if they do not. The breakdown goes to `RUN_STATS`, every skipped domain with its reason to `SKIPPED`.

### Limits and compliance

`robots.txt` is read before the page request and a disallowed homepage is never fetched. Because DNS and TLS are public infrastructure records rather than site content, those two layers still run in that case and the record is written with `htmlFetched: false` and the reason in `htmlSkipReason`. If `robots.txt` cannot be read at all, the page is not fetched.

**No personal data is collected, in any layer.** The output is infrastructure and software. WHOIS registrant fields, email addresses and personal names are never read — the DNS queries are limited to `MX`, `TXT`, `NS` and `A`.

The fingerprint set is written and maintained here rather than imported from a third-party ruleset, so no external data licence travels with the output.

### Pricing

**$0.05 per domain, everything included.** No start fee, no minimum charge per run, no separate platform usage line.

Paid Apify plans pay less, automatically — there is nothing to apply for: Bronze $0.045, Silver $0.040, Gold and above $0.035 per domain. The Free plan price is unchanged at $0.05.

You are charged per domain that produced data in at least one layer. A domain that resolves to nothing is reported in the run statistics and costs you nothing.

### Measured performance

Run on 18 September 2026 from Apify's own network, 20 inputs deliberately including a blocked site, a domain that does not exist, malformed text and the same domain written three ways:

| | |
|---|---|
| Inputs accepted as domains | 17 of 20 (malformed text dropped before any request) |
| Records written | 16 |
| Skipped, with a reason, charged nothing | 1 (`DOMAIN_NOT_RESOLVED`) |
| Counter reconciliation | 17 = 16 + 1, passed |
| Fields populated per domain | 40 of 45 (median), 33 worst |
| Technologies detected per domain | 6 (median), 11 (max) |
| SaaS found only in DNS | 120 total, 7.5 per domain |
| Records produced despite an HTTP failure | 1 of 1 (`trendyol.com`, HTTP 403, 40 fields) |
| Same domain given three ways | collapsed to 1 record, charged once |
| Personal data found in output | none — scanned for emails, phone numbers and registrant fields |
| Wall clock | 7 seconds |

**The miss:** `zara.com` returned **zero** technologies. Its homepage is a 2 KB redirect shell with nothing to fingerprint, so page-based detection has nothing to work with — the record still carried 33 fields from the DNS and TLS layers. A detector that reads only the page returns almost nothing for a domain like this.

**The richest record** was `figma.com` at 40 fields: 9 technologies from the page, and 16 more products found only in DNS, including Atlassian, DocuSign, Segment, Twilio, OpenAI and Anthropic. None of those 16 appear anywhere in the HTML.

# Actor input Schema

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

One domain per line. A full URL works too, everything after the host is ignored. Duplicates and www. are collapsed, so example.com and https://www.example.com/pricing count as one domain and are charged once.

## `includeHtmlDetection` (type: `boolean`):

Fetch the homepage and match technology fingerprints in the HTML, response headers and cookies. robots.txt is checked first and a disallowed site is never fetched; the DNS and TLS layers still run.

## `includeDns` (type: `boolean`):

Read MX, TXT, NS and A records: email provider, DNS provider, SPF and DMARC posture, and the SaaS verification tokens that reveal back-office software never present in the HTML.

## `includeTls` (type: `boolean`):

Certificate issuer, expiry, days remaining, SAN count and negotiated TLS version.

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

Hard cap on how many domains are charged, so a long list cannot produce an unexpected bill. The default of 100 costs $5.00 on the Free plan, which is exactly the monthly credit Apify gives you. Raise it when you are ready to scan more.

## `concurrency` (type: `integer`):

How many domains are analysed in parallel.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "shopify.com",
    "notion.com"
  ],
  "includeHtmlDetection": true,
  "includeDns": true,
  "includeTls": true,
  "maxItems": 100,
  "concurrency": 5
}
```

# Actor output Schema

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

One record per domain: detected technologies with categories and versions, security headers, email and DNS provider, SPF/DMARC posture, SaaS found only in DNS, and the TLS certificate.

## `runStats` (type: `string`):

Counters: domains processed, records written, HTML successes and failures, robots-blocked, empty DNS, empty TLS, retries.

## `skipped` (type: `string`):

Every domain that produced no data in any layer, with the reason. Free, never charged.

# 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": [
        "stripe.com",
        "shopify.com",
        "notion.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("midnight_static/techstack-dns-intelligence").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": [
        "stripe.com",
        "shopify.com",
        "notion.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("midnight_static/techstack-dns-intelligence").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": [
    "stripe.com",
    "shopify.com",
    "notion.com"
  ]
}' |
apify call midnight_static/techstack-dns-intelligence --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,midnight_static/techstack-dns-intelligence"
        }
    }
}
```

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/m9QXEcxagIrQzLxDA/builds/CPLNZa6brWDVf515h/openapi.json
