Business People Enrichment - Role Holders and Contacts avatar

Business People Enrichment - Role Holders and Contacts

Pricing

$20.00 / 1,000 person founds

Go to Apify Store
Business People Enrichment - Role Holders and Contacts

Business People Enrichment - Role Holders and Contacts

Find the owners, managers and other role holders inside an identified business, with the published evidence for each role and the professional contact details that are attributable to them. Not an employee directory.

Pricing

$20.00 / 1,000 person founds

Rating

0.0

(0)

Developer

Lead Proof

Lead Proof

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

2 days ago

Last modified

Share

Give it businesses you have already identified and the roles you care about. It finds the people who hold those roles, shows the published evidence for each role, and returns only the professional contact details a source attributes to that person.

Built for a list of businesses (from Google Maps, a supplier list, a CRM export) where you need to know who to talk to inside each one.

This is not an employee directory. It looks for the role holders you asked about. A business where nobody was found is not a business with no staff, and every row says which of the two it is.

What you get

  • People in the roles you asked for - owner, executive, purchasing, operations and more - each with the title exactly as the business published it and the page that says so.
  • Only attributable contacts. An email or phone is returned for a person only when the business publishes it against that person. No guessed addresses: nothing like first.last@domain is ever built and offered as found.
  • Business channels kept apart: main lines and function mailboxes (info@, sales@) are listed as company contacts, never as somebody's direct line.
  • An explicit account of what was not seen: pages that could not be read, limits reached, sources not consulted. An empty result is never presented as proof that nobody holds a role.
  • Ranking you can check: every person carries the comparisons that placed them. There is no opaque score.
  • One output row per input business, in input order, so your export lines up with your list.
  • Pay per person returned. Businesses where nobody was found are free from this Actor (see Pricing: the page reads are still billed by the reader it calls).

How to use it

  1. Put your businesses in businesses. Each needs a website, or a name together with a city, region or country.
  2. Choose targetRoles, for example ["owner", "executive", "purchasing"].
  3. Keep maxPeoplePerBusiness at 5 or raise it (up to 25).
  4. Set a maximum charge in the run options and maxDependencyChargeUsd for the pages and searches this Actor starts (see Pricing).
  5. Run it on a few rows first, then read people, candidates and coverage.gaps.

Input

{
"businesses": [
{"inputId": "acct-1", "name": "Example Accountants", "website": "https://accountants.example.co.uk/", "city": "Shrewsbury", "country": "GB"},
{"inputId": "dist-1", "name": "Example Industrial Supply", "city": "Salt Lake City", "region": "UT", "country": "US"}
],
"targetRoles": ["owner", "executive", "purchasing"],
"maxPeoplePerBusiness": 5,
"renderMode": "http",
"maxDependencyChargeUsd": 0.5
}
FieldMeaning
businessesOne object per business: inputId, name, website, city, region, country, mapsUrl, linkedinCompanyUrl. Up to 200 per run.
targetRolesRole families to look for. Empty means owner, founder, executive.
maxPeoplePerBusinessDefault 5, up to 25. Supported people past the limit are listed as candidates.
renderModehttp (default, cheapest), auto or browser. Use browser for sites whose team page is built by JavaScript.
verifyEmailsDefault off. On checks the direct addresses being returned for deliverability.
includeFormerDefault off, so relationships a source describes as past are excluded.
useLinkedInNot available in this release. Leave it off.
maxDependencyChargeUsdCeiling, in dollars and cents, on what the page reads and searches this run starts may charge in total.

Other limits you can lower: maxPagesPerBusiness (8), maxSearchesPerBusiness (3), maxSecondsPerBusiness (180), and the same per run.

A row needs enough to identify a business. A website, a company profile URL, or a name together with a place. A bare name is refused for that row before anything is fetched. With a name and a place but no website, the official site is searched for; if several companies carry that name, none is picked and the row returns them as businessCandidates with no people.

Roles it understands: owner, founder, executive, partner, purchasing, operations, finance, marketing, sales, hr, it, clinical, advisor, and synonyms such as procurement, buyer, supply chain, practice manager and leadership. A role name it does not model is reported in coverage.unrecognizedRequestedRoles, not approximated.

Output

One row per input business. Main fields:

FieldMeaning
statussucceeded, partial (a limit, a timeout or an unreadable source cut it short), failed or skipped.
outcomeWhy people looks the way it does: people_found, no_supported_role_holder_published, sources_were_inaccessible, stopped_at_a_limit, business_not_identified or no_source_could_be_read.
businessThe resolved company, how sure the match is, and why.
people[]fullName, associations (published rawTitle, relationship type, current or former, evidence with URL and quote), contacts with attribution and deliverability, matchedRoles, rankingReasons.
candidates[]People found but not returned, each with the reason (a role you did not ask for, the per-business limit, a contested identity, another location).
possibleDuplicateOfOn a person or candidate: other records with the same name and a compatible title that may be the same person. Nothing on the pages ties them together or tells them apart, so they are neither merged nor presented as different people.
businessContacts[]Company channels with no person attached.
coveragerolesFound, unresolvedRoles, limitsReached and gaps, each gap with a plain sentence saying what it means.
billedPeopleHow many person-found events this row was charged for.

The run page offers the full JSON, an overview CSV, and a run summary with the dependency spend.

How people are chosen and ordered

  1. The business is resolved from what you supplied. The home page is read, then the pages it links to about its people: team, about, leadership, contact, locations.
  2. People come from structured data, from person cards and from plain text, in that order of support. Only for roles the site did not answer, a bounded search runs. A search snippet is never treated as evidence of employment.
  3. Every requested role gets one person before any role gets a second; spare places then follow the order of your targetRoles.
  4. Inside a role: how directly the title names the role, then the strength of the evidence, then the seniority the title itself states (chief executive or owner, chair, other chief officers, vice presidents, directors, managers), then the name. Evidence always comes before seniority.

Seniority orders people and claims nothing more. A purchasing title is not purchase authority, and "Founder" is not read as owner or chief executive.

Contact scopes

ScopeMeaning
person_professionalThe business published this address against that person, inside their own entry. A mailbox that merely looks built from their name is never this.
branchPublished for one location.
business_generalA function mailbox or a main line.
unknownPublished, but not attributable: for example on a domain the business does not own.

Attribution and deliverability are separate fields. A mailbox that accepts mail is not proof of who reads it.

Coverage gaps

GapMeans
source_unreadableA source was blocked, timed out or failed. Nobody being found in it is not evidence that nobody is in it.
limit_reachedThe run stopped at one of its limits before the sources were exhausted.
people_withheld_by_charge_limitPeople were found but not returned because the run's maximum charge did not cover them. They were not charged.
page_needs_renderingA page is built by JavaScript, so its people were not in what was read. Re-run that business with renderMode: "browser".
page_empty_after_renderingA page was read in a browser, finished loading and was still empty, typically a guessed address such as /team that the site does not have.
no_page_about_the_peopleThe site was read and publishes no team or about page.
names_found_without_published_titlesPeople were named with no title, so they are candidates, not role holders.
no_official_site_identifiedThe company's own site was not found.
search_coverage_reducedThe search returned less than was asked of it.
profile_sources_not_usedNo professional-profile source was consulted.

coverage.absenceIsBounded is true when any gap could be hiding a role holder: on such a row, an empty people list is not a statement that nobody holds the role.

For JavaScript sites prefer renderMode: "browser" over auto: auto has been seen reading such a page over HTTP without switching to the browser.

Pricing

This Actor

$0.02 per person returned in people (event person-found). It is the only event this Actor charges. Two records that may be the same person (flagged possible_duplicate) take one place and are charged once.

Free from this Actor: candidates, company contacts, businesses where nobody was found, businesses that could not be identified, and rows that failed.

Your run's maximum charge is respected at every step:

  • A business is not started unless the remaining maximum still covers at least one more person, so no page is read and no search is run for an answer that could not be returned.
  • If a business has more people than the remaining maximum covers, the row returns only the people that were charged (the first places filled, one per requested role first), stays in the dataset as partial with charge_limit in coverage.limitsReached, and says how many were withheld. Withheld people are not charged and not listed. The run stops after that row.
  • If a run is interrupted and resumed, a row is never charged twice. A row interrupted during the charge itself is returned without being charged.

Actors this Actor calls, billed to you separately

Pages and searches are read by other Actors that run on your account and bill you at their own Store prices, in addition to the $0.02 per person:

Called ActorWhenIts price
Search & ReadEvery page read and every search$0.002 per page read over HTTP, $0.006 per page that needed a browser, $0.005 per search, plus $0.00005 per run start
Bulk Email VerifierOnly with verifyEmails: true$0.0049 per address checked

A business where nobody is found can still cost money. Its pages and searches are read and billed by Search & Read whether or not a role holder turns up. Only this Actor's $0.02 depends on people being found.

What bounds your cost

There is no typical cost to quote: it depends on how many pages each site has, whether they need a browser, how many searches run, and how many people each business publishes. The limits give hard ceilings instead, enforced in the code. Per business, the Actor never hands the reader more than maxPagesPerBusiness pages or maxSearchesPerBusiness searches, each read is asked for once, searches read no pages, and returned people never exceed maxPeoplePerBusiness. The reader charges each page and each search at most once, including its own retries and restarts, and nothing for a failed read. With the defaults, at today's prices:

Per businessUpper bound
This Actor, 5 people x $0.02$0.10
Search & Read over HTTP, 8 pages x $0.002 + 3 searches x $0.005$0.031
Search & Read in browser mode, 8 pages x $0.006 + 3 searches x $0.005$0.063
Run starts of the called Actors, at most one per page or search, 11 x $0.00005$0.00055

Email verification, when on, adds $0.0049 per address returned. Lower maxPagesPerBusiness, maxSearchesPerBusiness or maxPeoplePerBusiness to lower these. Set maxDependencyChargeUsd to cap what the called Actors may charge across the whole run; each is started with only what is left of it. Your run's maximum charge caps this Actor's own events and does not cover the called Actors. Start with a few rows and check the run summary.

What it will not tell you

  • A decision maker for every business. Many companies publish nobody. That returns no_supported_role_holder_published, with the gaps that bound it.
  • Who approves a purchase. A purchasing title names a function, not an approver.
  • That an unknown recency is current. A person on a live staff page is current; anywhere else recency stays unknown.
  • One person from two records by name alone. Two records merge only when something outside the name, such as a personal mailbox or a profile address published for exactly them, ties them together. Records whose own contacts differ are two people. The same name and title with nothing either way is flagged possible_duplicate: listed separately, one place, charged once. "Jane Q Smith" and "Jane Smith" follow the same rule.
  • A branch contact from a group page. If nothing ties a person to the location you asked about, branchRelevance stays unknown.

Fair use

Only public pages are read, with no login and nothing bypassed. The output is professional information a business chose to publish about its own staff. You are responsible for how you contact the people in it and for following the privacy and marketing rules that apply to you, such as GDPR and CAN-SPAM.

Development

Python 3.13, Apify SDK 4.0.2. python3 -m unittest discover -s test from this folder. The person and organization model is a shared library bundled at build time. The project records (HANDOFF.md, DEPLOYMENT.md, STORE_LISTING.md) are in this folder and are not part of the store page.