Skip to main content
The people liking and commenting on your competitors’ LinkedIn content are the hottest prospects you’re not calling. They already have category awareness, budget signals, and vendor curiosity — the three things you normally spend the first three discovery calls building. This recipe scrapes the last N months of posts from competitor company pages and their top employees, captures every reaction and comment, filters to senior buyers (VP/CXO/Director/Sales/Marketing), and outputs a ranked engagement dataset with an HTML dashboard. Validated output (3-company run):
  • 25 LinkedIn profiles → 268 posts → 209 high-engagement posts → 8,855 total engagements, 4,131 senior (49%)
  • Run time: ~45–60 minutes end to end
  • Cost: ~$20–30 Apify + ~75–150 Deepline credits

When to use

  • Building a prospect list for a competitive displacement campaign
  • Identifying accounts already evaluating your category
  • Finding champions at companies you haven’t penetrated yet
  • Researching who your competitors’ most engaged buyers are before a QBR

What you get

OutputDescription
all_engagements.csvEvery reaction + comment with full engager profile
senior_engagements.csvFiltered to VP / CXO / Director / Sales / Marketing
dashboard.htmlSelf-contained HTML dashboard — open in browser, shareable
competitive_analysis.zipAll three files packaged for sharing
The multi-company engagers (people who engaged with 2+ competitors) are highlighted in the dashboard — they are the highest-intent prospects in the output.

How it works

Why parallel batching? Scraping reactions inline times out every time. The workflow fires async Apify runs in groups of 12, collects dataset IDs as they complete, then downloads all datasets in parallel. This is what makes a 200-post run feasible in under an hour.

Invoke in Claude Code

The simplest way to run this — just paste your competitors and go:
/deepline-gtm Run competitive social listening on these competitors: Acme (https://www.linkedin.com/company/acme/ acme.com), Rival (https://www.linkedin.com/company/rival/ rival.com). Last 3 months, top 10 employees per company. Save results to /tmp/comp_run.
Claude Code will use the competitive-social-listening skill to handle the full pipeline — employee discovery, post scraping, parallel reaction batching, senior filtering, and dashboard generation.

Run the script directly

For full runs or to resume a partial run, use the e2e script:
Install prerequisites
# Deepline CLI (if not already installed)
curl -fsSL "https://deepline.com/api/v2/sdk/install" | bash

# Start the backend before any tool calls
deepline backend start
deepline billing balance   # check credits before starting
Estimate cost before running
python3 ~/.claude/skills/competitive-social-listening/scripts/competitive_social_listening.py \
  --companies "Acme:https://www.linkedin.com/company/acme/:acme.com" \
  --output /tmp/comp_estimate \
  --estimate-only
# Prints: URLs, posts, batches, estimated cost, Deepline credits
Full run
export WORKDIR=/tmp/comp_$(date +%Y%m%d)

python3 ~/.claude/skills/competitive-social-listening/scripts/competitive_social_listening.py \
  --companies "Acme:https://www.linkedin.com/company/acme/:acme.com" \
              "Rival:https://www.linkedin.com/company/rival/:rival.com" \
  --output $WORKDIR \
  --months 3 \
  --employees 10

# Test mode (1 company, 2 employees, 5 posts — no cost)
python3 ~/.claude/skills/competitive-social-listening/scripts/competitive_social_listening.py \
  --test --output /tmp/comp_test
View results
# Open dashboard in browser
open $WORKDIR/dashboard.html

# Preview senior engagements
deepline csv show --csv $WORKDIR/senior_engagements.csv --rows 0:5

# Launch in Playground for filtering + export
deepline csv render start --csv $WORKDIR/senior_engagements.csv --open

Input format

Create a seed CSV or pass companies inline using Name:LinkedInURL:domain format:
competitors.csv
company_name,company_linkedin,domain
Acme,https://www.linkedin.com/company/acme/,acme.com
Rival,https://www.linkedin.com/company/rival/,rival.com
If you don’t know a competitor’s domain, resolve it via Apollo:
deepline tools execute apollo_organization_search \
  --payload '{"q_organization_name": "Acme Corp", "per_page": 1}'
# Look for .data.organizations[0].primary_domain

Output schema

Both CSVs share the same columns:
ColumnDescription
companyCompetitor company name
post_urlLinkedIn post URL
post_authorName of the post author
post_dateWhen the post was published
post_total_reactionsTotal reaction count on the post
engagement_typereaction or comment
reaction_typeLike, Celebrate, Insightful, etc.
comment_textFull comment text (if engagement_type = comment)
engager_nameFull name of the person who engaged
engager_linkedin_urlTheir LinkedIn profile URL
engager_positionCurrent job title
engager_title_categoryC-Suite/Exec, VP, Director, Sales, or Marketing
is_seniortrue / false

Cost guide

Run this estimate before starting. Always pilot one batch first.
CompaniesEmployees eachEstimated costDeepline credits
15~$5–8~25–40
310~$20–30~75–150
515~$40–60~150–300
deepline billing balance   # check before every run

Known pitfalls

| # | Symptom | Fix | | --- | -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | --------------- | | 1 | 0 employees returned from Dropleads | Use filters.companyDomains, filters.seniority, pagination.limit (dot notation, not nested objects) | | 2 | Dropleads JSON parse fails | Response is raw text + CSV path — use re.search(r'(/tmp/\S+\.csv)', output) to extract the path | | 3 | 0 posts parsed | Field is linkedinUrl not postUrl; engagement is engagement["likes"] not totalReactions | | 4 | Posts actor 422 error | postedLimit only accepts "month", "3months", "6months", "year" | | 5 | Employee actor returns empty | harvestapi/linkedin-company-employees is broken — use dropleads_search_people only | | 6 | Reaction batch exit code 4 | Use async apify_run_actor (not sync) — capture defaultDatasetId, download separately | | 7 | 2–3 batches timeout when running >15 parallel | Cap at 12 parallel; retry timed-out batches solo (they usually complete on retry in ~15s) | | 8 | Viral posts (>300 reactions) always timeout | Separate batch: maxReactions: 50, batch size 5 instead of 10 | | 9 | JSONDecodeError on dataset files | Deepline CLI prepends a status header — skip to first { or [ with re.search(r'(\{ | \[)', content) | | 10 | Can’t map posts back to companies | source_url is always empty in Apify output — extract handle from linkedinUrl and match against all_urls.csv | | 11 | SyntaxError: single '}' not allowed in dashboard | JS braces inside Python f-string — use __DATA_JSON__ placeholder + .replace() | | 12 | _csv.Error: field larger than field limit | Don’t route Apify data through deepline enrich CSV cells — download datasets directly with apify_get_dataset_items |

What to do with the output

Highest-intent segment: multi-competitor engagers Filter senior_engagements.csv for engagers who appear against 2+ competitors — these people are actively evaluating the category.
# In Playground
deepline csv render start --csv /tmp/comp_run/senior_engagements.csv --open
# Filter: engager_name appears in 2+ rows with different company values
Suggested outreach angle:
“Noticed you’ve been following what [Competitor A] and [Competitor B] are building in [category]. We take a different approach — [one sentence differentiator]. Worth a quick conversation?”
Next steps after export:
  1. Run waterfall email enrichment on senior_engagements.csv to get work emails
  2. Push to HubSpot/Salesforce via MCP as a new “Competitive Engagement” list
  3. Add to an Instantly / Lemlist sequence with the competitive displacement template above
# Enrich emails on senior engagements
deepline enrich --input /tmp/comp_run/senior_engagements.csv \
  --with '{"alias":"work_email","tool":"person-linkedin-to-email","payload":{"linkedin_url":"{{engager_linkedin_url}}"}}'