Company Firmographic Enricher avatar

Company Firmographic Enricher

Pricing

from $3.40 / 1,000 results

Go to Apify Store
Company Firmographic Enricher

Company Firmographic Enricher

Domain to structured company firmographics: employee band, industry, HQ, founded year, revenue estimate, logo, and description from schema.org JSON-LD and meta tags. Flat JSON, Clay ready, with source provenance.

Pricing

from $3.40 / 1,000 results

Rating

0.0

(0)

Developer

Mamba Labs

Mamba Labs

Maintained by Community

Actor stats

1

Bookmarked

49

Total users

8

Monthly active users

4 days ago

Last modified

Share

🏢 What can Company Firmographic Enricher do?

Give it a company domain and it returns a structured firmographic record: employee band, industry, HQ location, founded year, a revenue estimate, the company main line, logo and description, parsed from the company's own schema.org/Organization JSON-LD and HTML meta tags.

Every record carries a source_signals array and a data_completeness score, so you always know where the data came from and how much was actually found.

📦 What you get⚙️ Features and integrations
🏢 Employee band, industry, HQ, founded year
☎️ Company main line, normalized to E.164
🔍 source_signals, exactly which sources contributed
🧾 23 flat fields, always present, null when absent
🧬 JSON-LD first, HTML meta tags as fallback
🛡️ Datacenter proxy fallback only when blocked
🧊 7 day cache, repeat lookups skip the network, not the per-row charge
⬇️ Export to JSON, CSV, Excel, HTML or XML

Bought by Clay users, RevOps teams and outbound agencies that need enriched company records without a paid data provider dependency. It is the canonical company record the rest of the Mamba Labs fleet joins on.

🚫 This reads what a company publishes about itself, not a licensed database. No ZoomInfo, no Clearbit, no Crunchbase. It will not return data those providers hold but the company does not publish. That is the tradeoff for auditable, low-cost enrichment.

💡 Why use Company Firmographic Enricher?

If you wantRead these fields
Company size for segmentationemployee_band, employee_count
Where they arehq_location
How long they have existedfounded_year
A rough revenue bandrevenue_estimate
A switchboard numbercompany_phone, company_phone_raw, company_phone_source
Copy for a sequencecompany_name, description, logo_url
Whether to trust the rowdata_completeness, source_signals

🔍 Provenance on every row, so you can gate on real coverage

source_signals names exactly which sources populated the record: jsonld_organization, meta_tag, a proxy fetch, cache_hit, and so on. data_completeness is a 0 to 100 share of the eight core fields that were actually found.

Together they mean you never have to guess whether a null is "the company did not publish it" or "the fetch went wrong". Gate downstream work on data_completeness >= 50 rather than assuming a field is present.

▶️ Ready-to-run examples

ExampleWhat it does
Enrich Company Firmographics from a DomainName, industry, employee count and location from a domain
Company Lookup by DomainDomain-to-company enrichment at a low per-row cost
Bulk Domain to Company EnrichmentEnrich a full list of domains in one batch run
Domain to Employee Count and SizeHeadcount and size band from a domain
Name and Domain Enrichment with FallbackUse both name and domain for higher match rates

📋 What data can Company Firmographic Enricher extract?

23 fields per domain, in this table and in "More fields on every row" below. Every field is always present; absent values are null.

FieldWhat it holds
company_nameFrom JSON-LD name, meta tags, or your input fallback
employee_bandBucketed range, 1-10 through 10001+
employee_countRaw count from JSON-LD numberOfEmployees
industryBest effort, often null
hq_location"City, Region, Country" from the JSON-LD address
founded_yearFour digit year from foundingDate
revenue_estimateHeuristic band derived from employee count
logo_urlFrom JSON-LD logo or og:image
descriptionCompany description, capped at 500 characters
company_phoneMain line, E.164 when a country resolves
company_phone_rawThe number exactly as published, before normalization
company_phone_sourceWhich node it came from
fetch_statusWhat happened on the fetch
source_signalsWhich sources populated the record
data_completeness0 to 100, share of the eight core fields populated
run_dateWhen the run happened

⚠️ revenue_estimate is a heuristic, not a figure, derived from employee count using a per-employee proxy. Treat it as a rough band. industry is best effort and often null, because schema.org has no first-class industry field. Neither should be relied on the way you would rely on employee_count, which comes straight off the company's own published data.

📏 How often headcount and HQ are filled. Both come only from the company's own homepage structured data, and most homepages do not publish them. Measured on 2026-10-12 across 28 reachable homepages of well known software companies: employee_count (and so employee_band and revenue_estimate) was published on 3, and hq_location on 13. The actor read every value that was published. A null here means the homepage does not state it, not that the read failed. A page that lists several offices without naming the headquarters returns null rather than a guess. For headcount on a list where most rows must be filled, use a source that estimates it, such as a data provider in Clay, and keep this actor for the companies that publish it.

🛠️ How to enrich a company from its domain

  1. Open the Input tab and put a bare domain in domain, without https:// or a trailing slash.
  2. Optionally set company_name as a fallback label for pages that do not publish one.
  3. For a list, use domains instead. It takes precedence and returns one row per domain.
  4. Click Start.
  5. Check data_completeness before you use the row.

🧪 Using it in Clay

Add an Apify enrichment column and map your domain column to domain. The output is flat, so every field maps to a Clay column with no JSON unwrapping.

Gate downstream enrichment or outreach on data_completeness >= 50 so you do not spend credits enriching rows where almost nothing was found.

☎️ How the phone number is chosen

telephone is a standard property on the organization nodes this actor already parses, so the switchboard number costs no extra request and no extra credit.

Read in precedence order: a node's own telephone, then a nested contactPoint with a general, main or customer service contactType ahead of sales, support and billing, then a telephone on the address or location node, then the phone meta tags. Where two nodes disagree, the one whose own url or @id sits on the domain being enriched wins, then the more specific node type.

The number is normalized to E.164 using the HQ country the actor already resolves. Where no country can be determined it is emitted exactly as published, without a leading +, so the leading + is itself the provenance: present means E.164, absent means the country was unknown and the value is untouched. Implausible values are rejected rather than emitted: wrong digit counts, repeated and sequential digit runs, the NANP 555-01xx fictional range and the Ofcom drama ranges.

💵 How much does it cost to enrich a company?

You are charged once per domain enriched.

PlanPer domainPer 1,000 domains
Free$0.004$4.00
Bronze$0.0038$3.80
Silver$0.0036$3.60
Gold$0.0034$3.40

There is also an Actor start event at $0.00005, charged once per run per GB of memory.

Free Apify plans get 800 results per calendar month, resetting on the 1st. Paid plans have no monthly cap.

🛑 Your spending limit is respected. Set a maximum cost per run in Apify, or max_charge_usd on the Input tab, and the run stops before it would go over, keeping every result already charged. Each row's billable_events_json lists the events that row was charged for.

💳 A domain that publishes nothing is still billed. The actor fetched the page, parsed it and reported honestly that there was nothing there, and data_completeness: 0 is a real answer.

📅 Scheduled pricing change, 2026-10-26 12:00 UTC. Results move from Apify's default per-row event to a named result event at the same price on every plan, so each result row costs exactly what it costs today. A run summary row and a blocked lookup row stop being charged. Until then every row is billed as described above.

⚠️ A cached repeat lookup is served instantly but is still billed. This actor prices per result row, and Apify charges that event for every row written to the dataset, whether the answer came from the network or from the 7 day cache. Corrected 2026-08-12: an earlier version of this section said cached repeat lookups were free. They are not, and they never were. Measured on 2026-08-12, an identical three domain re-run 19 minutes after the first was charged the same three result events. The cache saves you the wait and the platform usage, not the per-row price.

⌨️ Input

Everything is on the Input tab. Provide either domain or domains.

FieldTypeDefaultWhat it does
domainstringstripe.comBare domain, no scheme and no trailing slash.
company_namestringnoneFallback label when the page does not publish a name.
domainsarraynoneBatch. Takes precedence over domain. One row per domain.
companiesarraynoneWeb search mode. A list of {"domain": "...", "company_name": "..."}, either field may be empty. Needs anthropicApiKey. One row per company.
anthropicApiKeystringnoneYour own Anthropic API key, stored encrypted. With it, each company is looked up by Claude Haiku with web search instead of from its homepage, billed to your Anthropic account (about 0.03 to 0.05 USD a company).
batchSizeinteger5Domains enriched concurrently per wave. Maximum 10.
skipCachebooleanfalseForce a fresh enrichment and ignore the 7 day cache.
failOnMostlyEmptybooleanfalseOff by default: a domain that publishes no firmographic field returns a row with null fields, resolution_status and empty_reason, and the run ends SUCCEEDED. Set true to end the run FAILED when more than half the rows resolve no firmographic field (industry, employee count, HQ or founded year). The rows are saved either way.
max_charge_usdnumbernoneOptional. The run stops before the next result that would take this run's charges over this amount, and keeps every result already charged. Leave empty to rely only on the maximum cost per run you set in Apify.

📤 Output

One flat row per domain, exportable as JSON, CSV, Excel, HTML or XML.

{
"domain": "gitlab.com",
"company_name": "GitLab",
"employee_band": "1001-5000",
"employee_count": 2500,
"industry": null,
"hq_location": "San Francisco, CA, US",
"founded_year": "2011",
"revenue_estimate": "$250M-$1B",
"logo_url": "https://about.gitlab.com/images/press/logo/svg/gitlab-logo.svg",
"description": "GitLab is the DevSecOps platform.",
"company_phone": "+14155550199",
"company_phone_raw": "(415) 555-0199",
"company_phone_source": "jsonld_organization",
"fetch_status": null,
"industry_source": null,
"resolution_status": "resolved",
"empty_reason": null,
"source_signals": ["jsonld_organization"],
"data_completeness": 88,
"run_date": "2026-06-19T13:13:15Z"
}

🧾 More fields on every row

FieldTypeWhat it holds
industry_sourcestringWhich signal named the industry: jsonld_property (the site's own JSON-LD industry field), jsonld_type (a specific schema.org type such as CollegeOrUniversity), or page_text (keywords in the homepage title, description, headings and copy). Null when industry is null.
resolution_statusstringresolved when at least one of industry, employee_count, hq_location or founded_year was found; empty when the page was read but none was; fetch_failed when the homepage could not be read; error when enrichment threw.
empty_reasonstringWhy the row did not resolve, such as homepage_unreachable:<fetch_status>, page_text_too_thin:
row_statusstringok, degraded, error, or notice (a notice row carries a message, not a result). Present on every row.
error_reasonstringWhy the row is degraded or an error, in plain words. Null on an ok row. Present on every row.
billable_events_jsonstringJSON list of the pricing events this row was charged for, for example [{"event":"apify-default-dataset-item","count":1}]. Lets you reconcile a run from the dataset alone. The once per run start event is not repeated on rows.

💡 Tips

  • Gate on data_completeness rather than on any single field being non-null.
  • A row served from cache carries cache_hit in source_signals, so a null phone on a cached row is distinguishable from one on a fresh parse.
  • Pass company_name when you have it. It costs nothing and fills the label on sites that publish no name.
  • Use employee_band for segmentation and employee_count when you need the raw number. The band is stable across sites that publish ranges rather than exact figures.

🧯 What a run that finds nothing looks like

Since 2026-10-12 one empty lookup never ends the run.

  • A domain with nothing published returns its row with null fields, resolution_status: empty and an empty_reason such as no_firmographic_signal_on_homepage. The run ends SUCCEEDED, with a warning in the status message when most rows are empty. Set failOnMostlyEmpty: true if you want that case to end FAILED instead.
  • A domain that refuses the fetch returns resolution_status: fetch_failed and the refusal in fetch_status.
  • Run time is budgeted. Every page request is capped by the time left in the run, less a 90 second reserve, and no new batch of domains starts when less than about 35 seconds of that budget is left. Domains not started are listed under skipped_for_budget in the run's RESOLUTION_SUMMARY record, so a long list on a short timeout ends cleanly with the rows it has.
  • A labeled run outcome, not a billed row. The run's RESOLUTION_SUMMARY record (Storage, Key value store) carries run_outcome (found, no_results, blocked or timed_out), run_outcome_reason, the sources reached or refused, and the run ID and build. A refusal is labeled blocked, never reported as zero. This actor is priced per dataset row, and Apify charges every row written to the dataset, so the outcome is kept out of the dataset: you are never charged for a row the actor did not find beyond the per domain rows described under pricing.

⚠️ Known limits

Coverage varies by what the company publishes. Sites with rich schema.org/Organization JSON-LD return a near-complete record; sites with only basic meta tags return name, description and logo. data_completeness and source_signals make that transparent on every row.

revenue_estimate is a heuristic, not a figure. Treat it as a rough band, never as an authoritative revenue number.

industry is best effort and often null. schema.org has no first-class industry field. Do not build a segmentation on it being present.

Most sites publish no telephone. A null company_phone is the ordinary outcome, not a failure and not a degraded run. Every other field is unaffected, and the field is deliberately excluded from data_completeness so adding it did not reprice any historical score.

No paid data provider. This actor will not return data ZoomInfo, Clearbit or Crunchbase hold but the company does not publish.

Results are cached for 7 days. Pass skipCache: true for a live enrichment.

❓ FAQ

Why is industry null on a company that obviously has one?

Because the company did not publish it. schema.org has no first-class industry field, so this is the common case rather than the exception. Since build 0.0.34 the page copy is not used to guess one: a single generic word in a meta description used to decide it. Pass your own anthropicApiKey for the web search lookup, which names the industry, a headcount band, the company type and the company's own domain, and says whether the domain you sent belongs to the company you named.

What does the web search lookup add to a row?

official_domain (the named company's own website), domain_matches_company (whether the domain you sent is it), company_type (direct_employer, job_board, staffing_agency or other), industry with industry_source web_search, employee_band from the headcount band, identity_confidence (high, medium or low), identity_evidence, web_searches, and the input_domain and input_company_name you sent. A field the lookup could not establish is null.

What happens when a site blocks the fetch?

The actor fetches direct first, then the www host, and only falls back to a datacenter proxy when a domain blocks, which keeps runs fast and cheap. If it is still blocked, the row comes back with resolution_status: fetch_failed and fetch_status naming the refusal, for example direct:http_403,proxy:challenge_cloudflare. A refusal is labeled as one; it is never reported as a company with no data, and it is never raised as a run error.

One domain failed in my batch. Did it break the run?

No. Failures are caught per domain and the other rows are unaffected.

A JSON-LD block on the page is broken. Does that lose the whole record?

No. A block that is not valid JSON is skipped and reported in source_signals as jsonld_malformed_block, and the scan continues through the remaining blocks.

How do I know whether a phone number was normalized?

The leading +. Present means E.164; absent means the country could not be determined and the value is exactly as published. company_phone_raw always holds the published string.

🧩 Want other GTM data?

Mamba Labs builds custom actors for B2B go-to-market teams. The public versions of that work live here on the Store, so our users get the same tooling we build under contract.

🔭 Account Hiring Watch🕵️ Agent Accessibility Auditor
🤖 AI Tooling Detector📡 GTM Signals Aggregator
🚀 Prospect Engine📝 Publishing Frequency Tracker
🦋 Bluesky Brand Presence Mapper🔀 Career Change Signal Engine
⏩ Sequencer Lead Push🔄 Company Change Event Feed
📇 Company Contact Details Extractor🧭 Company Discovery List Builder
🪪 Company Identity Resolver🌐 Company Social Presence Mapper
🏷️ Contact Classifier📈 Influencer Change Monitor
🔎 Influencer Finder🧾 Influencer Lead List Builder
👤 Influencer Profile Scraper📬 Domain Deliverability Checker
🔗 Domain to LinkedIn URL Resolver🛒 Ecommerce Platform Profiler
✉️ Work Email Waterfall🎪 Event Presence Index
⚖️ Expert Witness Directory💵 Company Funding Record
💰 Funding and Press Signal Scanner🐙 GitHub Organization Signal Scanner
🧑‍💼 GTM Hiring Signal Scraper📋 Job Posting Monitor
🧱 Tech Stack Signal Detector🎯 ICP Fit Scorer
🔑 Job Board Keyword Signal Scanner🔁 Job Change Tracker
🏛️ Legal Entity Resolver📰 Link in Bio Scraper and Newsletter Detector
💼 LinkedIn Company Page Mapper💬 LinkedIn Post Tracker
📢 Meta Ad Library Monitor📸 Instagram Facebook Brand Presence Mapper
🆕 New Hire Finder📮 Outbound Stack Detector
📄 Page Finder and Extractor🧑 People Finder & Email Verifier
📌 Pinterest Brand Presence Mapper🏆 Government Contract Award Monitor
📅 Reporting Window Finder👽 Reddit Brand Presence Mapper
⭐ Trustpilot Reputation Enricher🤝 Influencer Talent Agency Lookup
👥 Team Page People Extractor🎵 TikTok Brand Presence Mapper
📊 Website Traffic Rank Estimator🏅 Workplace Program Detector
✖️ X Brand Presence Mapper▶️ YouTube Channel Stats Extractor

Every actor in the suite takes a domain or a company and returns one flat row, so they stack in the same Clay table without reshaping anything.

🛠️ Need something custom built for you or your team? Tell us what you are trying to find and we will build it. Talk to Mamba Labs.

🆘 Support

Something wrong, or a company the actor reads incorrectly? Open an issue on the Issues tab with the domain and the row, and we will look at it.

ℹ️ Sourcing and legal. Every field comes from structured data the company publishes on its own site: schema.org/Organization JSON-LD and HTML meta tags, read over plain HTTP. No paid data provider is called and no personal data is collected; the telephone number is a published company switchboard, not a person's line. You are responsible for how you use the output.

Built by Mamba Labs.