fairquery-contact-enricher
OfficialServer Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| APIFY_TOKEN | No | Optional Apify API token. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| enrich_domainsA | 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": $0.003 per domain that returns a result row. Unreachable, blocked or invalid domains are free. 100 domains ~ $0.30. ~2-10 s per domain. RETURNS: one type="domain" row per enriched domain (fields are null or empty when not published on the crawled pages) or free type="error" rows (code, message, retryable); with waitForFinish=false, items is empty and run holds run_id for get_enrichment_results. PARAMETERS: domains: entries are split on commas, semicolons and newlines, then each is reduced to its hostname (scheme, path, port and a leading "www." removed; for an email the part after @ is used; other subdomains are kept, so "shop.acme.com" and "acme.com" are separate) and deduplicated, so you pay once per unique enriched domain; unparseable entries give free INVALID_DOMAIN rows (max 1000 domains per run; extras get a free MAX_DOMAINS_EXCEEDED row). Run normalize_domains first to preview exactly what will be billed (billable_count, estimated_max_cost_usd). waitForFinish=false returns at once with run.run_id and the run keeps going in your Apify account (same price); use it for lists over ~100 domains, which may not finish within this call's 280 s wait, then poll get_enrichment_results. maxPagesPerDomain changes run time and coverage, never the price; 1 = homepage only, 3-5 usually finds the contact/imprint page. emailScope only filters which found addresses are returned: "role" keeps generic mailboxes (info@, sales@, support@...) on any domain, "business" adds named addresses on the company's own domain, "all" also keeps third-party/personal addresses - choose the narrowest scope your lawful basis allows. validateEmails=false skips only the DNS part: disposable/typo checks still run locally, has_mx and mail_domain become null and clean addresses get verdict "unknown" instead of "valid"; checkSpfDmarc adds SPF/DMARC for the company mail domain and has no effect when validateEmails=false. defaultPhoneCountry widens phone extraction to national-format numbers in page text (e.g. "030 1234567" with "DE"); without it only tel: links, JSON-LD and +international numbers are parsed, all returned as E.164. EXAMPLE: {"domains":["stripe.com","https://basecamp.com/about"],"maxPagesPerDomain":3} -> 2 domain rows. |
| get_enrichment_resultsA | WHAT: Read the status and result rows of an enrich_domains run started with waitForFinish=false, one page at a time. WHEN TO USE: after enrich_domains returned run.run_id, to poll until finished is true and page through the rows; rows already written are returned while the run is still RUNNING. WHEN NOT TO USE: to start an enrichment (enrich_domains) or to preview cost (normalize_domains). Cannot read runs of other Actors. BEHAVIOR: GET requests to the Apify API only (actor, run, dataset); never starts, aborts or re-bills a run. Typically <1 s. Poll every 10-30 s; a run works on 5 domains at a time, ~2-10 s each. COST: free; needs the APIFY_TOKEN that started the run. Reading charges no Actor event (the run was billed per enriched domain when it ran). RETURNS: run (run_id, status, finished) plus one page of the same rows enrich_domains returns; Errors: APIFY_TOKEN_MISSING, RUN_NOT_FOUND, RUN_NOT_FOR_THIS_ACTOR, APIFY_TOKEN_INVALID, APIFY_RUN_LOOKUP_FAILED / APIFY_DATASET_READ_FAILED (retryable). PARAMETERS: runId is run.run_id from enrich_domains (also in the Apify console URL). status is READY | RUNNING | SUCCEEDED | FAILED | ABORTED | TIMED-OUT; finished=true for the last four, after which total no longer changes. A FAILED or TIMED-OUT run still returns the rows it wrote. offset/limit page the rows in write order; total counts all rows written so far, so keep calling with nextOffset until it is null and finished is true. EXAMPLE: {"runId":"a1B2c3D4e5F6g7H8i"} -> {"run":{"status":"SUCCEEDED","finished":true},"count":1,"total":1,"items":[{"type":"domain","domain":"apify.com",...}]} |
| normalize_domainsA | 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. RETURNS: domains is exactly the list enrich_domains would bill; billable_count and estimated_max_cost_usd preview that bill; invalid entries would only get free error rows there. PARAMETERS: inputs: each element may hold one value or a newline/comma/semicolon-separated list; pieces are trimmed and empty ones dropped. Each piece is lowercased and reduced to its hostname (scheme, path, query, port and a leading "www." removed; for a bare email the part after @ is used); other subdomains are kept, so "blog.acme.com" and "acme.com" stay separate. A piece is invalid unless the host has a dot and a 2+ letter TLD (punycode xn-- hosts accepted). domains keeps first-seen order and duplicates_removed counts repeats after normalization. This is exactly the normalization enrich_domains applies: billable_count = min(domains.length, 1000 per run) and estimated_max_cost_usd = billable_count x $0.003 + the $0.00005 start fee, the most passing the same inputs to enrich_domains can cost (unreachable domains lower it). An empty array is accepted and returns empty lists. EXAMPLE: {"inputs":["https://www.Stripe.com/pricing","info@stripe.com","not a domain"]} -> {"domains":["stripe.com"],"invalid":["not a domain"],"duplicates_removed":1,"billable_count":1,"estimated_max_cost_usd":0.00305} |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 3 tools
Each tool has a clearly distinct role: normalize_domains cleans/previews input for free, enrich_domains performs the paid enrichment, and get_enrichment_results polls async runs. The descriptions explicitly state WHEN NOT TO USE, cross-referencing the other tools, so there is no realistic chance of misselection.
All three names follow a consistent snake_case verb_noun pattern: normalize_domains, enrich_domains, get_enrichment_results. The convention is predictable and readable throughout.
Three tools map exactly onto the natural workflow stages (preview, enrich, retrieve async results), with each tool earning its place and no redundancy. This is well-scoped for a narrow contact-enrichment purpose.
The surface covers the full lifecycle: free normalization/preview, paid enrichment, and async result retrieval including paging. Minor gaps exist (no run cancellation/abort or free-domain validation separate from enrichment), but agents can work around them.