keinsaasforever/gtm-pipeline-skills/gtm-people-search
Claude Code and agent skill by keinsaasforever/gtm-pipeline-skills
- community
Checking that the SKILL.md file is still in the repository.
Find contacts at specific companies or by persona. Use when you have a company list and need contacts with specific roles (company mode), or when building a persona-based prospect list without a company list (persona mode). Also triggers on "find contacts", "search for people at [company]", "find [role] contacts", "people search".
Install
npx vskill@latest install keinsaasforever/gtm-pipeline-skills/gtm-people-searchSKILL.md
People Search
Find contacts at specific companies or by persona. Returns a CSV with LinkedIn profile URLs, ready for enrichment.
Read `~/.claude/skills/gtm-pipeline/_shared/conventions.md` before executing.
When to Use
- You have a company list and need contacts with specific roles
- You need to build a persona-based prospect list (no company list)
- A demo was triggered and you need ~10 contacts matching a prompt
Inputs
| Input | Required | Source | |
|---|---|---|---|
| Company list (CSV with names + domains) | For company mode | Company Search / Company Enrichment output, or user-provided | |
| Target roles / job titles | Yes | User prompt or ICP definition | |
| Location filter | Recommended | User prompt or ICP definition | |
| Search mode | Yes | company (have company list) or persona (no company list) |
If company domains are missing, use SerpAPI domain lookup first (see Step 0 below).
Step 0: Domain Lookup (SerpAPI) — When Domains Are Missing
Most people-search APIs require domains, not LinkedIn URLs.
Extract domain:
Spot-check all domains before using — SerpAPI returns wrong results for generic names and global brands.
Env var: SERPAPI_API_KEY
Provider Selection
Company mode (have company list)
Finder cadence (segment-routed waterfall): FullEnrich Finder → BetterContact Lead Finder → Pipe0 searches (`amplemarket@2` → `crustdata@3` → `parallel@1`, last resort). Max 2 attempts per source (e.g. a title-token query then a name-only query), then fall through. Lead with FullEnrich for SME / owner-led / non-English-market segments (better indexed there); lead with BetterContact for broader / English-market / larger companies. Add a Pipe0 pass on whatever the finders missed — different index, additive coverage. See conventions "People-Source Cadence" for the full waterfall and fallback rule; the table below is per-provider mechanics and cost, not a fixed running order.
| Priority | Provider | Cost | LinkedIn URLs | Key Strength | |
|---|---|---|---|---|---|
| 1st | BetterContact Lead Finder | 0.10 cr/request | Yes | Cheaper fixed cost; good for high-volume batches | |
| 2nd | FullEnrich Finder | 0.25 cr/person | Yes | Richest filters; richer fields (seniority, headcount, industry) | |
| 3rd | Pipe0 `amplemarket@2` | 3.00 cr/page, flat (1 page = 100 records) | Yes | Cheap waterfall for FE/BC misses — separate index covers different shops (+36% shop coverage on DACH SMEs where FE had 0 hits). Company domain + name filters. Always ask for limit: 100 — 25 costs the same | |
| 4th | Pipe0 `crustdata@3` | 0.15 cr per result returned (limit: 25 → 3.75 worst case) | Yes | Richest filter set (29). Low additive value when run after amplemarket (+1 shop out of 81 in DACH test) | |
| 5th | Pipe0 `parallel@1` | 0.50 cr flat | Yes | AI entity search — no filters at all, every criterion goes in the objective prose. Use when the structured searches return 0 | |
| 6th | PhantomBuster Company Employees Export | Free (LinkedIn account) | Yes | Good when SN not available; scrapes directly from LinkedIn company page | |
| 7th | PhantomBuster SN Search Export | Free (SN account) | Yes | Full SN search power; use when you have a saved SN search URL |
Indexes barely overlap on small EU SMEs — running FE → amplemarket@2 → crustdata@3 is additive, not redundant. In an April 2026 DACH e-commerce run (81 FE-missed shops): amplemarket recovered 29 shops (74 contacts, 35%), crustdata added 1 more shop as fallback. Amplemarket stays first: 3.00 flat for up to 100 rows beats crustdata's 3.75 worst case for 25.
Two-tier search pattern (proven on EU e-commerce campaigns):
- Tier 1 — E-commerce + Marketing titles (always run)
- Tier 2 — Leadership titles (CEO/MD/Founder) — only if Tier 1 returns 0 contacts AND company traffic ≤ 200K visits/month (large shops likely have dedicated e-comm/marketing staff indexed by FE)
Run the same two-tier logic on whichever finder leads the cadence for the segment (FE or BC). BC cost is per-request regardless of results; FE cost is per-person returned.
When PhantomBuster is selected: read _shared/phantombuster.md and use the /phantombuster skill to generate the script. Phantom scripts: "LinkedIn Company Employees Export" (config key PB_AGENT_EMPLOYEES) and "Sales Navigator Search Export" (config key PB_AGENT_SN_SEARCH).
Persona mode (no company list)
| Priority | Provider | Cost | Key Strength | |
|---|---|---|---|---|
| 1st | Parallel FindAll | varies by processor | Discover people matching criteria from web sources | |
| 2nd | BetterContact Search | TBD | Search without company filter | |
| 3rd | Pipe0 `crustdata@3` | 0.15 cr/result | Richest persona filters (29): headcount brackets, industries, seniority, tenure, skills, education, career movement | |
| 4th | Pipe0 `amplemarket@2` | 3.00 cr/page | Structured filters (21): location, headcount, LinkedIn industry, employer revenue, founded year, departments, seniority, funding, follower level, b2b/b2c | |
| 5th | Pipe0 `parallel@1` | 0.50 cr | Prose criteria that no filter expresses (keywords, signals, free-text ICP brief) |
Persona mode has no company anchor, so an unfiltered search is a fully-billed market-wide dump. Never send a persona search without both a title list and at least one resolved region — the repo's builder raises on either being empty (see Provider E).
Do NOT use Parallel Task enrichment for people — model guesses titles, returns wrong roles. Use FindAll.
Manual / one-time
Both BetterContact and FullEnrich dashboards offer free search without API credits.
Execution Protocol
1. Sandbox / Docs Check
- Verify request/response structure with 1 record, zero cost
- For Pipe0: use
"environment": "sandbox" - For FE/BC: review docs or test via dashboard
2. Test Batch (15 companies or 15 records)
- Run chosen provider on 15 companies in production
- Print and review every row: company names, contact names, job titles, locations, LinkedIn URLs
- Check for global domain contamination (BC: add
lead_locationfilter for.comdomains) - Assess hit rate, data completeness, relevance
3. Review with User
- Present test results with hit rate and sample rows
- Flag issues (wrong country, irrelevant titles, missing LinkedIn URLs)
- Suggest improvements or provider switch if results are poor
- Get approval before full run
4. Full Run
- Submit remaining companies (skip test batch)
- Save request IDs to file immediately (recovery if crash)
- Poll with sufficient timeout
- Save results incrementally to CSV after each batch
5. Consolidation
- Merge test + full run results
- Deduplicate by LinkedIn URL (fallback: name + company)
- Clean names/titles (
.title(), strip whitespace) - Add
sourcecolumn - Write to
csv/intermediate/contacts_found.csv
Provider A: FullEnrich Finder
Endpoint: POST https://app.fullenrich.com/api/v2/people/search Auth: Authorization: Bearer $FULLENRICH_API_KEY Cloudflare: call with curl + a browser User-Agent — Python requests/urllib get blocked (error 1010). Never ship a urllib-based provider script. See conventions rule #8. Domain-keyed index: the Finder is keyed on company domain and misses orgs whose domain differs from the indexed one — prefer name-based search (current_company_names + location) over the domain filter; validate hits by domain-root or name token.
Request
Available Filters
| Filter | Type | Example | |
|---|---|---|---|
current_company_names | object[] | {"value": "Anthropic", "exact_match": true} | |
current_company_domains | object[] | {"value": "google.com", "exact_match": true} | |
current_company_linkedin_urls | object[] | LinkedIn company URL | |
current_company_industries | object[] | "Software Development" | |
current_company_types | object[] | "Public Company", "Privately Held" | |
current_company_headquarters | object[] | "San Francisco" | |
current_company_headcounts | object[] | {"min": 50, "max": 200} | |
current_company_founded_years | object[] | {"min": 2020, "max": 2024} | |
current_position_titles | object[] | "Chief Technology Officer" | |
current_position_seniority_level | object[] | "Director", "VP", "C-level" | |
past_position_titles | object[] | Past job title | |
past_company_names / domains | object[] | Previous employer | |
person_names | object[] | "John Smith" | |
person_linkedin_urls | object[] | Direct LinkedIn URL lookup | |
person_locations | object[] | "South Africa", "California" | |
person_skills | object[] | "JavaScript", "Project Management" | |
current_position_years_in | object[] | {"min": 0, "max": 1} (new in role) | |
current_company_years_at | object[] | {"min": 2, "max": 5} (tenure) | |
person_universities | object[] | "Stanford University" | |
current_company_days_since_last_job_change | object[] | {"min": 0, "max": 90} (recent hires) |
All filters support exclude: true for negative matching. Multiple filters within same field = AND logic.
Pagination: offset + limit (max 100/page, max offset 10,000). Beyond 10k: use search_after cursor.
Response
Response fields are nested — current_position_title and linkedin_url do NOT exist at top level.
⚠ LinkedIn URL path gotcha: the URL lives under social_profiles.professional_network.url, NOT social_profiles.linkedin.url. Older notes/snippets used .linkedin.url and silently returned empty strings for every row. Always parse professional_network first.
Also: the seniority field is seniority (not seniority_level) at employment.current.seniority. Values include Manager, Senior, Head, Director, VP, C-Level.
This is the start of the file. Read the full SKILL.md on GitHub.
Security
- Security scanPassed, 100 of 100Pattern checks on the skill's files.
- Model reviewPassed
Quality evaluation
- Task evaluationNot runNo evaluation yet. Run one in Skill Studio.
Source
- SKILL.md in the repositoryCheckingskills/gtm-people-search/SKILL.md
Popularity
- Repository stars
- 62
- Repository forks
- 6
- 7-day trend score
- 21
Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.
Works with
Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.
- Claude Code
- Cursor
- GitHub Copilot
- Windsurf
- Codex
- Gemini CLI
- Cline
- Amp
- Roo Code
- Goose
- Continue
- Kilo Code
- Junie
- Trae
- OpenHands
- Qwen Code
- AI Agent
- Tabnine Chat
- Sourcegraph Cody
- Amazon Q
- Firebase Genkit
- Sweep
- Devin
- Phind
- Mentat
- Claude Desktop
- Aider
- GPT Pilot
- Plandex
- bolt.new
- v0
- Replit Agent
- Anse
- TypingMind
- BetterChatGPT
- LibreChat
- Warp AI
- Pieces
- Fig AI
Embed badge
[](https://verified-skill.com/skills/keinsaasforever/gtm-pipeline-skills/gtm-people-search)