Skip to main content
Glama
fairquery

fairquery-contact-enricher

Official

Website Contact & Company Enricher

Fairquery Apify Actor that turns a list of domains into one clean row per company: business emails (validated for MX, disposable domains and typos), phone numbers (E.164), social profiles, company name, description and postal address, taken from the homepage and its contact / about / imprint pages. Plain HTTP, no browser, robots.txt respected, one request at a time per site.

When to use this Actor (LLM / tool selection)

Use it when an agent or sales-ops workflow needs to:

  • Add a contact email, phone and LinkedIn company page to a list of company websites (CRM enrichment, lead lists, vendor onboarding)

  • Find the generic inbox (info@, sales@, support@, press@) of a company and check that its domain actually receives mail

  • Get the legal company name and address from structured data or the imprint page (EU sites)

  • Collect a company's official social accounts

Do not use it to find personal emails of individuals or to scrape people's profiles. By default it returns role / generic mailboxes only (emailScope: "role"); personal LinkedIn profiles (/in/…) are never returned.

Related MCP server: Brand Intelligence MCP

How it works

  1. Normalises each input (https://www.Acme.com/contact, jane@acme.com, acme.com → acme.com) and removes duplicates.

  2. Reads robots.txt (RFC 9309: missing = allowed, 5xx = disallowed) and honours Disallow rules and Crawl-delay for user agent FairqueryBot.

  3. Fetches the homepage, then the best-scoring same-site pages (contact, Kontakt, contacto, about, imprint/Impressum, legal notice, team…) up to maxPagesPerDomain, waiting requestDelayMs between requests to the same site.

  4. Extracts: JSON-LD Organization/LocalBusiness (name, legalName, telephone, email, address, sameAs), mailto: and tel: links, Cloudflare-protected emails, name [at] domain [dot] com obfuscation, og:site_name, <address> blocks, social links.

  5. Validates emails with the same code as Fairquery Email Validator: syntax, DNS-over-HTTPS MX/null-MX, disposable list, free-provider and typo checks (no SMTP probing).

Input

Field

Type

Default

Description

domains

string[]

(required)

Domains, URLs or emails

maxPagesPerDomain

int

5

1–15, homepage included

emailScope

role | business | all

role

business adds named addresses on the company's own domain

validateEmails

bool

true

MX / disposable / typo

checkSpfDmarc

bool

false

SPF/DMARC for the company mail domain

defaultPhoneCountry

string

–

e.g. US; also parse national-format numbers in page text

domainConcurrency

int

5

Sites in parallel

requestDelayMs

int

1000

Delay between requests to one site

requestTimeoutSecs

int

15

Per request

maxDomains

int

1000

Safety cap

{ "domains": ["apify.com", "stripe.com", "https://basecamp.com/"], "maxPagesPerDomain": 4 }

Output

{
  "schema_version": "1.0.0", "type": "domain", "input": "apify.com", "domain": "apify.com", "final_url": "https://apify.com/",
  "company_name": "Apify Technologies s.r.o.",
  "description": "Cloud platform for web scraping, browser automation, AI agents, and data for AI…",
  "emails": [
    { "email": "hello@apify.com", "source_url": "https://apify.com/contact", "is_role_account": true, "on_company_domain": true,
      "is_free_provider": false, "is_disposable": false, "verdict": "valid", "reasons": ["ROLE_ACCOUNT"], "has_mx": true, "mail_provider": "Google Workspace / Gmail" }
  ],
  "primary_email": "hello@apify.com",
  "phones": [{ "e164": "+18889262289", "raw": "+1 888 926 2289", "source_url": "https://stripe.com/contact/sales", "source": "tel" }],
  "social_links": { "linkedin": ["https://linkedin.com/company/apify"], "twitter": ["https://x.com/apify"], "github": ["https://github.com/apify"] },
  "address": "Na Příkopě 959/27, 11000, Prague, CZ",
  "mail_domain": { "has_mx": true, "null_mx": false, "mail_provider": "Google Workspace / Gmail", "has_spf": null, "has_dmarc": null, "dmarc_policy": null },
  "pages_crawled": ["https://apify.com/", "https://apify.com/contact"], "pages_blocked_by_robots": 0, "robots_txt": "ok", "email_scope": "role"
}

Error rows (type: "error", never charged): INPUT_EMPTY, INVALID_DOMAIN, MAX_DOMAINS_EXCEEDED, SITE_UNREACHABLE, BLOCKED_BY_ROBOTS, ENRICH_FAILED.

RUN_SUMMARY (key-value store): pages fetched, pages blocked by robots.txt, emails found vs. excluded by scope, DNS requests, charged events.

Pricing

Pay per event: domain-enriched, once per domain that returns a result row. Unreachable, blocked or invalid domains are free. See the pricing tab.

Memory / runtime

Default 256 MB, no browser, no proxy. With the default politeness settings a domain takes about 3–8 s (homepage + 4 pages, 1 s apart), and 5 domains run in parallel, so 100 domains finish in roughly 1–3 minutes. Sites that block datacenter IPs or need JavaScript to render contact details will return fewer fields.

MCP / Apify tools

Call from agents via Apify MCP with Actor fairquery/website-contact-company-enricher:

{ "actor": "fairquery/website-contact-company-enricher", "input": { "domains": ["acme.com"] } }

Local stdio MCP server

The mcp/ folder is a stdio MCP server (TypeScript, @modelcontextprotocol/sdk) with two tools, both with annotations and output schemas:

Tool

What it does

Cost

enrich_domains

Runs this Actor and returns the rows

Needs APIFY_TOKEN, billed per domain

normalize_domains

Cleans URLs/emails into unique domains, lists invalid ones

Free, local, no token

cd mcp && npm install && npm run build
node scripts/smoke-test.mjs                          # initialize + tools/list + free tool, no token
APIFY_TOKEN=... node scripts/smoke-test.mjs --call   # real call
{ "mcpServers": { "fairquery-contact-enricher": { "command": "node", "args": ["/path/to/website-contact-company-enricher/mcp/dist/index.js"], "env": { "APIFY_TOKEN": "<your Apify token>" } } } }

Docker: the root Dockerfile builds the MCP server image (docker build -t fairquery-contact-enricher-mcp .); it is also the Dockerfile for Glama. The Actor image is .actor/Dockerfile. glama.json lists fairquery as maintainer.

Local development

npm install
npx apify run      # input from storage/key_value_stores/default/INPUT.json
npm test

Kits & templates

Optional guides from Fairquery for wiring this Actor into agents and automations (the Actor works fine without them):

Disclaimer

Returns only information that companies publish on their own websites. You are responsible for how you use it: business contact data can still be personal data under GDPR/UK GDPR, so have a lawful basis, honour opt-outs, and follow anti-spam law (CAN-SPAM, ePrivacy/PECR) when you contact anyone. DNS checks cannot prove a mailbox exists.

  • License: MIT

Available Tools

2 tools
enrich_domainsEnrich domains with public business contactsA
Read-onlyIdempotent

WHAT: For each company domain, crawl the homepage plus up to N contact/about/imprint pages (plain HTTP, robots.txt respected, polite per-site delay) and return one row with company_name, description, business/role emails (MX, disposable and typo validated), phones (E.164), social profiles (LinkedIn company, X, Facebook, Instagram, YouTube, GitHub...), postal address and mail-domain info. WHEN TO USE: enriching a lead/CRM list of company websites with public contact data; finding the official contact email or address of a business. WHEN NOT TO USE: to clean/dedupe a messy list first or preview what will be billed, call normalize_domains (free). Not for finding personal emails of individuals, LinkedIn people search, or JavaScript-only sites that need a browser. COST: paid. Runs Apify Actor fairquery/website-contact-company-enricher with your APIFY_TOKEN; pay-per-event "domain-enriched", once per domain that returns a result row. Unreachable, blocked or invalid domains are free. ~2-10 s per domain. RETURNS: {count, items}. type="domain" row per domain: input, domain, final_url, company_name, description, emails[], primary_email, phones[] (E.164), social_links, address, addresses, mail_domain (MX/provider, SPF/DMARC if requested), pages_crawled, pages_blocked_by_robots, email_scope, enriched_at; or type="error" rows (code, message, retryable). EXAMPLE: {"domains":["stripe.com","https://basecamp.com/about"],"maxPagesPerDomain":3} -> 2 domain rows.

ParametersJSON Schema
NameRequiredDescriptionDefault
domainsYesRequired. Domains, website URLs or email addresses (the domain part is used); comma/semicolon lists are split. Example: ["apify.com","https://stripe.com","info@basecamp.com"].
emailScopeNo"role" (default, GDPR-friendly) = generic mailboxes only (info@, sales@, support@); "business" = also named addresses on the company's own domain; "all" = every address published on the pages.
checkSpfDmarcNoDefault false. Add SPF/DMARC presence and DMARC policy for the company mail domain.
validateEmailsNoDefault true. DNS-over-HTTPS MX lookup plus disposable and typo checks on found emails (no SMTP probing).
maxPagesPerDomainNoPages fetched per domain, 1-15 (default 5): homepage plus the best contact/about/imprint pages linked from it.
defaultPhoneCountryNoISO 3166-1 alpha-2 code, e.g. "US", "DE". When set, national-format numbers in page text are also parsed; otherwise only tel: links, JSON-LD and +international numbers. No default.

Output Schema

ParametersJSON Schema
NameRequiredDescription
countYesNumber of rows in items
itemsYesDataset rows: type=domain or type=error

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly/openWorld/idempotent/non-destructive, and the description layers on substantial extra context: pay-per-event billing (once per domain returning a row), free failures for unreachable/blocked/invalid domains, ~2-10 s per domain, robots.txt respect, per-site delay, and the APIFY_TOKEN requirement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Strongly front-loaded and labelled (WHAT/WHEN/COST/RETURNS/EXAMPLE), so an agent can scan it fast. It is on the long side, and the RETURNS block partially duplicates the output schema, which slightly dilutes conciseness.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema and rich annotations, the description still covers cost, auth, failure modes, timing, and scope exclusions. Nothing an agent needs to decide or invoke correctly appears missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is already 100%, so baseline is 3. The description adds genuine meaning via maxPagesPerDomain ('homepage plus up to N contact/about/imprint pages') and the validation chain behind emails (MX, disposable, typo) and optional SPF/DMARC, though most parameter detail remains in the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Specific verb+resource: crawls a company domain's homepage and contact pages to return public business contact data, with the exact field set enumerated. It also implicitly distinguishes itself from normalize_domains by describing enrichment/paying-per-domain rather than list cleanup.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicit WHEN TO USE (lead/CRM enrichment, finding official business contact info) and WHEN NOT TO USE (list cleaning, personal emails, LinkedIn people search, JS-only sites), plus a named alternative: normalize_domains, flagged as free.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

normalize_domainsNormalize and dedupe domains (free)A
Read-onlyIdempotent

WHAT: Turn URLs, email addresses and messy strings into bare registrable hostnames (lowercase, no scheme/path/www), remove duplicates and list entries that are not valid domains. WHEN TO USE: before enrich_domains, to clean an input list and see exactly which domains would be billed. WHEN NOT TO USE: to get contacts, company names or check that a site is reachable - use enrich_domains. Does no DNS lookup. COST: free; local, no network, no APIFY_TOKEN, deterministic. RETURNS: {domains: string[], invalid: string[], duplicates_removed: number}. EXAMPLE: {"inputs":["https://www.Stripe.com/pricing","info@stripe.com","not a domain"]} -> {"domains":["stripe.com"],"invalid":["not a domain"],"duplicates_removed":1}

ParametersJSON Schema
NameRequiredDescriptionDefault
inputsYesRequired. URLs, domains or email addresses; each entry may also be a newline/comma/semicolon-separated list. Example: ["https://www.acme.com/contact","sales@acme.com"].

Output Schema

ParametersJSON Schema
NameRequiredDescription
domainsYesUnique normalized domains, input order
invalidYesEntries that could not be parsed as a domain
duplicates_removedYesCount of duplicate entries dropped

TDQS

A4.7/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, openWorldHint=false and destructiveHint=false, and the description adds genuinely new behavioral context: it is local, free, deterministic, requires no APIFY_TOKEN, and performs no network/DNS activity. It also states why it exists ('see exactly which domains would be billed'), which no annotation conveys.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Labeled sections (WHAT, WHEN TO USE, WHEN NOT TO USE, COST, RETURNS, EXAMPLE) make it skimmable and front-load purpose before routing. Each block carries distinct information with no filler, and the example compactly demonstrates dedupe plus invalid-entry handling.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The only parameter is fully specified and an output schema exists, so return values need no further prose; the inline RETURNS note is a bonus. Cost, side-effect profile, and pre-requisite chaining with enrich_domains are all covered, leaving no gap for an agent to fill.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and there is a single required parameter, so the schema already documents the input format, including the newline/comma/semicolon-separated list behavior. The description's mention of URLs, emails and messy strings plus the worked example largely restates that, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Opens with an explicit verb+resource ('Turn URLs, email addresses and messy strings into bare registrable hostnames') and enumerates the transformation rules (lowercase, no scheme/path/www). It explicitly names what it is not ('use enrich_domains') so an agent can separate it from its only sibling without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit WHEN TO USE (before enrich_domains, to preview billed domains) and WHEN NOT TO USE (contacts, company names, reachability checks), naming the alternative tool and a boundary condition ('Does no DNS lookup'). Nothing about tool selection is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.1.0
    • First observedenrich_domains
    • First observednormalize_domains

TDQS

A4.6/5.0

Scored across 2 tools

Disambiguation5/5

The two tools have clearly distinct roles: normalize_domains cleans and previews input locally for free, while enrich_domains performs the paid network enrichment. The descriptions explicitly state when to use each and when not to use them, eliminating overlap.

Naming Consistency5/5

Both tool names follow the same predictable verb_noun snake_case pattern: normalize_domains and enrich_domains. The names clearly indicate the action and target resource.

Tool Count3/5

A two-tool surface is thin but focused for a contact-enrichment server. The prep tool is useful, though the set could benefit from at least one additional operation for batch handling or cost estimation.

Completeness4/5

The core workflow—cleaning domains and enriching them with contact data—is covered well. Minor gaps remain around large-batch/async processing and explicit paid-cost preview, which agents may need for bigger lead lists.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    F
    maintenance
    Provides domain and brand intelligence for AI agents, including company enrichment, tech stack detection, and brand research from free public sources with an on-demand cache.
    MIT
  • A
    license
    A
    quality
    A
    maintenance
    Enables enriching company domains into structured firmographics including employee band, industry, HQ, founded year, revenue estimate, logo, and description, with source provenance and data completeness scoring.
    1
    77 npm
    1
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Domain and company intelligence for AI agents. Enables vetting companies, qualifying leads, and mapping targets from free public data without API keys.
    6
    2 npm
    1
    MIT