People Enrichment & Search (People Data Labs)

SkillSearch

Enrich and search People Data Labs (PDL) Person and Company data, writing results to .xlsx. Five operations: enrich named PEOPLE into a profile, employment history and LinkedIn URL; IDENTIFY an ambiguous person as several scored candidates; SEARCH people by criteria (company, title, location); enrich COMPANIES into firmographics (industry, size, employees, HQ, founded, LinkedIn); and SEARCH companies by criteria. Use when the user has a list of names or companies and wants their job, employer, work history, LinkedIn URLs or company profiles looked up, or wants to find people or companies matching criteria. Trigger on phrases like "enrich these contacts", "find their LinkedIn", "look up these people", "who works at X", "find directors at Y", "enrich these companies", or "get firmographics for". Do not scrape LinkedIn directly — this uses a licensed data aggregator (PDL) instead, for legal and reliability reasons.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the People Enrichment & Search (People Data Labs) skill

What this skill tells your AI

The instructions your AI receives, as published by moonlight-lupin/agent-skills in research/people-enrichment/SKILL.md and read by ahel’s review.

One script, scripts/enrich.py, with five subcommands over PDL's Person and Company datasets. All share the same API-key handling, the boolean-PII contact logic, status flags, .xlsx styling, and a --dry-run preflight mode.

SubcommandInputOutputBilling
person-enrichlist of named peopleprofile + work history1 credit / match
person-identifylist of people (ambiguous)several scored candidates eachper PDL identify terms
person-searchcriteria flagsa list of matching people1 credit / record returned
company-enrichlist of companiesfirmographics1 credit / match
company-searchcriteria flagsa list of matching companies1 credit / record returned

The two search commands return data per result, so cost scales with --size. Always state the rough cost before a large search and keep --size modest unless the user asks for more (max 100 per request).

Scope and routing

Use this skill when the user wants PDL person/company enrichment or search for a legitimate, proportionate purpose. Do not use it to scrape LinkedIn directly, to compile an intrusive profile, or where a host environment mandates a different data provider.

Why People Data Labs, not a LinkedIn scraper

Scraping LinkedIn is fragile and legally risky. This skill uses PDL as a licensed aggregator rather than live LinkedIn scraping. If the user insists on live scraping, explain the trade-off rather than building a scraper.

The API key

scripts/enrich.py needs a PDL key, resolved at runtime in this order: PDL_API_KEY env var → .env in the cwd → .env next to the script. The key must never be written into this skill or any script. If absent, live commands exit with instructions. --dry-run and --self-test do not require a key.

Create the .env only locally:

echo 'PDL_API_KEY=their_key_here' > .env

Treat .env as a secret: don't print, commit, or upload it. --self-test runs offline (canned data, no key) and writes sample people + company sheets so the user can see the output format before spending credits.

Dry-run preflight

Before spending credits, especially on larger files or searches, run the same command with --dry-run:

python scripts/enrich.py person-enrich --input people.csv --output out.xlsx --dry-run
python scripts/enrich.py person-search --company "Northwind Capital" --title director --size 25 --dry-run
python scripts/enrich.py company-search --industry "real estate" --country singapore --size 50 --dry-run

Dry-run validates the input/search, prints the planned output, and estimates maximum credits without requiring PDL_API_KEY, calling PDL, or writing .xlsx output.

Contact fields and the free plan

On the free plan, PDL returns PII fields (emails, phone_numbers) as a boolean, not the value: true = a contact exists but is paywalled, false = none on file. The people sheets surface this in Email status / Phone status columns: included (real value present, Pro plans), exists - upgrade to view (paywalled but present), none on file, or unknown.

The five commands

person-enrich — one-to-one match named people.

python scripts/enrich.py person-enrich --input people.csv --output out.xlsx

Recognised input columns (case/space-insensitive): name/full name, first name, last name, company/employer, title, location, email, linkedin/profile. More context per row = higher match rate.

person-identify — when a single enrich is ambiguous, get the candidate set.

python scripts/enrich.py person-identify --input people.csv --max-candidates 5

Same input as enrich. Output has multiple rows per input person, each a scored candidate (Match score column), so the user can pick the right one.

person-search — find people by criteria, no name list needed.

python scripts/enrich.py person-search --company "Northwind Capital" --title director --size 25

Flags: --company --title --location --country --industry --name, plus --size (1–100), --dataset (default all), and --sql to pass a raw PDL SQL query for full control. Flags are combined with AND.

company-enrich — match a list of companies to firmographics.

python scripts/enrich.py company-enrich --input companies.csv --output firms.xlsx

Recognised input columns: name/company, website/domain, ticker, linkedin/profile, location/country/region/locality. Needs at least one of name/website/ticker/profile per row.

company-search — find companies by criteria.

python scripts/enrich.py company-search --industry "real estate" --country singapore --min-employees 50 --size 50

Flags: --name --industry --country --locality --tag --min-employees, plus --size and --sql.

Reviewing output with the user

People sheets have two tabs: People (one row per person, status colour-coded) and Employment history (one row per past role). Company sheets have a single Companies tab. The Status column is the first thing to check: matched (green), needs_review (amber), no_match (red), error (orange). Call out the amber/red rows explicitly so the user knows what to double-check.

Tuning matches

--min-likelihood N (1–10) is the confidence floor for the enrich/identify commands. Raise it (6–8) when names/companies are common and you'd rather miss than mis-match; lower it when you have rich context and want coverage. Company enrichment is most reliable with a website/domain; person enrichment with a company or email alongside the name.

Swapping providers later

The PDL-specific pieces are the *_params builders, pdl_request, and the parse_person / parse_company mappers. To support another provider, implement those against its API and keep the same record dict shape; input parsing, status logic, SQL/flag handling, dry-run summaries, and .xlsx writing stay as is.

Principles

  • Drafts, not advice — output is a research aid for a person to review, not a determination.
  • Never invent — surface only what PDL returns; mark no_match/needs_review honestly rather than guessing an identity.
  • Deterministic where it counts — input parsing, status logic, dry-run estimates and .xlsx writing are deterministic.
  • Honesty and calibration — flag low-confidence matches and show the rough credit cost up front.
  • Workspace hygiene — write outputs where the user expects; never write the API key to disk in the skill.

Data handling

PDL is a third party and enrichment sends the real name/company to it — it cannot be tokenised, because the name is the lookup. Treat the .env / PDL_API_KEY as a secret: never print, commit or upload it. For people, confirm a legitimate and proportionate purpose and collect only fields relevant to that purpose.

Pitfalls

  1. Free plan hides contact values — emails/phones come back as booleans; read the status columns.
  2. Search cost scales with --size — quote the rough credit cost and keep --size modest.
  3. Common names mis-match — raise --min-likelihood or add context columns (company/email).
  4. Never commit .env — the key is a secret; --self-test and --dry-run need no key.
  5. Dry-run is an estimate — actual billing follows PDL's endpoint terms and returned records/matches.

Verification checklist

  • Legitimate, proportionate purpose confirmed for any person enrichment/search.
  • --dry-run used for large files or searches to validate scope and estimate credits.
  • PDL_API_KEY available via env or .env for live runs (never written into the skill).
  • Rough credit cost stated before any large search.
  • Amber/red status rows flagged to the user.
  • .env not printed/committed.

Requirements

  • Python 3.8+
  • pip install openpyxl (HTTP uses the stdlib urllib — no requests needed)
  • PDL_API_KEY (env var or .env) for live API calls; get one at https://www.peopledatalabs.com
  • Network access to the PDL API. --self-test and --dry-run run fully offline.

Signals

GitHub stars
65
Forks
11
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
people-enrichment
Source
github.com/moonlight-lupin/agent-skills