skills/capabilities/linkedin-message-writer/SKILL.md
Research LinkedIn profiles and write personalized messages for any LinkedIn message type — connection requests, InMails, DMs, message requests, post comments, and comment replies. Takes LinkedIn URLs as input, researches each person (profile data + recent posts via Apify), and generates messages tailored to each lead's background, interests, and recent activity. Exports tool-ready CSVs for Dripify, Expandi, Botdog, PhantomBuster, or generic format. No LinkedIn cookies or login required.
npx skillsauth add athina-ai/goose-skills linkedin-message-writerInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Research LinkedIn leads and write personalized messages for any LinkedIn message type. Takes LinkedIn URLs, researches each person using Apify (profile + recent posts), and writes messages based on what it finds.
No LinkedIn cookies. No database setup. Just LinkedIn URLs in, personalized messages out.
Load this skill when:
Required for researching LinkedIn profiles and posts. Set in .env:
APIFY_API_TOKEN=your_token_here
No LinkedIn cookies, login, or session tokens needed. Apify handles scraping without any LinkedIn credentials.
That's it. One env var. Nothing else.
This skill writes any text-based LinkedIn message type. Each type has different constraints.
| Message Type | Who Can Receive | Character Limit | When to Use | |-------------|----------------|-----------------|-------------| | Connection request | 2nd/3rd degree connections | 200 (free) / 300 (premium) | First touch. Must earn the accept. No selling. | | InMail | Anyone (requires premium credits) | Subject: 200, Body: 1,900 | Standalone pitch to people who won't accept cold connections. Senior execs, busy people. | | DM | 1st-degree connections only | 8,000 | Follow-ups after connection accepted. Conversational, not broadcast. | | Message request | Group members, event attendees, #OpenToWork | 8,000 | Warm context — you share a group or event. Reference the shared context. | | Post comment | Anyone (public posts) | 1,250 | Warm-up before connecting. Show you engaged with their content. Not a pitch. | | Comment reply | Anyone (in a thread) | 1,250 | Engage in a conversation they started. Add value, don't pitch. |
Connection request (200/300 chars):
InMail (subject 200 + body 1,900 chars):
DM (8,000 chars):
Message request (8,000 chars):
Post comment (1,250 chars):
Comment reply (1,250 chars):
Ask the user these questions. Skip any already answered.
Leads:
Message type: 3. What kind of LinkedIn message do you want to write? (connection request, InMail, DM, message request, post comment, comment reply, or a sequence of multiple types) 4. If connection request: do you have a free or premium LinkedIn account? (affects character limit: 200 vs 300)
Goal: 5. What's the objective? (book meetings, drive demo requests, get replies, build relationships, promote content, warm up before outreach) 6. What's the angle or hook? (pain-based, hiring signal, competitor displacement, event-based, content engagement, mutual connection, cold)
Tone: 7. Which tone? Present options:
Context: 9. What does your company/product do? (one-liner for the AI to work with) 10. Any proof points? (customer names, metrics, case studies to reference)
Output: 11. Which LinkedIn outreach tool do you use? (Dripify / Expandi / Botdog / PhantomBuster / Just give me a CSV)
Accept leads from whatever source the user provides:
linkedin_url, LinkedIn URL, LinkedIn, profile_url, url). If ambiguous, ask the user which column.Minimum required: At least one LinkedIn URL per lead.
Present the lead count to the user and confirm before proceeding to research.
Research each lead using two Apify actors. Both require only APIFY_API_TOKEN — no LinkedIn cookies.
Use harvestapi/linkedin-profile-scraper to get profile data for all leads.
API call:
curl -X POST "https://api.apify.com/v2/acts/harvestapi~linkedin-profile-scraper/runs?token=$APIFY_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"urls": [
{"url": "https://www.linkedin.com/in/PROFILE_1/"},
{"url": "https://www.linkedin.com/in/PROFILE_2/"}
]
}'
Cost: $0.003 per profile. 100 leads = $0.30.
Returns per lead:
Polling for results:
# Check run status
curl "https://api.apify.com/v2/acts/harvestapi~linkedin-profile-scraper/runs/{RUN_ID}?token=$APIFY_API_TOKEN"
# When status is SUCCEEDED, fetch results
curl "https://api.apify.com/v2/datasets/{DATASET_ID}/items?token=$APIFY_API_TOKEN"
Use harvestapi/linkedin-profile-posts to get recent posts. Run this when:
Skip this when:
API call:
curl -X POST "https://api.apify.com/v2/acts/harvestapi~linkedin-profile-posts/runs?token=$APIFY_API_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"profileUrls": [
"https://www.linkedin.com/in/PROFILE_1/",
"https://www.linkedin.com/in/PROFILE_2/"
]
}'
Cost: $0.002 per post. ~20 posts per profile = ~$0.04 per lead. 50 leads = $2.00.
Returns per post:
Polling: Same pattern as Step 1.
After research completes, present a summary table:
Leads researched: {count}
Profile data: {count} profiles retrieved
Posts scraped: {count} posts from {count} leads (or "skipped")
Research cost: ~${total}
Sample leads:
| Name | Title | Company | Recent Post Topic | Personalization Angle |
|------|-------|---------|-------------------|----------------------|
| Jane Smith | VP Sales | Acme Corp | Posted about AI in sales | Reference her AI post |
| ... | ... | ... | ... | ... |
If the user asked to filter/qualify leads, do that now based on profile data (title, company, industry, etc.) and present which leads made the cut.
Generate personalized messages for each lead based on the research.
Use the best available signal for each lead. In order of strength:
If the user provided reference messages that have worked, analyze those for tone, length, structure, and vocabulary. Use them as the template — don't override with defaults.
After generating any message, count the characters. If over the limit:
Generate a CSV with these columns:
linkedin_url, first_name, last_name, company, title, message_type, message_subject, message_body
For sequence-based campaigns (connection + follow-ups), use:
linkedin_url, first_name, last_name, company, title, connection_request, followup_1, followup_2, followup_3, inmail_subject, inmail_body
Dripify:
Profile URL, Note, Message 1, Message 2, Message 3Expandi:
LinkedIn URL, Connection message, Follow-up #1, Follow-up #2, Follow-up #3, InMail subject, InMail messageBotdog:
linkedin_profile_url, connection_note, message_1, message_2, message_3PhantomBuster:
profileUrl, messageGeneric CSV / Other:
Save to the current working directory:
{campaign-name}-{YYYY-MM-DD}.csv
Present final summary:
Campaign: {name}
Message type: {type}
Leads: {count}
Tool: {dripify/expandi/etc.}
Personalization: {profile-only / profile+posts}
Research cost: ~${amount}
Export file: {file_path}
Show 3-5 sample messages from the export for final review.
Do NOT mark as done without explicit user confirmation. Ask: "Messages look good? Anything to adjust before you import?"
After confirmation:
| Leads | Profile Only | Profile + Posts | |-------|-------------|----------------| | 10 | ~$0.03 | ~$0.43 | | 50 | ~$0.15 | ~$2.15 | | 100 | ~$0.30 | ~$4.30 | | 500 | ~$1.50 | ~$21.50 |
Profile scraper: $0.003/profile. Post scraper: ~$0.04/lead (20 posts × $0.002).
| Error | Fix |
|-------|-----|
| APIFY_API_TOKEN not set | Ask user to add it to .env |
| Apify run fails or times out | Retry once. If still fails, skip that lead and note it. |
| LinkedIn URL is invalid or profile not found | Skip the lead, report it to user |
| 0 profiles returned | Check URL format — must be full LinkedIn URL with https:// |
| Post scraper returns 0 posts | Person doesn't post publicly. Use profile data only for personalization. |
content-media
Takes an existing screen recording or demo video and adds professional zoom/pan effects synchronized to the narration. Uses transcript-driven zoom targeting and Remotion for rendering. Optionally replaces audio with a soundtrack.
tools
Repurposes long-form video (podcasts, interviews, talks) into short-form vertical clips for Instagram Reels, TikTok, and YouTube Shorts. Handles transcription, moment selection, clip extraction, speaker-tracked reframing (16:9 to 9:16), and animated captions.
development
Creates talking head videos from any source material (docs, changelogs, blog posts, notes, transcripts). Produces multi-scene videos with avatar narration over screenshots/images using HeyGen v2 API. Supports Quick Shot and Full Producer modes.
tools
Generates Instagram-ready product reels from any e-commerce product page URL. Scrapes product images, classifies by type, generates AI-animated clips via Higgsfield API, creates text overlays with style presets, and composes a 15-20 second reel with music. Supports model-based and product-only reels.