Developers have asked us a recurring question: can I detect what posts my prospects are liking and commenting on, and use that as a hook for cold outreach? This guide walks through a complete working example that does exactly that, end to end, from picking a target profile to writing a personalized DM for each prospect.
For a single-call snippet, see LinkedIn outreach hooks. This guide is the full pipeline.
What you will build
A five-stage pipeline that starts with a sender and a target LinkedIn profile and ends with a folder of personalized draft DMs ready for human review.
- Stage 0 — Fetch the sender’s own LinkedIn profile and summarize their voice and current company so the LLM has writing style and signature context.
- Stage 1 — Pull recent posts from the target profile (typically a high-signal author your prospects follow).
- Stage 2 — Find everyone who reacted to or commented on those posts, filter out teammates and noise, then enrich the top prospects with full profiles.
- Stage 3 — Scrape each prospect’s own recent LinkedIn activity so the DM can reference what they actually care about.
- Stage 4 — Draft a personalized DM per prospect with an LLM and render a
demo.html preview for review.
The example repo
Clone the example to follow along:
git clone https://github.com/the-hog/hog-linkedin-outreach
cd hog-linkedin-outreach
The repo is MIT licensed, a single Python file, and uses only the Python standard library. OpenAI is called via raw HTTP through urllib, not the openai package, so there is no pip install step.
Endpoints used
The pipeline uses six The Hog LinkedIn endpoints plus OpenAI for summarization and DM drafting.
| Endpoint | What it returns |
|---|
linkedin/profile | Full profile including experience, education, current company |
linkedin/profile-posts | Recent posts from a profile |
linkedin/post-reactions | Who reacted to a specific post |
linkedin/post-comments | Who commented on a specific post |
linkedin/profile-reactions | What a profile reacted to elsewhere |
linkedin/profile-comments | What a profile commented on elsewhere |
OpenAI chat.completions | Sender voice summary, prospect summaries, and final DM drafts |
The data flow
Each stage caches its output to disk so re-running the script is cheap. The cache filenames double as a tour of the pipeline:
| File | Stage | Contents |
|---|
00a_sender_profile.json | 0 | Raw sender profile from linkedin/profile |
00b_sender_summary.txt | 0 | LLM-generated voice and company summary for the sender |
01_target_posts.json | 1 | Recent posts from the target profile |
02_potential_prospects.json | 2 | Everyone who reacted to or commented on the target posts |
02b_top_prospects.json | 2 | Filtered and ranked list after exclusions |
02c_enriched_prospects.json | 2 | Top prospects with full profiles attached |
03_prospect_activity.json | 3 | Each prospect’s own recent reactions and comments |
04_personalized_messages.json | 4 | Final draft DMs |
demo.html | 4 | Browser-ready preview of every draft side by side |
The personalization payoff
Templated outreach fails because the recipient can tell it could have been sent to anyone. This pipeline gives the LLM enough specific context to write something that could only have been sent to one person.
For each DM, the model receives:
- Sender voice from the sender summary, so the draft sounds like the person sending it.
- Recipient context from the prospect’s enriched profile, so the DM grounds in their role and company.
- The specific post they engaged with, including its text and author, so the opener is a real reference.
- Their own words when they left a comment, so the DM can quote or paraphrase what they actually said.
- Their broader activity from
profile-reactions and profile-comments, so the DM can connect their public interests to your pitch.
The output is a DM that references real things the recipient said and did this week.
Filtering for safety
Two config knobs at the top of the script keep your own teammates and obvious noise out of the prospect list:
EXCLUDE_COMPANIES — drop anyone whose current company in their experience history matches one of these. This catches teammates whose LinkedIn headline does not mention the company name (a common gap that a headline-only filter misses).
EXCLUDE_HEADLINE_KEYWORDS — drop anyone whose headline contains these substrings, useful for filtering investors, recruiters, or competitors when you do not want to message them.
The experience-based filter requires the prospect enrichment step. The cheaper headline filter runs first, so the script only spends profile credits on prospects that survive the initial cut.
Costs and runtime
| Run | Hog calls | OpenAI calls | Wall time |
|---|
| First run | ~20 to 25 | ~40 | 5 to 10 minutes |
| Subsequent runs | Near zero | Near zero | Seconds |
Every stage reads from its cache file if present, so iterating on prompts, exclusion lists, or DM templates does not re-burn credits.
Customizing
Open the script and look for the CONFIG block near the top (around lines 50 to 80). The variables you will change most often:
| Variable | What it controls |
|---|
SENDER_USERNAME | Your LinkedIn username, used to fetch your profile and seed the sender voice summary |
TARGET_USERNAME | The author whose post engagement you want to mine for prospects |
EXCLUDE_COMPANIES | Company names to drop from the prospect list (catches teammates) |
MAX_PROSPECTS_FOR_OUTREACH | Cap on how many prospects make it through to the DM draft stage |
Past those, the prompt strings for the sender summary, prospect summary, and DM draft live inline and are easy to tune.
Review every drafted DM before sending. The script produces drafts, not approved messages. Respect LinkedIn’s terms of service and outreach rules in your market.