Prospect Engine: B2B Company and Contact Enrichment avatar

Prospect Engine: B2B Company and Contact Enrichment

Pricing

from $10.50 / 1,000 contact verifieds

Go to Apify Store
Prospect Engine: B2B Company and Contact Enrichment

Prospect Engine: B2B Company and Contact Enrichment

Turn a domain or a hiring keyword into enriched company records and verified contacts. Resolves the real employer behind a job posting, scores ICP fit, and returns one flat Clay-ready row per entity with per-field provenance. Bring your own vendor keys.

Pricing

from $10.50 / 1,000 contact verifieds

Rating

0.0

(0)

Developer

Mamba Labs

Mamba Labs

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

4 hours ago

Last modified

Share

Find the companies hiring for a role, work out who is actually employing them, enrich the company, and find the people to talk to. One actor, one run, flat Clay-ready rows.

Bring your own keys

This actor ships no vendor keys and marks up no vendor cost. You supply your own provider keys and each vendor bills you directly. What you pay here is the Apify per-event charge and nothing else.

You can try it with no keys at all. enrich_companies runs on the company's own website and public sources, so the prefilled input returns a real enriched record without you configuring anything. Keys unlock job discovery and email finding.

A missing key is never silently reported as an empty result. The run returns a named error saying which key is absent and how to supply it. See Vendor keys.

The thing this does that a job scraper does not: it reads the job description to decide whether the company named on the posting is the real employer or just the board that posted it. A job board, an industry association or a staffing agency posting on someone's behalf gets reported as the company by every SERP, and that attribution is wrong in a way that produces no error. This actor resolves the real employer and tells you how sure it is.

Four modes

Set mode to pick one.

enrich_companies takes domains or company names and returns one merged company record each: identity, firmographics, LinkedIn URL, social profiles, and per-field provenance. Needs no vendor key.

discover_jobs takes keywords and returns one row per posting: the real employer resolved from the job description, a signal type, an ICP score, dedupe and delta detection. Needs a SerpAPI key.

find_contacts takes domains and returns people, with an optional verified email. Needs an email finder key for the email step.

full chains all three. The originating job signal travels on every contact row, so you can write copy anchored to the posting without joining anything back together.

Inputs per mode

Everything not listed is optional and has a default. Numeric and boolean fields are supplied as strings, which is what Clay sends.

ModeRequiredThe ones worth setting
enrich_companiesdomains[] or company_names[]score_icp, icp_preset, include_social
discover_jobskeywords[], serpapi_keycountry, lookback_days, max_results, max_pages, remote_only, new_postings_only, previous_run_date, exclude_staffing
find_contactsdomains[] or company_names[]target_contacts, job_titles[], seniority[], departments[], include_email, verify_email
fullkeywords[], serpapi_keyeverything above, plus min_icp_score (default 45)

skip_cache forces a fresh enrichment. signal_taxonomy replaces the built-in signal set. extra_exclude_names[] adds to the ICP hard-exclude list.

Five things to try

1. Enrich a company, no keys needed. The prefilled input. Returns identity, firmographics, LinkedIn, socials and provenance.

{ "mode": "enrich_companies", "domains": ["stripe.com"] }

2. Enrich a list and score it against your ICP.

{ "mode": "enrich_companies", "domains": ["stripe.com", "figma.com", "linear.app"],
"score_icp": "true", "icp_preset": "proofed_editorial" }

3. Find who is hiring editors this week. Needs a SerpAPI key.

{ "mode": "discover_jobs", "keywords": ["copy editor", "proofreader"],
"country": "us", "lookback_days": "7", "max_results": "25",
"serpapi_key": "YOUR_SERPAPI_KEY" }

4. Find contacts at a company, with verified emails. Needs an email finder key and a verifier key.

{ "mode": "find_contacts", "domains": ["stripe.com"], "target_contacts": "3",
"departments": ["marketing"], "seniority": ["director", "vp"],
"include_email": "true", "verify_email": "true",
"prospeo_api_key": "YOUR_KEY", "reoon_api_key": "YOUR_KEY" }

5. The whole pipeline in one run. Discovery to enrichment to contacts, with the job signal carried onto every contact row. Bills on the chained event set, 25% below running the three modes separately.

{ "mode": "full", "keywords": ["content editor"], "country": "us",
"lookback_days": "7", "min_icp_score": "45", "target_contacts": "2",
"include_email": "true", "verify_email": "true",
"serpapi_key": "YOUR_SERPAPI_KEY", "prospeo_api_key": "YOUR_KEY",
"reoon_api_key": "YOUR_KEY" }

What comes back

One flat row shape across every mode. 64 snake_case columns, one row per entity, no nested JSON. A mode that does not populate a column leaves it null rather than dropping it, so a Clay mapping keeps working when you change mode.

The columns that matter most:

ColumnWhat it tells you
employer_namewho is actually hiring, not who posted
employer_confidence0 to 100
employer_resolution_methodhow it was decided
poster_is_employerfalse when the posting company was the board
needs_reviewtrue when the attribution is not safe to trust
signal_typewhich of the seven buying signals this posting is
icp_score and icp_tierfit against the ICP rules, A to D or excluded
company_phonethe company main line, E.164 when a country resolves
company_phone_sourcewhich node the main line came from
field_sourceswhich source supplied each field, as JSON
source_statusper-source ok, degraded, empty or skipped
data_completenesspercentage of the contract this row filled

Company main line. telephone is a standard property on the schema.org organization nodes the homepage source already parses, so the switchboard number costs no extra request, no vendor and no credit. Every JSON-LD block on the page is scanned rather than only the block the company record resolves out of, Organization subtypes such as LocalBusiness and ProfessionalService count as organization nodes, and a block that is not valid JSON is skipped rather than aborting the scan.

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. company_phone_source names the node the number came from, for example jsonld_organization or jsonld_localbusiness, and company_phone_raw holds the string exactly as published so a bad normalization is diagnosable from the row alone.

The number is normalized to E.164 using the HQ country resolved from the same address node. Where no country can be determined it is emitted exactly as published, without a leading +, so the leading + is itself the provenance. 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.

Most sites publish no telephone, so a null company_phone is the ordinary outcome rather than a failure. The field is excluded from data_completeness for that reason: counting it would drag every score down against a denominator that never applied to the scores already recorded, and this actor charges on data_completeness > 0.

The extractor is shared with Company Firmographics (mambalabs/company-firmographic-enricher) through @mambalabsdev/gtm-core/phone, so one domain cannot yield two different main lines depending on which actor you ran.

Employer resolution

The part worth understanding before you trust the output.

A posting says "National Association of Black Journalists". The description says "Southern California Edison is a proud Equal Opportunity Employer". The employer is Southern California Edison, and it is 13,700 people rather than 217, which changes who you contact and what you say.

The resolver reads the description, and the posting page when the description is truncated, looks for the patterns a company uses to name itself, and weighs them against the posting company. The equal-opportunity clause is the strongest single signal: it is legally motivated, it names the hiring entity, and a board posting on someone's behalf carries the employer's clause rather than its own.

When the evidence is not strong enough, the row is flagged needs_review instead of being attributed. That is the point. The failure mode being fixed is confident and wrong, so the fix has to be able to say it is unsure.

Overriding the posting company is deliberately hard. A company naming another company inside its own job description is completely normal, so a differing name is not on its own evidence of anything. An override needs either an explicit statement (on behalf of, our client, recruiting for) or a posting company that looks like a board, an association, a staffing agency or a government portal. A shared brand token or an acronym is read as the same corporate family, not a different employer.

Measured on 227 real postings pulled from Google Jobs on 2026-08-03:

postings where the employer was overridden1 (0.4%)
postings flagged for human review7 (3%)
review flags that were correct on a hand check7 of 7
employer confidence, median60
employer confidence, 75th percentile85
postings carrying a description over 500 characters85%

Confidence of 100 happens, but it is rare: 4 of 227. Treat 85 as a strong result and anything at or below 60 as "the description did not corroborate the posting company".

Signal types

Every posting is classified, because a company hiring a copy editor and a company hiring four content writers want different messages.

TypeWhat it means
contract_editorialalready trying to buy capacity rather than hire it
direct_editorial_hireeditorial volume exceeds current capacity
new_editorial_ownera budget holder just arrived with a mandate
regulated_high_stakesone error costs real money
content_production_rampoutput is about to rise, editing follows
localization_translationmulti-language output with a QA gap
ai_generated_contentAI content at volume, unclear who checks it

Supply signal_taxonomy to replace the set with your own. The default is editorial because that is the vertical with a live use case, not because the actor is limited to it.

ICP scoring

Configurable rules, not a hardcoded profile. The default preset encodes real drafted-versus-skipped decisions from a live outbound pipeline.

Hard excludes: government and public sector, student and campus media, clinical records work inside an EMR, staffing and recruitment platforms, and any posting whose employer cannot be resolved.

Scores up: contract, temporary and on-call editorial roles, because a company posting one is already shopping for the thing a managed service sells.

Size is not a disqualifier. Large employers score neutral and carry a buyer-check note telling you to target functional owners rather than the C-suite.

Vendor keys

This actor ships no keys and marks up no vendor cost. You supply your own and are billed by each vendor directly.

KeyNeeded for
serpapi_keydiscover_jobs and full. Note this is SerpAPI, not Serper
serper_api_keybetter contact discovery, optional
icypeas_api_key or prospeo_api_keyfinding emails
reoon_api_key or bounceban_api_keyverifying emails

Cost and speed

Everything runs in one container. There are no sub-actor calls, so you are not paying a second actor's list price on top of this one, and there are no container starts between stages.

Measured runtimes, 2026-08-03, one container, no sub-actor calls:

ModeRuntimeWork covered
discover_jobs6s6 postings, employer resolution and scoring on each
find_contacts7s1 company
enrich_companies9s2 companies, full identity and firmographics and social
full53s4 companies end to end: discovery, employer resolution, enrichment, contacts

full at 53 seconds is over the 20 to 25 second target the architecture was chosen against, so be straight about it: the target was per-company and this is four companies chained, roughly 13 seconds each. The saving over a fan-out design is real but it is a saving on container starts, not on network time, and network time is what dominates once the containers are gone.

What it costs

Pay per event, charged on output only. You are charged for a result once it carries the thing being sold, never for making a request.

EventCharged whenPrice
Company enriched, fullthe merge produced data from an external source, with signals and an ICP score$0.0800
Company enriched, basicthe same, without scoring$0.0200
Contact verifieda contact is returned with a verified deliverable email$0.0150
Job posting discovereda posting survives dedupe and resolves to an employer$0.0100

Nothing is charged for a miss. A contact found without an email is free. A company that no external source could enrich is free. A posting whose employer could not be resolved is free. A cache hit is free. A notice row is free.

Subscription tiers discount every event: Bronze 10%, Silver 20%, Gold 30%.

Cheaper than assembling it yourself

The same work bought a la carte from the individual Mamba Labs actors:

What you getHereAs separate actorsYou save
Company enriched, basic$0.0200$0.032038%
Company enriched, full$0.0800$0.177055%
Contact verified$0.0150$0.022333%
Job posting discovered$0.0100$0.020050%

Running full is cheaper again. The chained path bills its own event set at 25% below the sum of the single-mode rates, because one run doing all three stages costs less to serve than three runs doing one each. It is applied automatically. You do not opt in and there is nothing to configure.

Free tier

Free Apify plans get 10 charged results a month. Paid plans are unlimited.

When the limit is reached the run finishes normally. It does not error. It returns the results it produced, adds a row explaining the limit was reached, and sets a run status message saying the same. Nothing is charged past the limit.

Results are cached for 7 days, so re-running the same company inside that window costs nothing.

Notes

Enable Apify Proxy with the residential group. DuckDuckGo, Brave and LinkedIn all block datacenter IPs from the platform, which lowers name-to-domain and LinkedIn resolution rates without it.

Contact discovery is the least proven part of this actor. The people cascade and the email waterfall are written against each vendor's documented API and covered by tests, but they have not been run against live vendor accounts. Treat contact output as unvalidated until you have compared fill rates on your own keys.

Built by Mamba Labs