# Company Data Scraper | Emails, Phones and Tech Stack (`silentflow/company-data-scraper`) Actor

Company Data Scraper turns any domain list into structured company records: legal name, postal address, emails, phones, LinkedIn and 14 other social links, registration numbers, tech stack, and domain infrastructure. Built for sales prospecting, lead enrichment, KYC checks, and B2B research.

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

## Pricing

from $2.04 / 1,000 companies

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 Data Scraper

**Turn a list of domains into complete company records: identity, contacts, postal address, legal registration, technology stack and domain infrastructure, one row per company.** 30 domains resolved in 7 minutes 29 seconds across France, Germany, the UK, the US, Spain and Italy.

### How it works

![How it works](https://api.apify.com/v2/key-value-stores/YXm81xySHg6uRkewS/records/company-data-scraper-how-it-works-v1.png)

1. **You submit a list of domains or website URLs.** A bare domain and a full URL both work, the domain is extracted automatically, and duplicates in the list are combined into a single row.
2. **Each domain is read for the pages that actually carry company data.** The homepage, plus the handful of pages a real visitor would open: about, contact, legal notice, pricing. The structured data a company already publishes about itself is parsed straight from those pages, rather than the scraper guessing what to look for.
3. **One consolidated row comes back per domain.** Company identity, contacts, postal address, legal registration, 15 social profiles, a detected technology stack and domain infrastructure, all in the same record, with a status field explaining what happened on the domains that could not be fully resolved.

### ✨ Why teams choose this over other company data scrapers

Tired of a contact scraper that hands you an email address and calls it a day? Sick of stitching four separate tools together just to confirm a company is even real? Done paying twice for the same domain because the output arrives one row per page and you dedupe it yourself?

- 🏢 **Get the company, not just its inbox.** Name, legal name, description, logo, founding year, industry code and headcount band all come back in the same row as the contacts, not behind a separate lookup.
- 📮 **A postal address, not a city guess.** Street, postal code, region, country and country code, pulled from the pages that actually carry them.
- 🧾 **A legal registration number you can trust.** Checksum-validated offline and checked against public registries where they exist, with the exact page it was found on attached. If a number does not validate, the row says so instead of asserting it.
- 💻 **A technology stack in the same row as the contacts.** Checked against 6,633 known technologies and returned with a confidence score for each match.
- 🌐 **Domain infrastructure done properly.** Normalized registration dates on every TLD, a derived email and DNS provider instead of raw records to parse yourself, an email security read, and the sibling domains a certificate reveals.
- 🔗 **One row per company, joinable on what you sent.** The exact string you submitted comes back untouched as `inputUrl`, so the output snaps straight onto your source list without a join key you have to invent.
- ✅ **Every input gets a row.** A domain that is dead, parked, or refuses to answer still produces a row, with a status explaining which, so your list stays complete and an empty cell is never ambiguous.
- 🔍 **Every fact carries where it came from.** Every contact and every identifier includes the URL it was found on, so anything can be checked by hand in one click.
- 🔓 **No account, no API key, nothing to install.** Point it at a domain list and run it.

### 🎯 What you can do with company data

| Team | What they build |
|------|-----------------|
| **Sales ops** | Turn an exported prospect list into CRM-ready rows with legal name, postal address and headcount band, no manual dedup across pages |
| **RevOps / data teams** | Segment a market by technology, email provider, company age or headcount band before a campaign goes out |
| **Compliance / procurement** | Confirm a supplier or partner is a registered, active legal entity, with the registry source attached for the file |
| **Automation builders** | Wire a fixed 31-key schema into n8n, Make or Zapier once, and stop re-mapping the workflow every time a run comes back different |
| **Growth teams** | Enrich inbound signups by the domain they signed up with, and route the lead by headcount or industry code |
| **Market researchers** | Build a territory list from a conference exhibitor roster, ranked by employee range and industry |
| **Agencies** | Turn a client's competitor list into a side-by-side sheet: technology stack, social presence, and domain age, in one table |

### 📥 Input parameters

| Parameter | Type | Default | Description |
|-----------|------|---------|--------------|
| `domains` | array of strings, required | - | Domains or website URLs to look up, for example `stripe.com` or `https://www.qonto.com/pricing`. The domain is extracted automatically. |
| `maxItems` | integer | 100 | Maximum number of company rows this run returns (1 to 10,000). |
| `debugMode` | boolean | false | Turns on detailed logs, useful when investigating a specific run. |

### 📊 Output data

#### A resolved domain

```json
{
    "inputUrl": "brightloop.io",
    "domain": "brightloop.io",
    "finalUrl": "https://www.brightloop.io/",
    "httpStatus": 200,
    "status": "ok",
    "isLive": true,
    "redirectChain": ["https://brightloop.io/", "https://www.brightloop.io/"],
    "language": "en",
    "company": {
        "name": "Brightloop",
        "legalName": "Brightloop SAS",
        "description": "Brightloop builds expense management software for finance teams.",
        "tagline": "Spend management, simplified",
        "logo": "https://www.brightloop.io/logo.png",
        "foundedYear": 2019,
        "industry": "Financial Software",
        "employeeRange": "51-200",
        "sourceUrl": "https://www.brightloop.io/about"
    },
    "contacts": {
        "emails": [
            {"address": "contact@brightloop.io", "isRole": true, "mxValid": true, "sourceUrl": "https://www.brightloop.io/contact"}
        ],
        "phones": [
            {"raw": "+33 1 84 88 32 10", "e164": "+33184883210", "country": "FR", "sourceUrl": "https://www.brightloop.io/contact"}
        ],
        "contactPageUrl": "https://www.brightloop.io/contact"
    },
    "address": {
        "raw": "14 Rue de la Paix, 75002 Paris, France",
        "street": "14 Rue de la Paix",
        "postalCode": "75002",
        "city": "Paris",
        "region": "Ile-de-France",
        "country": "France",
        "countryCode": "FR",
        "confidence": "high",
        "sourceUrl": "https://www.brightloop.io/legal-notice"
    },
    "registration": {
        "ids": [
            {"type": "SIREN", "value": "812345678", "checkable": true, "checksumValid": true, "verified": true, "sourceUrl": "https://www.brightloop.io/legal-notice"}
        ],
        "legalName": "Brightloop SAS",
        "status": "active",
        "incorporationDate": "2019-03-12",
        "naceCode": "6202A",
        "officers": [{"name": "Claire Dubois", "role": "President"}],
        "identityConfidence": "high",
        "attributionUncertain": false
    },
    "social": {
        "linkedin": "https://www.linkedin.com/company/brightloop",
        "twitter": "https://twitter.com/brightloophq",
        "facebook": "",
        "instagram": "https://www.instagram.com/brightloophq/",
        "youtube": "",
        "tiktok": "",
        "github": "https://github.com/brightloop",
        "pinterest": "",
        "threads": "",
        "telegram": "",
        "whatsapp": "",
        "discord": "",
        "reddit": "",
        "snapchat": "",
        "medium": "https://medium.com/brightloop"
    },
    "tech": {
        "count": 14,
        "technologies": [
            {"name": "React", "categories": ["JavaScript Framework"], "version": "18.2", "confidence": 100},
            {"name": "Segment", "categories": ["Analytics"], "version": "", "confidence": 95}
        ],
        "categoriesSummary": {"JavaScript Framework": 1, "Analytics": 2, "CDN": 1},
        "cms": "Webflow",
        "ecommerce": "",
        "cdn": "Fastly",
        "framework": "React",
        "analytics": ["Google Analytics", "Segment"]
    },
    "domainInfo": {
        "registrar": "OVH SAS",
        "createdAt": "2019-02-08T00:00:00Z",
        "expiresAt": "2027-02-08T00:00:00Z",
        "ageDays": 2657,
        "nameservers": ["ns1.ovh.net", "ns2.ovh.net"],
        "ips": ["51.68.24.10"],
        "mx": ["aspmx.l.google.com"],
        "emailProvider": "Google Workspace",
        "dnsProvider": "OVH",
        "spf": "v=spf1 include:_spf.google.com ~all",
        "dmarcPolicy": "quarantine",
        "dkimPresent": true,
        "relatedDomains": ["app.brightloop.io", "brightloop.fr"]
    },
    "meta": {
        "pagesFetched": 5,
        "fetchProfile": "direct",
        "warnings": []
    },
    "companyName": "Brightloop",
    "email": "contact@brightloop.io",
    "phone": "+33184883210",
    "city": "Paris",
    "country": "France",
    "registrationId": "812345678",
    "linkedin": "https://www.linkedin.com/company/brightloop",
    "employeeRange": "51-200",
    "industry": "Financial Software",
    "techCount": 14,
    "cms": "Webflow",
    "emailProvider": "Google Workspace",
    "domainAge": 2657,
    "identityConfidence": "high",
    "scrapedAt": "2026-08-17T09:14:22Z"
}
```

This example trims the technologies list for readability, and leaves out the certificate issuer and validity fields that also live inside `domainInfo`. See Data fields below for the full, exact count.

#### A domain that could not be reached

```json
{
    "inputUrl": "a-domain-that-recently-shut-down.com",
    "domain": "a-domain-that-recently-shut-down.com",
    "finalUrl": "",
    "httpStatus": 0,
    "status": "unreachable",
    "isLive": false,
    "redirectChain": [],
    "language": "",
    "company": {"name": "", "legalName": "", "description": "", "tagline": "", "logo": "", "foundedYear": 0, "industry": "", "employeeRange": "", "sourceUrl": ""},
    "contacts": {"emails": [], "phones": [], "contactPageUrl": ""},
    "address": {"raw": "", "street": "", "postalCode": "", "city": "", "region": "", "country": "", "countryCode": "", "confidence": "none", "sourceUrl": ""},
    "registration": {"ids": [], "legalName": "", "status": "", "incorporationDate": "", "naceCode": "", "officers": [], "identityConfidence": "none", "attributionUncertain": false},
    "social": {"linkedin": "", "twitter": "", "facebook": "", "instagram": "", "youtube": "", "tiktok": "", "github": "", "pinterest": "", "threads": "", "telegram": "", "whatsapp": "", "discord": "", "reddit": "", "snapchat": "", "medium": ""},
    "tech": {"count": 0, "technologies": [], "categoriesSummary": {}, "cms": "", "ecommerce": "", "cdn": "", "framework": "", "analytics": []},
    "domainInfo": {"registrar": "", "createdAt": "", "expiresAt": "", "ageDays": 0, "nameservers": [], "ips": [], "mx": [], "emailProvider": "", "dnsProvider": "", "spf": "", "dmarcPolicy": "", "dkimPresent": false, "relatedDomains": []},
    "meta": {"pagesFetched": 0, "fetchProfile": "", "warnings": ["dns lookup failed"]},
    "companyName": "",
    "email": "",
    "phone": "",
    "city": "",
    "country": "",
    "registrationId": "",
    "linkedin": "",
    "employeeRange": "",
    "industry": "",
    "techCount": 0,
    "cms": "",
    "emailProvider": "",
    "domainAge": 0,
    "identityConfidence": "none",
    "scrapedAt": "2026-08-17T09:14:30Z"
}
```

Every key is still present. Nothing is missing from the shape, only from the values, and `status` says exactly why.

### 🗂️ Data fields

Every row carries the same 31 top-level keys, whether the domain resolved or not. Fourteen of them are flat columns built for the table view and CSV export: `companyName, email, phone, city, country, registrationId, linkedin, employeeRange, industry, techCount, cms, emailProvider, domainAge, identityConfidence`. The remaining top-level keys are row metadata (`inputUrl, domain, finalUrl, httpStatus, status, isLive, redirectChain, language, scrapedAt`) and eight nested blocks:

| Block | Fields | What is inside |
|---|---|---|
| `company` | 9 | name, legal name, description, tagline, logo, founding year, industry code, headcount band, source URL |
| `contacts` | 3 | emails\[] (4 fields each: address, role flag, mail-server validity, source URL), phones\[] (4 fields each: raw text, E.164 format, country, source URL), contact page URL |
| `address` | 9 | raw text, street, postal code, city, region, country, country code, confidence, source URL |
| `registration` | 8 | ids\[] (6 fields each: type, value, whether the type can be checked at all, checksum validity, registry confirmation, source URL), legal name, status, incorporation date, industry classification code, officers\[] (2 fields each: name, role), identity confidence, an attribution-uncertain flag |
| `social` | 15 | one profile URL field per platform: LinkedIn, X/Twitter, Facebook, Instagram, YouTube, TikTok, GitHub, Pinterest, Threads, Telegram, WhatsApp, Discord, Reddit, Snapchat, Medium |
| `tech` | 8 | technology count, technologies\[] (4 fields each: name, categories, version, confidence score), a category summary, detected CMS, ecommerce platform, CDN, framework, analytics tools |
| `domainInfo` | 16 | registrar, created date, expiry date, age in days, nameservers, IP addresses, MX records, derived email provider, derived DNS provider, SPF record, DMARC policy, DKIM presence, certificate issuer, certificate issued date, certificate expiry date, and the sibling domains found in the certificate |
| `meta` | 3 | pages read, an internal note on how the domain answered, and any warnings raised during the run |

`status` is one of `ok`, `partial`, `blocked`, `unreachable`, or `parked`. `blocked` means the server answered and declined to serve the page; `unreachable` means the domain could not be read at all, whether nothing answered or the answer was a server error. `identityConfidence` is one of `high`, `medium`, `low`, or `none`, and it is the field to filter on: a `high`-confidence company can still have an empty `registrationId` if the site never published one. The technology engine behind `tech` carries 6,633 technologies.

`inputUrl` is preserved exactly as submitted, so it is the join key back to your source list even when `domain` has been normalized. `registrationId` in the flat column carries an identifier when it passed a checksum, or when its type has no checksum to pass and exactly one candidate was found on the site. Everything else stays in `registration.ids` with the page it came from: a number that failed its checksum, one of several same-type candidates where the row cannot say which is the company's, and any number a registry attributes to a different company, which also sets `registration.attributionUncertain`. So a flat cell you can trust never shares space with one you cannot. Timestamps (`scrapedAt`, `domainInfo.createdAt`, `domainInfo.expiresAt`) are RFC 3339.

### 🚀 Examples

#### Look up a single company

```json
{
    "domains": ["qonto.com"]
}
```

#### Enrich a list of fintech competitors

```json
{
    "domains": ["qonto.com", "monzo.com", "wise.com", "revolut.com", "n26.com"],
    "maxItems": 100
}
```

#### Cap a large exported list to a first batch

```json
{
    "domains": ["acme-corp.com", "beta-labs.io", "cargo-supply.eu", "delta-parts.de", "example-retail.co.uk"],
    "maxItems": 3
}
```

#### Mix bare domains and full URLs from a bookmarks export

```json
{
    "domains": ["airtable.com", "https://www.qonto.com/pricing", "https://monzo.com/blog/2024/product-update"]
}
```

#### Investigate a run with unexpected statuses

```json
{
    "domains": ["a-domain-that-recently-shut-down.com"],
    "debugMode": true
}
```

### 🤖 Copy to your AI assistant

Paste this block into Claude, ChatGPT, or Cursor to give it full context about this scraper:

```
You have access to the Company Data Scraper on Apify: silentflow/company-data-scraper

Input schema:
- domains (array of string, required): domains or website URLs to look up
- maxItems (integer, default 100, min 1, max 10000): maximum number of company rows returned
- debugMode (boolean, default false): detailed logs for troubleshooting a run

Output: one row per input domain, 31 top-level keys always present.
Flat columns (14, for table/CSV): companyName, email, phone, city, country, registrationId,
linkedin, employeeRange, industry, techCount, cms, emailProvider, domainAge, identityConfidence
Row metadata: inputUrl, domain, finalUrl, httpStatus, status (ok/partial/blocked/unreachable/parked),
isLive, redirectChain, language, scrapedAt
Nested blocks: company (10 fields), contacts (3 fields, emails[] and phones[] with 4 fields each),
address (9 fields), registration (8 fields, ids[] with 5 fields each and officers[] with 2 fields each),
social (15 platform URL fields), tech (8 fields, technologies[] with 4 fields each, checked against
6,633 technologies), domainInfo (16 fields), meta (3 fields)

Use apify-client for Python or JS to run it and read the resulting dataset.
```

### 💻 Integrations

#### Build a segmented lead list (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")

run = client.actor("silentflow/company-data-scraper").call(run_input={
    "domains": ["qonto.com", "monzo.com", "wise.com", "revolut.com"],
    "maxItems": 500,
})

for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item["status"] not in ("ok", "partial"):
        continue
    if item["employeeRange"] in ("201-500", "501-1000", "1001-5000"):
        print(f"{item['companyName']} ({item['country']}): {item['email']} - {item['linkedin']}")
```

#### Flag suppliers with no validated registration number (JavaScript)

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });

const run = await client.actor('silentflow/company-data-scraper').call({
    domains: ['acme-supplier.com', 'another-supplier.eu', 'third-vendor.de'],
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
const unverified = items.filter(
    (i) => i.registration.ids.length === 0 || i.registration.attributionUncertain
);
unverified.forEach((i) => console.log(`Check manually: ${i.domain} (${i.status})`));
```

#### Export a spreadsheet-ready CSV (Python)

```python
import csv
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("silentflow/company-data-scraper").call(run_input={
    "domains": ["qonto.com", "monzo.com", "wise.com", "n26.com"],
})

with open("companies.csv", "w", newline="") as f:
    writer = csv.writer(f)
    writer.writerow(["domain", "companyName", "email", "phone", "city", "country", "registrationId", "linkedin", "status"])
    for item in client.dataset(run["defaultDatasetId"]).iterate_items():
        writer.writerow([
            item["domain"], item["companyName"], item["email"], item["phone"],
            item["city"], item["country"], item["registrationId"], item["linkedin"], item["status"],
        ])
```

### 📈 Performance

Measured on the actor's own end-to-end run, 30 domains across France, Germany, the UK, the US, Spain and Italy:

| Metric | Value |
|--------|-------|
| **Domains resolved** | 93% |
| **Company name returned** | 100% of resolved domains |
| **Technology stack detected** | 100% of resolved domains |
| **Run time** | 30 domains in 7 minutes 29 seconds |
| **Pages read per domain** | About 5 |

### 💾 Data export

Results are available in:

- **JSON** (full nested structure, best for pipelines and AI ingestion)
- **CSV** (the 14 flat columns plus row metadata, nested arrays flattened)
- **Excel** (.xlsx, native formatting)
- **JSONL** (for streaming consumers)

Download from the Apify console, or pull programmatically from the dataset API:

```
https://api.apify.com/v2/datasets/{DATASET_ID}/items?format=csv&token=YOUR_TOKEN
```

### 💡 Tips for best results

1. **Submit bare domains when you have them.** `qonto.com` resolves the same as `https://www.qonto.com/pricing`, a bare domain just skips a parsing step.
2. **Start with a small `maxItems`** to confirm the fields you actually need are populated before scaling a run up to your full list.
3. **Filter on `identityConfidence`, not on whether `registrationId` is filled in.** A high-confidence company can still have no registration number if the site never published one; an empty `registrationId` is not by itself a failure.
4. **Group your list by expected country before a large run.** Legal registration and postal address coverage is strongest in the EU and the UK, where a legal notice page is standard practice, and thinner in the US. Segmenting sets the right expectation before you look at the results.
5. **Re-run domains that come back `blocked` or `unreachable` on their own.** A site that failed once can answer on a second pass if it was mid-deploy or briefly down when this run read it.
6. **Turn on `debugMode` only when investigating one run.** Leave it off day to day, it exists for troubleshooting a specific batch.

### ❓ FAQ

**What does the scraper return for each domain?**
One consolidated company record: identity (name, legal name, description, logo, founding year, industry, headcount band), contacts with the page each one was found on, a postal address, legal registration data, 15 social profile links, a detected technology stack, and domain infrastructure. Every row carries the same 31 keys, whether the domain resolved or not.

**What can I put in `domains`?**
Bare domains (`qonto.com`) or full website URLs (`https://www.qonto.com/pricing`). The domain is extracted automatically, so mixing both formats in the same list works. Duplicate domains are combined into a single row.

**How fresh is the data?**
Live. Each run reads the target pages at the moment it runs. There is no cached or pre-scraped data behind the results.

**Do I need an account, an API key, or to log into the target sites?**
No. Point it at a domain list and run it. Only publicly available pages are read.

**What happens when a domain does not resolve, is dead, or is parked?**
You still get a row. The `status` field says what happened (`ok`, `partial`, `blocked`, `unreachable`, or `parked`), so an empty cell never means "we don't know", it means exactly the thing the status says. Your list stays complete even when some domains fail.

**How do I know a registration number is really that company's?**
Every identifier whose register publishes a check digit is checked against it, and the flat `registrationId` column carries that number once it passes. Some registers publish no check digit at all, so nothing can be verified locally; there the flat column carries the number only when the site named exactly one of that kind, and each identifier says which case it falls under. If a number fails its check, if a registry attributes it to a different company, or if the site named several and the row cannot choose, the row says so through `identityConfidence` and `registration.attributionUncertain` instead of asserting an answer. The raw finding stays available in `registration.ids`, with the page it came from.

**Does coverage vary by country?**
Yes. Legal registration data is richest in the EU and the UK, where publishing a registration number on a legal notice page is standard practice. Coverage is thinner in the US, where no equivalent requirement exists. Company identity, contacts, technology and domain infrastructure are read the same way everywhere.

**Can I submit a large list in one run?**
Yes. `maxItems` controls how many rows the run returns, raise it as your list grows.

**How many technologies can it detect?**
The technology engine checks each site against 6,633 known technologies and returns every match with a confidence score, alongside a summary by category.

**What is `identityConfidence` and how should I use it?**
It is the scraper's own read on how sure it is that the legal entity it found is this company: `high`, `medium`, `low`, or `none`. Filter on it instead of on whether `registrationId` is filled in.

**Is this legal? Where does the data come from?**
The data comes from pages the companies themselves publish: homepages, about pages, contact pages, and legal notice pages. See the Legal section below.

### ⚖️ Legal

This Actor extracts publicly available data from company websites and the legal notice pages they publish. It does not bypass any login, paywall, or CAPTCHA. Users are responsible for complying with the terms of service of the sites in their input list and with applicable data protection laws, including GDPR, CCPA, and PIPL where relevant. The output can contain personal data, such as named officers, work email addresses, and phone numbers: handle it accordingly. Registration numbers and registry attributions are informational; verify with the relevant national registry before relying on them for a compliance or KYC decision.

### 🔗 Related scrapers

Need just one slice of this data instead of the full company record:

- **Technology only:** [Website Tech Stack Scraper](https://apify.com/silentflow/website-tech-stack-scraper) runs the same 6,633-technology engine on its own, without the company, contact, or registration data.
- **Domain infrastructure only:** [Domain Enricher](https://apify.com/silentflow/domain-enricher) returns registration dates, DNS and email provider, and certificate data for a domain list, without the company record.
- **More scrapers from the same publisher:** [silentflow on Apify](https://apify.com/silentflow)

### 📬 Support

Need something this scraper doesn't do yet? We ship features fast.

- Feature requests go straight to our backlog.
- Enterprise needs? We do custom integrations and high-volume plans.

Response time: usually under 24 hours.

Check out our other scrapers: [silentflow on Apify](https://apify.com/silentflow)

# Actor input Schema

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

<b>Domains or website URLs to look up.</b> Paste a bare domain like <code>stripe.com</code> or a full URL like <code>https://www.qonto.com/pricing</code>, the domain is extracted automatically.<br><br><ul><li>One row comes back per domain, always, including domains that could not be resolved.</li><li>Duplicate domains in the list are combined into a single row.</li></ul><br><br>A realistic list of 30 mixed-country domains resolves in about 7 minutes, with roughly 9 in 10 domains returning a usable company record.

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

<b>Caps how many company rows this run returns.</b> The list is processed in the order submitted; once this many rows are delivered, the run stops.<br><br>Set this to the size of the list you actually have. A smaller run finishes sooner and is easier to spot check before you scale up.

## `debugMode` (type: `boolean`):

<b>Turns on detailed logs for this run.</b><br><br><ul><li>Leave this off for normal runs.</li><li>Turn it on when investigating a specific run, for example a domain that keeps coming back with an unexpected status.</li></ul>

## Actor input object example

```json
{
  "domains": [
    "stripe.com",
    "monzo.com",
    "wise.com",
    "n26.com",
    "shopify.com",
    "notion.so",
    "doctolib.fr",
    "alan.com",
    "swile.co",
    "payfit.com"
  ],
  "maxItems": 100,
  "debugMode": false
}
```

# Actor output Schema

## `companies` (type: `string`):

Complete data including: companyName, email, phone, city, country, registrationId, linkedin, employeeRange, industry, techCount, cms, emailProvider, domainAge, identityConfidence

# 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",
        "monzo.com",
        "wise.com",
        "n26.com",
        "shopify.com",
        "notion.so",
        "doctolib.fr",
        "alan.com",
        "swile.co",
        "payfit.com"
    ],
    "maxItems": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("silentflow/company-data-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",
        "monzo.com",
        "wise.com",
        "n26.com",
        "shopify.com",
        "notion.so",
        "doctolib.fr",
        "alan.com",
        "swile.co",
        "payfit.com",
    ],
    "maxItems": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("silentflow/company-data-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",
    "monzo.com",
    "wise.com",
    "n26.com",
    "shopify.com",
    "notion.so",
    "doctolib.fr",
    "alan.com",
    "swile.co",
    "payfit.com"
  ],
  "maxItems": 100
}' |
apify call silentflow/company-data-scraper --silent --output-dataset

```

## MCP server setup

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