---
name: linkedin-post-engagers
description: Turn the people who reacted to or commented on a LinkedIn post into a vetted HeyReach lead list. Scrapes engagers with Apify, enriches their companies, qualifies each person against your ICP with subagents, shows the list for approval, then loads approved leads into a new HeyReach list. Use for /linkedin-post-engagers <post_url> ["List name"].
argument-hint: "<post_url>[,<post_url>...] [\"List name\"]"
allowed-tools: Read, Write, Bash, WebFetch, WebSearch, Agent, AskUserQuestion
---

# LinkedIn post engagers → HeyReach list

Given one or more LinkedIn post URLs: collect everyone who reacted or commented, look up their
companies, qualify each person against the ICP below, show the list for approval, and load the
approved people into a fresh HeyReach list. The HeyReach campaign and its messages are set up in
the HeyReach UI; this skill only builds and loads the list.

Published by Kestrel GTM (kestrelgtm.com/skills/linkedin-post-engagers).

## Setup (once)

Environment variables:
- `APIFY_TOKEN`: an Apify API token (free plan works for a few posts a month)
- `HEYREACH_API_KEY`: HeyReach → Settings → API

Edit the ICP section below before the first run. It is the whole qualify bar.

## ICP (edit this)

- Keep: **{{BUSINESS_MODEL}}** companies (e.g. B2B SaaS) × personas **{{PERSONAS}}** (e.g. founder,
  GTM leader, exec).
- Drop: students, open-to-work, recruiters, individual contributors outside the personas, agencies
  or consultants selling what you sell, B2C.
- **Drop competitors**: companies whose product overlaps yours: **{{COMPETITOR_CATEGORIES}}**. They pass
  every firmographic filter, so check explicitly.

## Apify calls (async: start, poll, fetch)

Actors (harvestapi): `harvestapi~linkedin-post-comments`, `harvestapi~linkedin-post-reactions`,
`harvestapi~linkedin-company`.

```bash
# start a run
RUN=$(curl -s -X POST "https://api.apify.com/v2/acts/harvestapi~linkedin-post-comments/runs?token=$APIFY_TOKEN" \
  -H 'content-type: application/json' \
  -d '{"posts":["<post_url>"],"maxItems":0,"profileScraperMode":"main"}' | jq -r .data.id)
# poll until SUCCEEDED
curl -s "https://api.apify.com/v2/actor-runs/$RUN?token=$APIFY_TOKEN" | jq -r .data.status
# fetch results
DS=$(curl -s "https://api.apify.com/v2/actor-runs/$RUN?token=$APIFY_TOKEN" | jq -r .data.defaultDatasetId)
curl -s "https://api.apify.com/v2/datasets/$DS/items?token=$APIFY_TOKEN&clean=true" > comments.json
```

Same pattern for reactions (same input) and companies (`{"companies":["<company_url>", ...]}`).
Always use the async runs endpoint: viral posts take longer than the synchronous endpoint allows.
Output shapes vary; read the person from `actor`, `author` or `profile`, whichever exists.

## HeyReach calls (POST only, header `X-API-KEY`)

```bash
H="https://api.heyreach.io/api/public"
# create a list (name max 50 characters)
curl -s -X POST "$H/list/CreateEmptyList" -H "X-API-KEY: $HEYREACH_API_KEY" -H 'content-type: application/json' \
  -d '{"name":"<List name>"}'
# read the leads already in a list (paginate with offset until a page comes back short)
curl -s -X POST "$H/list/GetLeadsFromList" -H "X-API-KEY: $HEYREACH_API_KEY" -H 'content-type: application/json' \
  -d '{"listId":123,"offset":0,"limit":100}'
# add leads (max 100 per request). The URL field is profileUrl, NOT linkedInUrl.
curl -s -X POST "$H/list/AddLeadsToListV2" -H "X-API-KEY: $HEYREACH_API_KEY" -H 'content-type: application/json' \
  -d '{"listId":123,"leads":[{"profileUrl":"https://www.linkedin.com/in/...","firstName":"","lastName":"","position":"","companyName":"","location":""}]}'
```

Check `addedLeadsCount` and `failedLeadsCount` in the response. `0/0/0` means the leads were not
parsed at all (usually the wrong URL field).

## Steps

1. **Parse args.** Post URL(s) and an optional list name (default:
   `Engagement - <source> - <YYYY-MM-DD>`, trimmed to 50 characters) or the id of an existing
   HeyReach list to add to. Check both env vars exist.
2. **Scrape engagers.** Comments and reactions for every post. Merge into one record per person by
   LinkedIn URL. Someone who commented and reacted is one person, kept as a comment (higher intent,
   and keep the comment text).
3. **Cheap noise cut.** Before any reasoning, drop open-to-work, students, interns, recruiters,
   company pages and titles clearly outside the personas. When unsure, keep.
4. **Enrich companies.** One company scrape per unique company URL (cache repeats): industry,
   about, specialties, headcount, domain.
5. **Qualify with subagents.** Batches of 15 to 25 people per subagent. Each returns, per person:
   `{ linkedin_url, persona, fits_business_model, is_competitor, confidence, reach_out, reasoning }`.
   Persona is judgment, not string matching. `reach_out` is true only for a matching persona at a
   matching company that is not a competitor. Web lookup only when company data is thin. Keep one
   output file per batch and join results by position, not by URL: agents sometimes rewrite URLs.
6. **Dedupe.** Drop anyone already loaded before, so overlapping posts do not re-add the same
   people. Check every reach-out and borderline person against `heyreach-loaded.csv` (every profile
   URL this skill has loaded before) and, when you pass the id of an existing HeyReach list, against
   the leads already in it (`list/GetLeadsFromList`). Compare normalized URLs (lowercase, no
   trailing slash or query string). Note who was dropped.
7. **Review gate.** Show two lists, commenters first: **Reach out** (confidence ≥ 0.75) and
   **Borderline** (0.60 to 0.75), each with name, title, company, persona, one-line reason and the
   comment if there is one. Add a tally: scraped → after noise cut → qualified → after dedupe →
   reach out. Ask for approval (AskUserQuestion). Never load anything without an explicit yes.
8. **Load.** Create the HeyReach list (or use the existing list id you passed) and add the approved
   leads in batches of 100. Append every added profile URL to `heyreach-loaded.csv`. Report the list
   name, id, added and failed counts, so it can be attached to a campaign in HeyReach.

## Notes

- Pick posts whose engagers are your buyers, not your peers. A post by an influencer in your own
  field is mostly peers, agencies and competitors.
- Stay within LinkedIn's limits: roughly 25 to 30 connection requests per day per sender.
- Outreach should open on the topic of the post, never on the fact that they engaged with it.
