# Company Enrichment — firmographics, contacts, tech, hiring (`greyridge-signals/company-enrichment-scraper`) Actor

Give company domains (stripe.com) and get one deduped, live-from-source record each: firmographics, social links, tech-stack fingerprint, verified role emails (MX-graded) and phones, plus a live hiring signal from 8 ATS boards. Crawls each company's own site — keyless, no resold DB. Pay per event.

- **URL**: https://apify.com/greyridge-signals/company-enrichment-scraper.md
- **Developed by:** [Rez Archer](https://apify.com/greyridge-signals) (community)
- **Categories:** Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 company enrichments

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

## Company Enrichment Scraper

**Give it a domain, get one clean, live-from-source record per company:** firmographics,
verified business contacts, a tech-stack fingerprint, social profiles, and — uniquely — a
**live hiring signal derived from 8 ATS platforms**. No resold database, no paid API, keyless.

Most enrichment actors resell a static third-party database (ZoomInfo/Apollo-style) behind a
paid key. The data is stale the day it ships and you pay for a vendor you can't see. This actor
is different: it reads each company's **own public website live on every run**, grades the
contacts it finds, fingerprints the tech, and layers on a hiring signal pulled straight from the
company's live ATS. Nothing is cached and resold.

### What you get per company

```jsonc
{
  "input_domain": "stripe.com",
  "canonical_domain": "stripe.com",
  "company_name": "Stripe",
  "legal_name_guess": "Stripe, Inc.",
  "description": "Stripe powers online and in-person payment processing ...",
  "industry": null,
  "founded_year": null,
  "employee_size_hint": null,
  "hq_country": "US",
  "office_address": {
    "street": "354 Oyster Point Blvd", "locality": "South San Francisco",
    "region": "CA", "postal_code": "94080", "country": "US",
    "formatted": "354 Oyster Point Blvd, South San Francisco, CA, 94080, US",
    "source": "jsonld"
  },
  "legal_registration": {
    "vat_number": "GB123456789", "vat_country": "GB",
    "company_number": "09876543", "registry": "UK-Companies-House"
  },
  "logo_url": "https://.../favicon.svg",
  "social_links": [{ "network": "github", "url": "https://github.com/stripe" }],
  "tech_stack": [
    { "name": "Next.js", "category": "js-framework" },
    { "name": "React", "category": "js-framework" }
  ],
  "contacts": {
    "emails": [
      { "email": "press@stripe.com", "type": "role", "domain": "stripe.com",
        "mx": true, "mx_host": "aspmx.l.google.com", "deliverability_grade": "B" }
    ],
    "phones": [{ "number": "+18889262289", "type": "business" }]
  },
  "hiring": {
    "ats_vendor": "greenhouse",
    "careers_url": "https://stripe.com/jobs",
    "open_reqs_count": 724,
    "departments_hiring": ["Engineering", "Sales", "..."],
    "locations_hiring": ["San Francisco", "Dublin", "..."],
    "remote_share": 0.15,
    "hiring_intensity": "high",
    "newest_posting_at": "2026-10-29T09:14:34.000Z"
  },
  "email_provider": "Google Workspace",
  "dns_saas": [
    { "name": "Atlassian", "category": "dev-tools" },
    { "name": "DocuSign", "category": "esignature" },
    { "name": "SendGrid", "category": "email-infra" }
  ],
  "mx_hosts": ["10 aspmx.l.google.com", "20 alt1.aspmx.l.google.com"],
  "domain_created_at": "1995-09-12T04:00:00Z",
  "domain_age_years": 31.1,
  "domain_expires_at": "2027-09-11T04:00:00Z",
  "registrar": "SafeNames Ltd.",
  "source_freshness": "live",
  "_enriched_at": "2026-10-29T11:42:31.697Z"
}
```

#### The hiring signal nobody else has

`hiring` is derived live from **8 ATS platforms** (Greenhouse, Lever, Ashby, SmartRecruiters,
Workday, Recruitee, Workable, BambooHR) with automatic vendor detection from the domain. It tells
you whether a company is actively hiring, how hard, in which departments and locations, and when it
last posted — a buying-intent and growth signal a static firmographics database simply cannot carry.

#### DNS & domain intelligence (what the crawl can't see)

Three more signals come straight from the domain's DNS and registration, keyless:

- **`email_provider`** — who runs the company's mail (Google Workspace, Microsoft 365, Proofpoint,
  Mimecast, Zoho, …), read from live MX records.
- **`dns_saas`** — the SaaS tools the company actually runs, inferred from DNS domain-verification
  tokens and the SPF `include:` chain (OpenAI, Atlassian, DocuSign, Stripe, SendGrid, Zendesk,
  Salesforce, Slack and dozens more). A technographic signal a page crawl never surfaces.
- **`domain_age_years` / `domain_created_at` / `registrar`** — domain age and registrar from RDAP
  (the authoritative registry source, discovered via the live IANA bootstrap — every TLD, no stale
  list). Domain age is a strong company-maturity and anti-fraud signal.

#### Office address & legal registration

- **`office_address`** — the postal address, structured (`street` / `locality` / `region` /
  `postal_code` / `country`) when the site exposes schema.org `PostalAddress`, with a `formatted`
  one-line fallback from the page's `<address>` block. Country-only markup is dropped rather than
  passed off as an address.
- **`legal_registration`** — the company's statutory identifiers parsed from imprint / legal
  footers, **keyword-anchored** so an order ID or SKU is never mistaken for one: `vat_number`
  (+ `vat_country`) for EU/UK VAT, and `company_number` with its `registry` (UK Companies House,
  German Handelsregister HRB/HRA, French SIREN). Both stay `null` when the site doesn't state them —
  never fabricated.

### Input

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `domains` | string\[] | — (required) | Company domains or URLs, one per company. |
| `maxCompanies` | integer | `0` (no cap) | Cap how many companies are processed. |
| `maxPagesPerCompany` | integer | `4` | Pages crawled per company (homepage + /about, /contact, ...). |
| `include_hiring` | boolean | `true` | Derive the live multi-ATS hiring block. |
| `include_contacts` | boolean | `true` | Extract emails (MX-graded) + E.164 phones. |
| `include_tech` | boolean | `true` | Tech-stack fingerprint. |
| `include_dns` | boolean | `true` | DNS & domain intelligence: email provider, SaaS-from-DNS, domain age/registrar. |
| `verifyEmailsSmtp` | boolean | `false` | Best-effort SMTP RCPT probe (port 25 is often blocked on egress, so this is advisory). |

```json
{ "domains": ["stripe.com", "ramp.com", "bunq.com"], "maxPagesPerCompany": 4 }
```

### Email deliverability grades

`A` MX present **and** an SMTP server accepted the address · `B` valid MX (mailbox existence
unconfirmed) · `D` resolves but no MX · `F` does not resolve. MX uses a free DNS lookup; the SMTP
step is opt-in and advisory. We never claim a grade stronger than the evidence.

### Pricing

Pay per event — you are charged once per **deduped enriched company record**, never per page
fetched or per contact found. Price is shown on the actor's Apify Store page.

### How it stays accurate (the maintenance you're renting)

Company sites and ATS response shapes drift constantly. A built-in freshness monitor probes the
adapters against live canaries and flags drift so the parsers are repaired before a bad run reaches
you. That upkeep is the reason to rent this instead of maintaining a crawl-plus-deliverability-plus-
anti-bot fleet yourself.

### Notes & scope

- **Business data only.** Published business firmographics and published business contact points
  (role inboxes, switchboard numbers, public social profiles). Fields absent from a site stay
  `null` — nothing is invented.
- **HTTP-first.** The common path is a fast keyless fetch of the company's own pages. A
  JavaScript-only site is the documented fallback case (a headless render); v0.1 handles the
  large majority that serve static/SSR HTML.
- **CLI:** the same core runs locally with no account — `node cli.js stripe.com ramp.com`.

### Run locally

```bash
npm install
node cli.js stripe.com bunq.com --pretty   # keyless, no account
npm test                                    # offline unit + wiring tests
npm run live                                # live keyless canary
```

# Actor input Schema

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

One entry per company — a domain (stripe.com) or a full URL (https://stripe.com). Each resolves to ONE deduped enriched record. Bare names without a dot are skipped (supply the domain).

## `maxCompanies` (type: `integer`):

Cap how many companies are processed (bounds compute/COGS). 0 = no cap.

## `maxPagesPerCompany` (type: `integer`):

How many of each company's own pages to crawl (homepage + high-signal internal pages like /about, /contact). Higher = richer contacts, more compute.

## `include_hiring` (type: `boolean`):

Derive a live hiring block from 8 ATS platforms (open reqs, departments, locations, remote share, intensity). The differentiator no resold-DB enricher offers.

## `include_contacts` (type: `boolean`):

Extract business emails (with MX deliverability grade) and E.164 phones from the company's own pages.

## `include_tech` (type: `boolean`):

Wappalyzer-style detection of the company's web tech (framework, CMS, CDN, analytics, payments, support widgets).

## `include_dns` (type: `boolean`):

Derive the email provider (from MX), the SaaS tools the company runs (from DNS verification tokens + SPF), and domain age/registrar (from RDAP). Keyless — no proxy, no paid API.

## `verifyEmailsSmtp` (type: `boolean`):

Also run a best-effort SMTP RCPT probe to upgrade deliverability grades (A). Port 25 is often blocked on cloud egress, so this is advisory — the MX grade is the default signal.

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "ramp.com"
  ],
  "maxCompanies": 0,
  "maxPagesPerCompany": 4,
  "include_hiring": true,
  "include_contacts": true,
  "include_tech": true,
  "include_dns": true,
  "verifyEmailsSmtp": false
}
```

# Actor output Schema

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

The normalized rows this run produced, in the default dataset.

## `overview` (type: `string`):

Open the run results in the Apify Console.

# 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",
        "ramp.com",
        "vercel.com"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("greyridge-signals/company-enrichment-scraper").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",
        "ramp.com",
        "vercel.com",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("greyridge-signals/company-enrichment-scraper").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",
    "ramp.com",
    "vercel.com"
  ]
}' |
apify call greyridge-signals/company-enrichment-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,greyridge-signals/company-enrichment-scraper"
        }
    }
}
```

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/mwDu444KfaQ94HdYE/builds/q3GFT7pUwgp4c5QJS/openapi.json
