# Firmfold — Company Enrichment API: Logo, Tech, Contacts, Jobs (`cybermax/company-enrichment`) Actor

Company enrichment API: give domains, work emails or company names; get one flat profile each: official domain, logo + brand colour, tech stack, email security, domain age, contacts, socials, hiring and Wikidata firmographics. $0.01/company; misses free.

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

## Pricing

$10.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.

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

## Firmfold: company enrichment API (domain, email or name to full company profile)

**Company enrichment in one call:** give Firmfold a list of domains, website URLs, work emails or plain company names, and get back **one flat profile per company**: the official domain, logo and brand colour, tech stack, email security (MX, SPF, DMARC), domain age, public contact card (role emails, phones, LinkedIn, X and other socials, address), hiring activity (open jobs, new jobs in the last 30 days, top functions and locations) and firmographics from Wikidata (founded, employees, HQ, industry, ticker, parent company).

It folds five tested CyberMax tools into one row with the same ~60 keys every time, ready for Google Sheets, Clay, HubSpot, Salesforce or an AI agent. A Clearbit Enrichment alternative with no LinkedIn scraping and no sign-up anywhere else.

**$0.01 per company enriched.** Unknown names, dead domains, personal email providers (gmail.com…) and, in monitor mode, unchanged companies are free.

### Why Firmfold

- **One call instead of five.** Buying the same fields from the single tools costs about $0.021 per company; Firmfold is $0.01.
- **Every row has the same shape.** Missing data is `null` or `[]`, never a missing key, so a spreadsheet or CRM mapping never breaks. `modulesFilled` says which parts found data.
- **Change alerts for scheduled CRM refreshes.** Monitor mode returns only companies whose profile changed (new tech, new contacts, a rebrand, a big hiring jump) and says which parts changed. Unchanged companies cost nothing.
- **Names work too.** "Acme Brick Company" becomes `brick.com` first (with a confidence score), then gets the full profile.
- **Honest data.** Firmographics come only from a Wikidata record whose official website is this domain. If there is no such record the fields stay empty; they are never guessed.

### What you get (one record per company)

| Group | Fields |
|---|---|
| Identity | `domain`, `website`, `companyName`, `description`, `language`, `domainSource` (`input` or `name-lookup`), `domainConfidence` |
| Logo (Logolark) | `logoUrl`, `iconUrl`, `logoFormat`, `logoIsIcon`, `brandColor` (every image is downloaded and checked) |
| Tech & domain (DomainDNA) | `techStack[]`, `cms`, `ecommerce`, `analytics[]`, `hosting[]`, `emailProvider`, `spf`, `dmarcPolicy`, `domainCreatedAt`, `domainAgeYears`, `registrar`, `domainExpiresAt`, `tlsExpiresAt`, `securityHeadersScore` |
| Contacts (Hollerdex) | `bestEmail`, `emails[{email,label}]` (sales, support, press, general…), `phone`, `phones[]`, `linkedin`, `x`, `facebook`, `instagram`, `youtube`, `tiktok`, `github`, `contactPageUrl`, `contactFormUrl`, `salesUrl` (scheduler, demo or sign-up link), `contactRoute` (the best public way in: email, contact form, contact page, phone, sales link or LinkedIn company page), `address` |
| Hiring (HireHeat) | `hiringOpenJobs`, `hiringNewLast30Days`, `hiringLevel`, `hiringTopFunctions[]`, `hiringTopLocations[]`, `hiringRemoteJobs`, `jobBoardUrl`, `jobBoardAts` (Greenhouse, Lever, Ashby, SmartRecruiters, Workable, Recruitee) |
| Firmographics (Wikidata, CC0) | `wikidataId`, `founded`, `employees`, `employeesAsOf`, `hqCity`, `hqCountry`, `hqCountryCode`, `industries[]`, `ticker`, `stockExchange`, `parentCompany` |
| Run info | `status`, `modulesFilled[]`, `summary`, `errors[]`, `sources`, `ms`, `enrichedAt`; in monitor mode `change` and `changedModules[]` |

`status` is `enriched` (charged), `domain-not-found` (unknown name or a domain that doesn't resolve; free, with `bestGuessDomain` when there was a weak match) or `invalid` (free).

### Ready-made inputs (first try in one click)

**Three well-known domains, every part on** (about 20 s, 3 × $0.01):

```json
{ "companies": ["stripe.com", "notion.so", "linear.app"] }
```

**Company names to full profiles** (names with "&" work: GW\&K Investment Management → gwkinvest.com):

```json
{ "companies": ["Acme Brick Company", "GW&K Investment Management", "Monzo Bank | gb"] }
```

**Weekly CRM refresh, only changed companies returned** (pair with a Schedule):

```json
{ "companies": ["stripe.com", "g-p.com", "hubspot.com"], "monitor": true, "monitorKey": "crm-accounts" }
```

### Input

```json
{
  "companies": ["stripe.com", "Notion", "hello@linear.app", "Acme Brick Company", "Monzo Bank | gb"],
  "modules": ["logo", "tech", "contacts", "hiring", "firmographics"]
}
```

| Field | Default | What it does |
|---|---|---|
| `companies` | required | One per line: domain, URL, work email or company name. Add a country to a name after a bar (`"Hiscox \| gb"`). Duplicates are skipped. |
| `modules` | all | Pick parts to save time. The domain check, tech stack and email security always run. |
| `country` | none | Default country for names (helps with ambiguous names). |
| `monitor` / `monitorKey` | false / auto | Return only new or changed companies (see below). |
| `includePersonalEmails` | false | Off: only role addresses (sales@, support@…). On: also named people's addresses that the company publishes itself. |
| `minDomainConfidence` | 0.6 | For names: below it the company is returned free as `domain-not-found`. |
| `maxConcurrency` | 5 | Companies in parallel (5 is right for 1 GB memory). |
| `timeoutSecs` | 20 | Time budget per part; a slow part is cut off and noted in `errors`, the rest of the profile still comes back. |
| `maxContactPages` | 4 | Pages read per site for contacts. |
| `proxyConfiguration` | off | Optional Apify Proxy for sites that block data-centre IPs. |

### Output (real record from an Apify run, 24 Sep 2026, trimmed)

```json
{
  "input": "figma.com",
  "status": "enriched",
  "domain": "figma.com",
  "companyName": "Figma",
  "logoUrl": "https://static.figma.com/app/icon/2/icon-512.png",
  "brandColor": "#000000",
  "techStack": ["Sanity", "Next.js", "Amazon CloudFront", "Netlify", "Font Awesome", "Vimeo embed"],
  "cms": "Sanity",
  "emailProvider": "Google Workspace",
  "dmarcPolicy": "quarantine",
  "domainAgeYears": 27.4,
  "bestEmail": "press@figma.com",
  "emails": [{ "email": "press@figma.com", "label": "press" }, { "email": "legal@figma.com", "label": "legal" }],
  "linkedin": "https://www.linkedin.com/company/figma",
  "x": "https://x.com/figma",
  "hiringOpenJobs": 159,
  "hiringNewLast30Days": 38,
  "hiringLevel": "very strong",
  "hiringTopFunctions": ["sales", "engineering", "design", "data", "product"],
  "jobBoardUrl": "https://boards.greenhouse.io/figma",
  "wikidataId": "Q96186334",
  "founded": "2012",
  "hqCity": "San Francisco",
  "hqCountry": "United States",
  "modulesFilled": ["logo", "tech", "contacts", "hiring", "firmographics"],
  "summary": "Figma (figma.com): founded 2012, 324 employees, HQ San Francisco, United States; site on Sanity; email on Google Workspace; 159 open jobs (38 new in 30 days); contact press@figma.com; 6 social profiles."
}
```

The full records of that run are available on request; the table and example above are copied from it unchanged.

### Tested on real companies

Real Apify run on 40 mixed inputs (domains, work emails, names, a gmail address and a made-up domain), 24 Sep 2026: **38 of 38 real companies enriched**, the gmail address and the made-up domain returned free, **93 s for the whole run** at 1 GB, about $0.0001 of compute per company. Parts found: tech 37/38, firmographics 33/38, logo 32/38, contacts 30/38, hiring 19/38 (many companies simply have no public job board on the six systems checked). Names resolved correctly: Acme Brick Company → brick.com, Monzo Bank → monzo.com, Hiscox → hiscoxgroup.com, Ben & Jerry's → benjerry.com.

**Same 10 domains through two other enrichment Actors** (details in `examples/benchmark-2026-09-24.json`):

| | Firmfold | foxlabs/company-enrichment | mambalabs/company-firmographic-enricher |
|---|---|---|---|
| Tech stack, MX, SPF/DMARC, domain age | 10/10 | none | none |
| Role emails, phones, socials | 9/10 | none (address only) | phone on 3/10 |
| Hiring activity | yes (3 of these 10 have public boards) | none | none |
| Founded / employees / HQ | 9/10 (Wikidata) | 10/10 (Owler) | founded 2/10, employees 1/10 |
| Revenue range, CEO, competitors | **not provided** | yes | revenue estimate |
| Change alerts | yes | no | no |

If you need revenue estimates or CEO names, use an Owler-based tool; if you need what a company runs, how to reach it and whether it's growing, use Firmfold.

### Pricing

**$0.01 per company enriched** (pay per event, no subscription, no start fee). Free: unknown names (`domain-not-found`), dead domains, personal email providers (gmail.com…), invalid input, and in monitor mode every unchanged company. Compute is small (about $0.0001 per company at 1 GB) and runs on your Apify plan.

### When NOT to use Firmfold

- You need **people** (named decision makers, personal emails, phone numbers of employees). Firmfold only returns what a company publishes about itself.
- You need **revenue, funding or valuation**: not in this data.
- You only need **one** field group for many companies: the single tools are cheaper per call (see below).
- Sites behind strong bot walls (some large retailers) may return tech and firmographics but no contacts; that part is listed in `errors` and you're still charged only once for the rest.

### Run it on a schedule (CRM refresh)

1. Save your company list as a task with `"monitor": true` and a `monitorKey` such as `"crm-accounts"`.
2. Schedule it daily or weekly in Apify (Schedules → Create).
3. Each run returns only companies that are new or changed, with `changedModules` (e.g. `["tech"]` when a company moves from Mailchimp to HubSpot, `["contacts"]` for a new sales email, `["hiring"]` for a 20%+ jump in open roles, `["logo"]` for a rebrand). Unchanged companies are not returned and not charged. A part that failed on this run keeps its old value, so a flaky site never triggers a false alert.

### Integrations: code, no-code and AI agents

**Python**

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("cybermax/company-enrichment").call(run_input={"companies": ["stripe.com", "Notion"]})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(row["companyName"], row["cms"], row["hiringOpenJobs"], row["employees"])
```

**JavaScript**

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('cybermax/company-enrichment').call({ companies: ['stripe.com', 'Notion'] });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**HTTP (one call, results back):** `POST https://api.apify.com/v2/acts/cybermax~company-enrichment/run-sync-get-dataset-items?token=<APIFY_TOKEN>` with body `{"companies": ["stripe.com"]}`.

**Google Sheets / Clay / Make / Zapier / n8n:** use the Apify app or an HTTP step with the call above; every row has the same columns, so map them once.

**AI agents (MCP):** Firmfold is a tool on Apify's MCP server: `https://mcp.apify.com/?tools=cybermax/company-enrichment`. Tool description for agents: *"Enrich companies: domain, work email or company name in; logo, tech stack, email security, domain age, contacts, socials, hiring and firmographics out, one flat record each."*

### FAQ

**The company has no contact page or email. What do I get?** `contactRoute` always names the best public way in: email, contact form, contact page, phone, a sales/booking link (`salesUrl`, e.g. a Calendly or "Get started" page) or the LinkedIn company page.

**Why is `hiringOpenJobs` 0 for a company I know is hiring?** Firmfold checks Greenhouse, Lever, Ashby, SmartRecruiters, Workable and Recruitee, by the domain and by the company's name (so g-p.com finds the `globalizationpartners` board). Companies on Workday or their own careers system aren't covered.

**Is a company name enough?** Usually. Add a country after a bar for ambiguous names (`"Hiscox | gb"`), or pass the domain when you have it; below `minDomainConfidence` the row comes back free as `domain-not-found` with `bestGuessDomain`.

**Do you scrape LinkedIn or people data?** No. Only what the company publishes about itself, DNS/RDAP, public job boards and Wikidata.

### Also from CyberMax

The parts, if you need only one field group (cheaper per call):

- [Namewhere](https://apify.com/cybermax/company-to-domain): company name to official domain.
- [Logolark](https://huggingface.co/spaces/CyberMax-tools/saas-logos-brand-colors?soon=logo-finder): company logos, icons and brand colours.
- [DomainDNA](https://apify.com/cybermax/domain-intel): tech stack, email security and domain age.
- [Hollerdex](https://apify.com/cybermax/website-contacts): emails, phones and social profiles.
- [HireHeat](https://apify.com/cybermax/hiring-signals): hiring signals by domain, with the full job list.

All tools: [Boardroom Buys](https://apify.com/cybermax/sec-insider-tracker) · [Swellmeter](https://apify.com/cybermax/google-trends) · [SnapScout](https://apify.com/cybermax/website-screenshot) · [Gripeline](https://apify.com/cybermax/app-reviews) · [Keyfern](https://apify.com/cybermax/keyword-ideas) · [HireHeat Jobs](https://apify.com/cybermax/career-site-jobs) · [Linkheft](https://apify.com/cybermax/domain-authority)

### Data and privacy

Public company information only: the company's own website, DNS and RDAP records, public job boards and Wikidata (CC0). No LinkedIn scraping, no people lookups. Personal email addresses are off by default.

### Changelog

- 2026-09-25: Names with "&" resolve (GW\&K Investment Management → gwkinvest.com, AT\&T → att.com); job boards named after the company are found (g-p.com → Greenhouse `globalizationpartners`, 48 open jobs); new `salesUrl` and `contactRoute` fields so every company has a way in.
- 2026-09-24: First release.

© 2026 CyberMax.

# Actor input Schema

## `companies` (type: `array`):

One company per line: a domain (stripe.com), a website URL, a work email (jane@acme.com) or a company name ("Acme Brick Company", "Monzo Bank | gb" with an optional country). Names are turned into the official domain first (Namewhere). Personal email providers (gmail.com…) are skipped free.

## `modules` (type: `array`):

Leave empty for everything. The domain check, tech stack, email security and domain age always run (they decide whether the company exists).

## `country` (type: `string`):

Optional, e.g. "us", "gb", "de". Helps turn ambiguous company names into the right domain.

## `monitor` (type: `boolean`):

For scheduled CRM refreshes: return (and charge for) only companies that are new or whose profile changed since the last run with the same list, with changedModules saying what changed (new tech, new contacts, rebrand, hiring jump…). Unchanged companies are free.

## `monitorKey` (type: `string`):

Optional. Name your watchlist to keep its state when you edit the list.

## `includePersonalEmails` (type: `boolean`):

Off by default: only role addresses (sales@, support@, press@…). Turn on to also return personal addresses the company itself publishes on its site.

## `minDomainConfidence` (type: `number`):

Only used for company names. Below it the name is returned free as domain-not-found with its best guess.

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

5 is safe at 1 GB memory; raise memory with it.

## `timeoutSecs` (type: `integer`):

A slow part is cut off and reported in errors; the rest of the profile is still returned.

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

Homepage plus likely contact/about/imprint pages read for emails, phones and address.

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

Optional Apify Proxy for the contact pages of sites that block data-centre IPs. Off by default.

## Actor input object example

```json
{
  "companies": [
    "stripe.com",
    "Notion",
    "hello@linear.app"
  ],
  "modules": [
    "logo",
    "tech",
    "contacts",
    "hiring",
    "firmographics"
  ],
  "monitor": false,
  "includePersonalEmails": false,
  "minDomainConfidence": 0.6,
  "maxConcurrency": 5,
  "timeoutSecs": 20,
  "maxContactPages": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# 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 = {
    "companies": [
        "stripe.com",
        "Notion",
        "hello@linear.app"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("cybermax/company-enrichment").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 = { "companies": [
        "stripe.com",
        "Notion",
        "hello@linear.app",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("cybermax/company-enrichment").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 '{
  "companies": [
    "stripe.com",
    "Notion",
    "hello@linear.app"
  ]
}' |
apify call cybermax/company-enrichment --silent --output-dataset

```

## MCP server setup

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

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/Us6pspacZGeoHm2qr/builds/skcGWEzgzd9nyUSYO/openapi.json
