# Company Enrichment by Domain – B2B Company Card (`ausbizstack/company-enrichment-by-domain`) Actor

Company enrichment API by domain. Input: domains or URLs. Returns one B2B company card per domain: DMARC/SPF/DKIM email security, tech stack, CMS, ecommerce, llms.txt AI readiness, careers/ATS hiring, public emails, phones, socials, AU ABN match and overall score. $0.02/company; blocked rows free.

- **URL**: https://apify.com/ausbizstack/company-enrichment-by-domain.md
- **Developed by:** [AusBizStack Intelligence](https://apify.com/ausbizstack) (community)
- **Categories:** Lead generation, Developer tools, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $14.00 / 1,000 company enricheds

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

## Company Enrichment by Domain – B2B Company Card

**Give it a list of company domains and get one B2B company card per domain. Each card covers email security (MX provider, SPF, DMARC, DKIM provider), tech stack, CMS and ecommerce platform, llms.txt and AI-search readiness, careers/ATS hiring signals, public contact emails, phones and social links, an optional Australian ABN match, and an overall score. You pay only for domains that return a full card.**

#### ▶️ Try it now

The input is prefilled with `stripe.com`, `shopify.com` and `hubspot.com`. Click **Start**: three company cards arrive in about 5 seconds and cost about $0.06.

Real rows from our cloud test run (512 MB–1 GB, no proxy):

| domain | status | mail provider | DMARC | CMS / ecommerce | llms.txt | ATS (open roles) | ABN match | overall score | charged |
|---|---|---|---|---|---|---|---|---|---|
| stripe.com | ok | Google Workspace | reject | Contentful | ✅ | – | – | 76 | ✅ |
| shopify.com | ok | Google Workspace | reject | Shopify / Shopify | ✅ | – | – | 62 | ✅ |
| hubspot.com | ok | Google Workspace | reject | HubSpot CMS | ✅ | – | – | 78 | ✅ |
| cultureamp.com | ok | Proofpoint | reject | – | ✅ | Greenhouse (33) | Culture Amp Pty Ltd · medium | 79 | ✅ |
| figma.com | ok | Google Workspace | quarantine | Sanity | ❌ | Greenhouse (141) | – | 74 | ✅ |
| jbhifi.com.au | ok | Proofpoint | quarantine | Contentful / Shopify | ✅ | – | JB HI-FI LIMITED · medium | 78 | ✅ |
| linktr.ee | ok | Google Workspace | reject | Next.js (framework) | ✅ | careers page (~27 role links) | LINKTREE PTY LTD · high | 75 | ✅ |
| canva.com | blocked | Google Workspace | reject | – (Cloudflare challenge) | – | – | – | – | **$0** |

Every input gets exactly **one row**, including failures, so you can join results back to your list. DNS results (MX, SPF, DMARC, DKIM) are still returned on `blocked` rows, and those rows are free.

#### 💰 What it costs

Pay-per-event: **$0.02 per company card** (`company-enriched`). **Blocked, unreachable, timed-out and invalid inputs are never charged.**

| Volume | FREE plan ($0.02) | BRONZE ($0.018) | SILVER ($0.016) | GOLD / PLATINUM / DIAMOND ($0.014) |
|---|---|---|---|---|
| 100 domains | **$2.00** | $1.80 | $1.60 | $1.40 |
| 1,000 domains | **$20.00** | $18.00 | $16.00 | $14.00 |
| 10,000 domains | **$200.00** | $180.00 | $160.00 | $140.00 |

A tiny start fee ($0.00005 per GB of memory) applies per run. Set **Max cost per run** in the run options and the Actor stops cleanly when it reaches your budget. Rows that are not charged are marked `"charged": false`.

***

### 💳 What counts as a billed result

You are charged **one `company-enriched` event ($0.02) per domain** only when the row is `status: "ok"` (`"charged": true`). That is the only per-result charge. Apify also adds its standard tiny start fee per run (`apify-actor-start`).

| Row in your dataset | Charged? |
|---|---|
| `status: "ok"` | **Yes, $0.02** |
| `blocked`, `unreachable`, `timeout`, `error` | No, $0 |
| `invalid_input` (not a domain, URL or email) | No, $0 |
| `duplicate_input` (same domain as an earlier line) | No, $0 |

**Accuracy:** failed fetches (timeouts, network errors, 403 / 429 / 5xx and bot-challenge pages) are retried and then reported as `fetch_failed`, never as missing; only a real 404 / 410 counts as missing (see `llmsTxtStatus`, `robotsTxtStatus`, `sitemapStatus`). Unverified checks are left out of `aiReadinessScore` (`aiReadinessScoreIsPartial: true`), and a domain that is unreachable after retries is not charged.

A non-Australian company costs exactly the same as an Australian one. The ABN block is simply `null` and `auFieldsNote` says why (see below).

### 🧾 Accepted input formats

Paste your list as-is into `domains`. Each line can be a bare domain (`stripe.com`), a domain with `www.`, mixed case, spaces or a trailing slash (`WWW.Stripe.com/`), a full URL (`https://www.stripe.com/pricing/`) or an email address (`sales@stripe.com`, `mailto:sales@stripe.com`). All of these resolve to `stripe.com`.

Inputs are cleaned and de-duplicated **before** anything runs or is charged. A second line for the same domain comes back as a free `duplicate_input` row with `duplicateOf`, and every row keeps what you typed in `input`.

**Australian-only fields:** `abn` (ABN, GST, entity status) only applies to Australian businesses. For a non-Australian domain it is `null`, `auFieldsApplicable` is `false` and `auFieldsNote` reads *"Not an Australian domain - ABN/GST/entity status fields only apply to Australian businesses, so they are left empty (null)."*

### 📊 Measured quality (not marketing numbers)

We ran **39 real public company domains** on the Apify cloud (SaaS, fintech, AU retail, ecommerce brands, dev tools), with no proxy:

| Metric | Result |
|---|---|
| Full company cards (`ok`) | **33 / 39 = 85%** |
| Blocked by bot protection (Cloudflare / Akamai) | 6: canva.com (twice), bunnings.com.au, woolworths.com.au, kmart.com.au and seek.com.au. These were returned as `blocked` with DNS and email-security data, **not charged** |
| Invalid input test (`not a domain`) | returned as `invalid_input`, **not charged** |
| Run time | 36 domains in **59 s** (concurrency 8), 3 domains in 4 s |
| Peak memory | 210 MB, so the 512 MB default is enough |
| Our platform cost | about $0.0001 per domain, so the price is sustainable |
| Australian ABN matches | 9 of the 10 `ok` sites with an Australian signal were matched to a registered ABN (1 high, 8 medium). nab.com.au returned `null` rather than a weak guess. No match means `abn: null`; we do not guess |

Sites behind hard bot protection block data-centre IPs. They come back as a clear `blocked` row with the reason, and you pay nothing.

### ✨ What is in each card

| Section | Fields |
|---|---|
| **Email security** | `mxProvider` (Google Workspace, Microsoft 365, Proofpoint, Mimecast, Cisco…), `spfStatus`, `spfAuthorisedSenders`, `spfDnsLookupsApprox`, `dmarcPolicy`, `dmarcPct`, `dmarcRuaAddresses`, `dkimSelectors`, `dkimProviders` (SendGrid, Amazon SES, Mailchimp, HubSpot…), `spoofingRisk`, `emailSecurityScore` |
| **Tech stack** | `cms` (WordPress, Shopify, Webflow, Contentful, Sanity, AEM, Sitecore, HubSpot CMS…), `frontendFramework` (Next.js, Nuxt, Astro…), `ecommercePlatform`, `isEcommerce`, `paymentProviders`, `techStack`, `techByCategory`, `techCount` |
| **AI-search readiness** | `hasLlmsTxt`, `hasLlmsFullTxt`, `llmsTxtTitle`, `hasRobotsTxt`, `hasSitemapXml` (`true` / `false` / `null` when it could not be fetched), `llmsTxtStatus` / `robotsTxtStatus` / `sitemapStatus` (`present`, `missing`, `invalid`, `fetch_failed`), `blockedAiBots` (GPTBot, ClaudeBot, PerplexityBot…), `hasOrganizationSchema`, `aiReadinessScore` |
| **Hiring signals** | `careersPageUrl`, `atsProvider` (Greenhouse, Lever, Ashby, Workday, SmartRecruiters…), `atsBoardUrl`, `openRolesCount` (from the public ATS board API when available), `openRolesEstimateFromPage`, `hiringSignal` |
| **Contact** | `publicEmails`, `phones`, `socialLinks` (LinkedIn, X, Facebook, Instagram, YouTube, TikTok…) |
| **Australian ABN** | `abn` object: ABN, registered name, entity type, status, GST, state/postcode, business names, `matchSource`, `matchConfidence` (high/medium/low) and ABR link. This is `null` when there is no confident match or the ABR page is blocked |
| **Web & score** | `webSecurityHeadersScore`, `missingSecurityHeaders`, `overallScore` (0–100), `scoreBreakdown`, `keyFindings` |

Everything is done in one run in one process, with no calls to other Actors and no API keys needed.

### 📥 Input

```json
{
  "domains": ["stripe.com", "https://www.shopify.com/pricing", "hubspot.com"],
  "checkDkim": true,
  "checkHiring": true,
  "matchAustralianAbn": true,
  "maxContactPages": 1,
  "concurrency": 8
}
```

- `domains` takes bare domains, full URLs or `www.` hosts. Duplicates are removed.
- `matchAustralianAbn` runs only when the site looks Australian (`.au` domain, AU phone number, an ABN printed on the site, or "Australia" in the title). You can add an `abrGuid` (a free ABR web-services GUID) for the official lookup.
- `maxItems` caps the number of domains processed.
- `perDomainTimeoutSecs` defaults to 75 (each check is retried up to twice before it is reported as `fetch_failed`).

### 📤 Output sample (trimmed)

```json
{
  "domain": "linktr.ee",
  "status": "ok",
  "charged": true,
  "companyName": "Linktree",
  "countryDetected": "AU",
  "mxProvider": "Google Workspace",
  "spfStatus": "strict (-all)",
  "dmarcPolicy": "reject",
  "dkimProviders": ["Google Workspace", "SendGrid"],
  "spoofingRisk": "low",
  "emailSecurityScore": 100,
  "frontendFramework": "Next.js",
  "techStack": ["Intercom", "Amplitude", "Google Tag Manager", "Next.js"],
  "hasLlmsTxt": true,
  "aiReadinessScore": 75,
  "careersPageUrl": "https://linktr.ee/about/careers",
  "openRolesEstimateFromPage": 27,
  "hiringSignal": "careers page found",
  "publicEmails": ["press@linktr.ee"],
  "socialLinks": {"linkedin": "https://www.linkedin.com/company/linktree", "x_twitter": "https://x.com/Linktree_"},
  "abn": {"abn": "68 608 721 562", "entityName": "LINKTREE PTY LTD", "matchSource": "website_abn", "matchConfidence": "high"},
  "webSecurityHeadersScore": 60,
  "overallScore": 75,
  "scoreBreakdown": {"emailSecurity": 100, "aiSearchReadiness": 75, "webSecurityHeaders": 60, "contactability": 70, "techMaturity": 52}
}
```

`status` is one of `ok` (charged), `blocked`, `unreachable`, `timeout`, `invalid_input` or `duplicate_input` (all free).

### 🤖 Use it from AI agents (MCP)

Add this Actor to Claude, Cursor, ChatGPT or any MCP client through the Apify MCP server:

```
https://mcp.apify.com/?actors=ausbizstack/company-enrichment-by-domain
```

Then ask, for example: *"Enrich these 20 domains and tell me which use Microsoft 365, have no DMARC, and are hiring on Greenhouse."* The agent gets one structured JSON row per domain.

### 🔌 API (curl)

```bash
curl -X POST "https://api.apify.com/v2/acts/ausbizstack~company-enrichment-by-domain/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"domains":["stripe.com","shopify.com","hubspot.com"]}'
```

### 🎯 Use cases

- **Lead enrichment and scoring:** add the mail provider, CMS, ecommerce platform and hiring status to a CRM list before outreach.
- **Email deliverability / security prospecting:** find companies with `dmarcPolicy: none` or a soft SPF.
- **Agency prospecting:** find Shopify or WordPress stores, sites without llms.txt, or sites that block AI crawlers.
- **Recruiting / sales triggers:** see which companies have an ATS board and how many open roles they list.
- **Australian B2B data:** match a website to its ABN and registered entity.

### ❓ FAQ

**Is a blocked site charged?** No. `blocked`, `timeout`, `unreachable`, `invalid_input` and `duplicate_input` rows are free and say why.

**Does it use proxies?** No. It makes polite direct requests: the homepage, up to 3 contact/about pages, robots.txt, llms.txt, sitemap.xml and the careers page.

**How accurate is tech detection?** It is signature-based on the homepage HTML, scripts and headers. Ecommerce platforms are only reported when the platform's own markers are present, not when the name is just mentioned on the page.

**Why is `abn` null?** Either the site does not look Australian, no registered entity matched the company name confidently, or the ABR lookup page was unavailable. We never return a guessed ABN.

**Is the data personal?** It returns only company-level public information published on the company's own website and in public DNS.

Built by **AusBizStack**. Issues and feature requests are welcome on the Issues tab.

# Actor input Schema

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

Company domains or URLs, one per line (e.g. stripe.com, https://www.canva.com). www. and paths are stripped; duplicates are removed. One output row per domain. Aliases accepted via API: `urls`, `targets`, `websites`.

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

Optional cap on how many domains to process (0 = no cap).

## `checkDkim` (type: `boolean`):

Looks up about 60 common DKIM selectors (Google, Microsoft 365, SendGrid, Mailchimp, HubSpot, Klaviyo, Amazon SES, ...) to show which providers sign the domain's email.

## `checkHiring` (type: `boolean`):

Finds the careers page, detects the ATS (Greenhouse, Lever, Ashby, Workable, Workday, SmartRecruiters and more) and counts open roles from public job-board APIs where available.

## `matchAustralianAbn` (type: `boolean`):

For sites with an Australian signal (.au domain, AU phone, Pty Ltd, Australian address), finds the ABN on the site or by name search on the public ABN Lookup, with a match confidence. Skipped for blocked pages.

## `abrGuid` (type: `string`):

Optional free GUID from abr.business.gov.au for the official ABN Lookup JSON service. Not needed for normal use.

## `maxContactPages` (type: `integer`):

Extra same-site pages (contact, about) read for emails, phones and socials. 0 = homepage only.

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

Domains processed in parallel.

## `perDomainTimeoutSecs` (type: `integer`):

Rows that take longer are returned as status 'timeout' and are not charged.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "hubspot.com",
    "shopify.com"
  ],
  "maxItems": 0,
  "checkDkim": true,
  "checkHiring": true,
  "matchAustralianAbn": true,
  "maxContactPages": 1,
  "concurrency": 8,
  "perDomainTimeoutSecs": 75
}
```

# Actor output Schema

## `companyCards` (type: `string`):

No description

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

// Run the Actor and wait for it to finish
const run = await client.actor("ausbizstack/company-enrichment-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": [
        "stripe.com",
        "hubspot.com",
        "shopify.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("ausbizstack/company-enrichment-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": [
    "stripe.com",
    "hubspot.com",
    "shopify.com"
  ]
}' |
apify call ausbizstack/company-enrichment-by-domain --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,ausbizstack/company-enrichment-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/FHPJoom3KBgBiSvbw/builds/qGQH6numpUbPqK8hu/openapi.json
