Company Data Enrichment — Firmographics, Revenue & HQ avatar

Company Data Enrichment — Firmographics, Revenue & HQ

Pricing

from $1.00 / 1,000 company records

Go to Apify Store
Company Data Enrichment — Firmographics, Revenue & HQ

Company Data Enrichment — Firmographics, Revenue & HQ

Enrich a list of company names or domains with clean firmographics: website, industry, HQ, employee count, revenue range, founded year, ownership & description as JSON. A low-cost ZoomInfo / Clearbit alternative for CRM enrichment, lead lists & sales intelligence.

Pricing

from $1.00 / 1,000 company records

Rating

5.0

(1)

Developer

Berkan Kaplan

Berkan Kaplan

Maintained by Community

Actor stats

3

Bookmarked

282

Total users

53

Monthly active users

6 days ago

Last modified

Share

Company Data Enrichment — Firmographics, Revenue & HQ ✨

🎉 Turn a list of company names, domains or Owler URLs into clean firmographics — website, industry, HQ address, founded year, employees, revenue band, ownership, CEO and description as one row per company. Built for CRM enrichment, lead scoring and account research.

🔍 What is Company Data Enrichment — and when should you use it?

Give this Actor company names, domains or Owler company URLs and it returns one firmographic record per company from the public company profiles on Owler. Names and domains are matched to a profile through Owler's own search suggestions; URLs are used as given. Every run reads the pages live.

Use it when you need: firmographics to fill gaps in a CRM or lead list (industry, size, revenue band, HQ, ownership); a short company profile before a sales call; the name, title and LinkedIn of the executive Owler lists as CEO.

Use something else when: you need registry-grade legal records (use a national company-registry Actor), email addresses or phone numbers (not in this output), or competitors, funding rounds and acquisitions — Owler Scraper returns those from the same source.

🤖 Use with AI agents

Already on the Apify MCP server? Ask for this Actor by name: foxlabs/company-enrichment.

Your agent can pay for its own runs. This Actor is pay-per-event with agentic payments, so an agent can discover it, run it and settle the bill over x402 (USDC on Base) or Skyfire — no Apify account or API token of its own. Billing is the same either way: per delivered company record — rows that only report a failed lookup are free from 25 September 2026.

Otherwise paste this into Claude, ChatGPT, Cursor or any MCP-enabled assistant:

I want company firmographics from the Apify Actor `foxlabs/company-enrichment`.
Input: `companyNames` is a list of company names and `companyDomains` a list of domains (both are matched through Owler's search); `companyUrls` takes exact Owler company URLs. `maxResults` caps how many companies are looked up; a profile Owler doesn't have comes back as a free row with an `error` field (`includeUnmatched`, on by default).
Start with: {"companyNames":["Stripe","Notion","Datadog"],"maxResults":100}
Ask me what to look up, run the Actor, then summarise the rows as a table.

The machine-readable API, MCP config and OpenAPI definition live at apify.com/foxlabs/company-enrichment.md.

📋 Overview

Everything you need to enrich a company list — in one Actor, with no login, cookies or API key.

Why teams pick this Actor:

  • ✅ Names, domains or URLs — use whatever your list has; a domain gives the most exact match.
  • 🏢 Firmographics in one row — website, domain, industry, HQ and street address, founded year, employees, revenue band, ownership, CEO and description.
  • 🧹 One row per company — inputs that resolve to the same Owler profile are fetched once.
  • 💰 Pay per record — $0.001 per company record, no subscription.
  • 🤖 Agent-ready — MCP + x402 agentic payments.

✨ Features

  • 🔍 Three ways in — companyNames, companyDomains and companyUrls (Owler company URLs, or a .txt / .csv upload).
  • 🎯 Matching through Owler's search — in our test run Notion resolved to Notion Labs, Inc. and Datadog to Datadog, Inc.; when the search returns nothing usable, the Actor falls back to an Owler address built from the name or domain.
  • 🧾 Structured values — numeric employees next to the readable employeesFormatted and the employeesRange band; revenue as revenueFormatted plus revenueRange; the address split into street, city, state, country and zip; the CEO as name, title, Owler rating, photo, LinkedIn and Twitter.
  • 🚦 Misses are visible and free — a profile Owler doesn't have comes back as a row with an error field, which is not charged; turn includeUnmatched off to skip misses instead.

🎬 Quick Start

curl -X POST "https://api.apify.com/v2/acts/foxlabs~company-enrichment/runs?token=YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"companyNames":["Stripe","Notion","Datadog"],"maxResults":100}'

🚀 Getting Started (3 steps)

  1. Paste your companies — names, domains or Owler URLs; a domain gives the most exact match.
  2. Set the cap — maxResults limits how many companies are looked up (default 100; 0 = no limit).
  3. Run and export — download the dataset as JSON, CSV, Excel, HTML or XML.

📥 Input

{"companyNames":["Stripe","Notion","Datadog"],"maxResults":100}
FieldTypeDescription
companyNamesarrayCompany names, one per line. Each is matched through Owler's search suggestions: the first suggestion whose name contains your text (or is contained in it), otherwise Owler's top suggestion.
companyUrlsarrayOwler company URLs (https://www.owler.com/company/<slug>), or a .txt / .csv upload. Used exactly as given; URLs from other sites are ignored.
companyDomainsarrayDomains such as stripe.com. Owler's search is queried with the domain name, and the suggestion whose primary domain equals your domain wins; if none does, the domain is matched like a name.
maxResultsintegerMost companies looked up in one run (default 100; 0 = no limit). URLs come first, then domains, then names; companies past the cap are skipped.
maxConcurrencyintegerProfile pages fetched in parallel (default 10; values above 20 are treated as 20).
includeUnmatchedbooleanOn (default): a profile Owler doesn't have comes back as a free row with an error field. Off: it is skipped and only logged.
proxyConfigurationobjectApify proxy settings (default: Apify proxy, automatic selection).

More recipes

  • Exact match by domain: {"companyDomains":["stripe.com"],"maxResults":10} — resolved to Stripe, Inc. in our test run.
  • Skip misses: {"companyUrls":[{"url":"https://www.owler.com/company/zzqx-no-such-company-2026"}],"includeUnmatched":false} — the profile Owler doesn't have is only logged. With the default, it comes back as a free row with error: Empty company in initialState (Owler 404 / Page Not Found).

📤 Output

One row per company. Rows that report a miss carry an error field and no company data: a profile Owler doesn't have (unless includeUnmatched is off), or a page that loaded but could not be read (in any mode). A page that still fails after its retries — blocked or server errors — is only logged, not written. From 25 September 2026 rows with error are not charged.

FieldDescription
urlOwler profile the row was read from
name / legalNameShort name and full legal name
website / domain / logoWebsite, root domain, logo image URL
industry / industriesOwler's industry sectors, comma-joined (e.g. Fintech Software), and its broader industry group (e.g. Banking, Financial Services and Insurance)
headquarters / addressHQ as city, state, country, and the street address: street1, street2, city, state, country, zipcode
yearFoundedFounding year
employees / employeesFormatted / employeesRangeEmployee count as a number and as text, and Owler's size band
revenueFormatted / revenueRangeRevenue figure and band as shown on Owler (e.g. 5.1B, $5B - 10B)
ownershipPublic or Private
ceoThe executive Owler lists as CEO: name, designation (title), rating (Owler approval rating, 0–100), photoUrl, linkedin, twitter
descriptionOwler's one-paragraph company description
matchedAlways true on a company row — misses come back with error instead
totalCompetitors / totalAcquisitionsHow many competitors and acquisitions the source profile lists. The counts are returned; the lists are not (see below)
competitors / acquisitions / fundingAlways empty here — this Actor is scoped to firmographics
deeperDataAtWhere the full lists come from: the Owler Scraper Actor
scrapedAtTimestamp of the lookup
errorMiss rows only — why the profile could not be read

Why some fields are deliberately empty. This Actor returns firmographics — the fields you need to enrich a CRM or score a lead list — at a price built for running over a whole list. Competitor lists, funding rounds and acquisitions are a different job: you want them on a handful of accounts, not on ten thousand. So the record carries the counts (so you can see what exists) and points at Owler Scraper, which returns those lists in full along with ticker, phone, leadership and financial history.

In our test run on 2026-09-20 (Stripe, Notion, Datadog, Monzo, Ramp, Kith, Peloton) every company field above was filled on all 7 rows, and totalCompetitors / totalAcquisitions were present on 7/7 — genuinely 0 for Notion and Datadog, whose Owler profiles list no competitors. address.street2 remains partial, as street addresses often have no second line.

Sample — Stripe, from our test run on 2026-09-10:

{
"url": "https://www.owler.com/company/stripe",
"name": "Stripe",
"legalName": "Stripe, Inc.",
"website": "https://stripe.com/",
"domain": "stripe.com",
"industry": "Fintech Software",
"industries": [
"Banking, Financial Services and Insurance"
],
"headquarters": "San Francisco, California, USA",
"address": {
"street1": "185 Berry Street",
"street2": "Suite 550",
"city": "San Francisco",
"state": "California",
"country": "USA",
"zipcode": "94107"
},
"yearFounded": 2009,
"employees": 8200,
"employeesFormatted": "8,200",
"employeesRange": "5,000 - 10,000",
"revenueFormatted": "5.1B",
"revenueRange": "$5B - 10B",
"ownership": "Private",
"ceo": {
"name": "John Collison",
"designation": "President",
"rating": 64,
"photoUrl": "https://image4.owler.com/ceo/John_Collison_owler_20240726_224858_original.png",
"linkedin": "https://www.linkedin.com/in/johnbcollison/",
"twitter": "https://twitter.com/collision"
},
"description": "Stripe is a California-based financial infrastructure platform that offers solutions such as payment processing, VAT automation and invoice management for businesses.",
"logo": "https://image4.owler.com/logo/stripe_owler_20260318_134635_medium.png",
"matched": true,
"totalCompetitors": 23,
"competitors": [],
"totalAcquisitions": 22,
"acquisitions": [],
"funding": null,
"deeperDataAt": "https://apify.com/foxlabs/owler-intelligence",
"scrapedAt": "2026-09-20T12:56:21.732Z"
}

Stripe's profile lists 23 competitors and 22 acquisitions. This Actor tells you they exist; Owler Scraper returns them.

💼 Use cases

1. CRM enrichment — fill empty account fields. Input: domains from your CRM. Output: industry, HQ, employees, revenue band, ownership. Use: complete account records for routing and reporting.

2. Lead scoring — size and revenue band for every lead. Input: the company names on a lead list. Output: employees, revenueRange, industry. Use: score and prioritise the list.

3. Account research — a short profile before a call. Input: one company. Output: description, HQ, the CEO with title and LinkedIn. Use: prepare the conversation.

🔗 Integration

JavaScript / Node.js

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('foxlabs/company-enrichment').call({"companyNames":["Stripe","Notion","Datadog"],"maxResults":100});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items[0]);

Python

from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('foxlabs/company-enrichment').call(run_input={"companyNames": ["Stripe", "Notion", "Datadog"], "maxResults": 100})
for item in client.dataset(run.default_dataset_id).iterate_items():
print(item)

Written for apify-client 3 for Python, where call() returns a run object; with version 1 or 2 it returns a dict — write run['defaultDatasetId'] there.

Automation (n8n / Zapier / Make): schedule or webhook → HTTP request to the Actor API with your companyNames or companyDomains → handle the JSON dataset → push to a sheet, CRM or dashboard.

📊 Pricing

Pay-per-event: $0.001 per delivered company record (≈ $1 per 1,000 companies), plus Apify's run-start event ($0.00005 per GB of run memory). Rows that only report a failed lookup (error) are free (since 25 September 2026). A profile Owler doesn't have comes back as such a free row, or produces no row at all with includeUnmatched off. Current pricing.

Spending limit. Before every record the Actor checks the run's maximum charge — the limit you set for the run, or else what is left of your monthly usage limit. When there is no room for another record it stops fetching, keeps what it has delivered, and the status message says how many companies you got.

❓ FAQ

Where does the data come from? From Owler's public company profile pages (owler.com/company/…): the Actor reads the company data embedded in each page. Names and domains are matched with Owler's search suggestions. No login and no Owler account are needed.

Name, domain or URL? A URL is exact. A domain picks the Owler suggestion whose primary domain equals it. A name picks the first suggestion whose name matches your text, otherwise Owler's top suggestion — so check name, legalName and domain in the output. Rows don't repeat your input; match them to your list on those fields.

Are the revenue and employee figures official? They are the figures on the company's Owler profile, passed through unchanged.

Why is matched always true? Every company row is a match. A miss either comes back as a row with error or is left out (see Output).

Can I export to CSV / Excel / JSON? Yes — directly from the Apify dataset.

🐛 Troubleshooting

  • A name returns the wrong company — use its domain or paste its Owler URL; names are matched through search.
  • A company is missing — look for its row with an error field: Owler has no such profile, or its page could not be read. With includeUnmatched off, misses are skipped and only the run log lists them. Pages that still fail after their retries are only logged.
  • Fewer rows than inputs — maxResults (default 100) caps the lookups, and inputs that resolve to the same profile are fetched once.
  • The run ended with "stopped because the maximum charge … was reached" — the run's maximum charge, or what is left of your monthly usage limit, covered fewer companies than you asked for. The dataset holds the ones that were delivered; raise the limit and run the rest.
  • The run fails with "No companies to enrich" — the input had no names, no domains and no Owler company URLs (URLs from other sites are ignored).

The Actor reads publicly accessible company profile pages. CEO names, photos and social links are personal data under the GDPR and similar laws — process them only with a lawful basis, and check that your use fits the source website's terms. Owler is a trademark of its owner; this Actor is not affiliated with Owler.

🤝 Support & contact

Changelog

0.1.20 — 2026-10-08 — README: the Python example works with the current Apify client

  • The README's Python example read the results with run['defaultDatasetId']. With the current Apify client for Python (version 3) call() returns a run object, not a dict, and that line raised TypeError: 'Run' object is not subscriptable — after the run itself had finished. The example now reads run.default_dataset_id; with client version 1 or 2, keep run['defaultDatasetId']. Checked with apify-client 3.2.1, 2.5.1 and 1.12.2.
  • No code, output field or pricing change.

0.1.19 — 2026-10-07 — the run stops at its maximum charge and says so

  • A run that reaches its maximum charge now stops by itself and tells you. The maximum charge is the limit you set for the run, or else what is left of your monthly usage limit. Until now the Actor did not look at it: companies past the limit were logged as enriched but left out of the dataset, with nothing in the run to say so. The Actor now checks the room left before every record, stops fetching when there is none, and finishes as a normal run with a status message that says how many companies were delivered. Nothing past the limit is charged.
  • No output field changed, no pricing change.

0.1.17 — 2026-09-26 — misses come back as free rows by default

  • Behaviour change: includeUnmatched is now on by default. A company Owler doesn't have comes back as a row with an error field instead of being skipped, so a run whose companies all miss no longer ends with an empty dataset and no explanation. These rows are not charged (per-record billing since 25 September 2026). To keep the old behaviour, set "includeUnmatched": false.

0.1.16 — 2026-09-20 — scope made visible in the output (6 new fields, additive)

  • Six fields added to every company row. totalCompetitors and totalAcquisitions carry the counts the source profile lists; competitors, acquisitions and funding are returned empty, and deeperDataAt points at Owler Scraper, which returns those lists in full. Previously these fields were absent from the output entirely, so a record looked complete when it was deliberately scoped to firmographics — you could not tell what you were not getting.
  • Additive change, nothing removed or renamed. Existing integrations keep every field they had; the row goes from 21 to 27 fields. Error rows carry the same six fields so the shape is consistent in both modes.
  • Dataset table view now shows the two counts and the pointer, so the scope is visible in the Console as well as in the JSON.
  • Measured on a local run of 7 companies (Stripe, Notion, Datadog, Monzo, Ramp, Kith, Peloton): the two counts were present on 7/7 rows and the field count was identical on all 7. 0 for Notion and Datadog is the real value — their Owler profiles list no competitors, confirmed against the source.
  • No pricing change, no change to what is charged: the company-record event and the 25 September switch described in 0.1.15 are unaffected.

0.1.15 — 2026-09-10 — failed lookups become free (per-record billing) + README corrected

  • Input descriptions corrected: the cost examples now use the real price ($0.001 per record: 100 ≈ $0.10, 1,000 ≈ $1), and maxConcurrency says the Actor caps it at 20.
  • You will pay only for delivered company records. The Actor now charges a new company-record event for each delivered company, and rows that only report a failed lookup (error) are pushed without a charge (src/billing.js; the record push in src/main.js now goes through pushRecord, the two error-row pushes stay plain).
  • Timing: Apify applies pricing changes after a 14-day notice. From 25 September 2026 the pricing switches from the per-dataset-item event to company-record at the same price ($0.001). Until then billing is exactly as before; the code detects the switch by itself.
  • No output field changed.
  • README corrected. The 2026-09-07 README documented inputs this Actor does not have (queries, maxResultsPerQuery, includeRaw) with a placeholder (undefined) example, named the source "public company web and registry sources" (the Actor reads Owler), called industry a NACE code and employees a registered count, and promised registry-ID lookup, status / legal form / formation date, "formation / status monitoring", "whole dataset in one call", "every row carries query", "no degenerate columns" (matched is always true) and "empty/failed lookups never billed". The new README describes the real inputs, output and billing, checked on local runs.
  • The older entries below are kept as written; the 0.0 entry's "registry sources … registry ID" was wrong — the source is Owler, and lookups are by name, domain or Owler URL.

0.1 — 2026-09-07

  • Enabled AI-agent payments (x402) + rebuilt the README to the full standard (What-is / when, AI-agents + x402 agentic payments + MCP, Overview, Features, Use cases, Integration, FAQ, Troubleshooting, Support & contact).

0.0

  • Initial release: data from public company web and registry sources by name or registry ID.