
Social Content
- 99 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Social Content is a Claude skill for creating platform-native social media content across LinkedIn, Twitter/X, Instagram, TikTok and Facebook, with hook formulas, content pillars and repurposing workflows.
About
Social Content is a framework for creating platform-native social media content across LinkedIn, Twitter/X, Instagram, TikTok, Facebook and YouTube. It provides platform-specific format rules, a hook formula library, a content-pillar system, a content-calendar framework, repurposing workflows, engagement strategy and analytics guidance. Marketers use it to write posts and threads, plan social calendars, and repurpose content across platforms while following each platform's best practices.
- Creates platform-native content for LinkedIn, Twitter/X, Instagram, TikTok and Facebook
- Includes a content-pillar system, hook formula library and repurposing workflows
- Provides per-platform format rules, posting frequencies and best times
Social Content by the numbers
- 99 all-time installs (skills.sh)
- Ranked #445 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
social-content capabilities & compatibility
Free; a content-creation guidance framework, no API keys.
- Works with
- Use cases
- copywriting · marketing
- Pricing
- Free
What social-content says it does
Create engaging social media content across LinkedIn, Twitter/X, Instagram, TikTok, and Facebook with platform-specific formats, hook formulas, content pillar systems, repurposing workflows, engag
Platform-native social media content creation, scheduling, and optimization for building audience, driving engagement, and supporting business goals.
Choose 3-5 pillars that sit at the intersection of:
npx skills add https://github.com/borghei/claude-skills --skill social-contentAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 99 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Create platform-native social posts and calendars across LinkedIn, X, Instagram, TikTok and Facebook.
Who is it for?
Marketers and creators writing social posts and threads, planning calendars, and repurposing content across platforms.
When should I use this skill?
You are creating social posts, planning a social calendar, writing a LinkedIn post or Twitter thread, or repurposing content.
What you get
Platform-appropriate posts, a content calendar, and repurposing workflows aligned to each platform's best practices.
- Platform-specific posts
- Content calendar
- Content pillar plan
By the numbers
- 6 platforms covered (LinkedIn, Twitter/X, Instagram, TikTok, Facebook, YouTube)
- 3-5 content pillars recommended
Files
Social Content
Platform-native social media content creation, scheduling, and optimization for building audience, driving engagement, and supporting business goals.
---
Table of Contents
- Keywords
- Quick Start
- Platform Reference Guide
- Content Pillar System
- Hook Formula Library
- Platform-Specific Formats
- Content Calendar Framework
- Repurposing System
- Engagement Strategy
- Analytics and Optimization
- Best Practices
- Integration Points
---
Keywords
social content, social media, LinkedIn post, Twitter thread, Instagram carousel, TikTok content, social calendar, engagement strategy, content pillar, hook formulas, viral content, social scheduling, platform-specific content, social media strategy, content repurposing, social analytics, audience building, personal brand, company brand, social media optimization
---
Quick Start
Create a Social Post
1. Choose the platform and format 2. Select a hook from the formula library 3. Write the body following platform-specific guidelines 4. Add a CTA aligned with your goal (engagement, traffic, conversion) 5. Include relevant hashtags per platform best practices 6. Schedule or post at optimal time for your audience
Build a Content Calendar
1. Define 3-5 content pillars aligned with expertise and audience needs 2. Assign percentage allocation to each pillar 3. Map pillar topics to platform-specific formats 4. Build a weekly template with assigned slots 5. Batch create content (2-3 hours per week) 6. Schedule with room for real-time/reactive posts
---
Platform Reference Guide
Platform Quick Comparison
| Platform | Best For | Posting Frequency | Top Format | Audience |
|---|---|---|---|---|
| B2B, thought leadership | 3-5x/week | Carousels, text posts | Professionals, decision-makers | |
| Twitter/X | Tech, real-time, community | 3-10x/day | Threads, hot takes | Tech, media, creators |
| Visual brands, lifestyle, B2C | 1-2 posts + Stories daily | Reels, carousels | 18-44, visual-first audiences | |
| TikTok | Brand awareness, younger demos | 1-4x/day | Short-form video | 18-34, entertainment-first |
| Communities, local, older demos | 1-2x/day | Groups, native video | 30-65, community-oriented | |
| YouTube | Long-form education, tutorials | 1-4x/month | Videos, Shorts | All ages, search-driven |
Platform-Specific Rules
LinkedIn:
- Maximum post length: 3,000 characters (but 1,200-1,800 performs best)
- First 2-3 lines must hook (everything after "see more" is hidden)
- External links in comments, not post body (algorithm deprioritizes link posts)
- Carousels (PDF uploads) get highest organic reach
- Best posting times: Tuesday-Thursday, 7-9 AM in target timezone
Twitter/X:
- 280 characters per tweet, threads for longer content
- First tweet of thread is the hook — it determines everything
- Quote tweets with added insight outperform plain retweets
- No more than 2-3 hashtags (fewer is better)
- Best posting times: Monday-Friday, 8-10 AM and 12-1 PM
Instagram:
- Feed posts: square (1:1) or vertical (4:5)
- Reels: vertical (9:16), 15-90 seconds
- Carousels: up to 10 slides, first slide is the hook
- Stories: 24-hour content, polls/questions drive engagement
- Hashtags: 3-5 relevant ones (algorithm change in 2025 reduced hashtag importance)
TikTok:
- Video length: 15-60 seconds optimal (up to 10 minutes)
- Hook in first 2 seconds (viewers decide instantly)
- Captions: 80-100 characters
- Native feel outperforms polished production
- Trending sounds increase reach significantly
---
Content Pillar System
Defining Your Pillars
Choose 3-5 pillars that sit at the intersection of:
- Your expertise (what you know deeply)
- Audience interest (what they want to learn)
- Business connection (what supports your goals)
Pillar Allocation Template
| Pillar | % of Content | Purpose | Content Types |
|---|---|---|---|
| Industry insights | 30% | Build authority | Data, trends, predictions, analysis |
| Educational | 25% | Provide value | How-tos, frameworks, tips, tutorials |
| Behind-the-scenes | 20% | Build trust | Building stories, team, process, failures |
| Personal/opinion | 15% | Build connection | Stories, values, contrarian takes, lessons |
| Promotional | 10% | Drive business | Product updates, launches, offers, case studies |
Pillar-to-Platform Mapping
| Pillar | Twitter/X | TikTok | ||
|---|---|---|---|---|
| Industry insights | Data post, article share | Thread, commentary | Carousel infographic | Trend reaction video |
| Educational | Carousel, text post | Thread, tip tweet | Carousel, Reel | Tutorial video |
| Behind-the-scenes | Story post, company update | Tweet, photo | Stories, Reel | BTS video |
| Personal/opinion | Text post, story | Hot take, quote | Quote graphic | Story time video |
| Promotional | Case study, announcement | Launch tweet, thread | Product showcase | Demo video |
---
Hook Formula Library
Curiosity Hooks
| Formula | Example |
|---|---|
| "I was wrong about [belief]." | "I was wrong about cold email." |
| "The real reason [X] happens isn't what you think." | "The real reason your posts don't get engagement isn't the algorithm." |
| "[Result] — and it only took [timeframe]." | "10,000 followers — and it only took 90 days." |
| "Here's what nobody tells you about [topic]:" | "Here's what nobody tells you about startup marketing:" |
| "I spent [time] studying [X]. Here's what I found:" | "I spent 6 months studying viral LinkedIn posts. Here's what I found:" |
Story Hooks
| Formula | Example |
|---|---|
| "Last week, [unexpected thing] happened." | "Last week, our biggest deal fell through." |
| "3 years ago, I [past state]. Today, [current state]." | "3 years ago, I had 200 followers. Today, I have 50,000." |
| "I almost [big mistake]." | "I almost turned down the best job of my career." |
| "Someone asked me [question]. My answer surprised them." | "Someone asked me what the best marketing channel is. My answer surprised them." |
Value Hooks
| Formula | Example |
|---|---|
| "How to [outcome] (without [pain]):" | "How to grow on LinkedIn (without posting every day):" |
| "[Number] [things] that [outcome]:" | "7 headline formulas that actually convert:" |
| "Stop [mistake]. Do this instead:" | "Stop writing 'I'm excited to announce.' Do this instead:" |
| "The [X] framework I use for [Y]:" | "The content framework I use for every LinkedIn post:" |
Contrarian Hooks
| Formula | Example |
|---|---|
| "Unpopular opinion: [bold statement]" | "Unpopular opinion: SEO is dead for most startups." |
| "[Common advice] is wrong. Here's why:" | "'Post every day on LinkedIn' is wrong. Here's why:" |
| "Everyone is doing [X]. Almost everyone is wrong." | "Everyone is doing content marketing. Almost everyone is wrong." |
Data Hooks
| Formula | Example |
|---|---|
| "I analyzed [X] [things]. Here's the data:" | "I analyzed 500 LinkedIn posts. Here's the data:" |
| "[Surprising statistic]." | "93% of B2B buyers research online before talking to sales." |
| "[X] vs [Y]: the numbers don't lie." | "Short posts vs. long posts: the numbers don't lie." |
---
Platform-Specific Formats
LinkedIn Post Formats
Text Post (highest frequency):
[Hook — first 2 lines must compel "see more" click]
[Body — 5-10 lines of content, short paragraphs]
[One-line insight or lesson]
[CTA — question to drive comments]Carousel (highest reach):
- Slide 1: Bold hook statement (title slide)
- Slides 2-8: One point per slide, clear and visual
- Final slide: Summary + CTA ("Follow for more," "Save this")
- Upload as PDF for native carousel format
Poll (highest engagement):
- Use for genuine questions relevant to your audience
- 3-4 options maximum
- Add context in the post body explaining why the question matters
- Follow up with a post sharing the results
Twitter/X Thread Format
Tweet 1: [HOOK — the tweet that determines if anyone reads further]
Tweet 2: [Context or setup — why this matters]
Tweet 3-7: [Main content — one point per tweet]
Tweet 8: [Summary or key takeaway]
Tweet 9: [CTA — "Follow me for more on [topic]" or "RT tweet 1 if helpful"]Thread rules:
- First tweet must stand alone as a complete thought
- Each tweet should make sense independently
- Use numbers ("1/9") for clarity
- End with a CTA that drives engagement or follows
- Quote-tweet your own first tweet to resurface the thread
Instagram Carousel Format
- Slide 1: Bold statement or question (the hook)
- Slides 2-8: One clear point per slide
- Each slide has minimal text (30-50 words maximum)
- Consistent visual template (brand colors, fonts)
- Final slide: CTA ("Save this post," "Share with someone who needs this")
TikTok Video Structure
0-2 seconds: HOOK (text on screen + verbal hook)
2-15 seconds: Core content (get to the point fast)
15-45 seconds: Details and examples
Final 5 seconds: CTA ("Follow for more" or "Comment your [X]")---
Content Calendar Framework
Weekly Calendar Template
| Day | Twitter/X | ||
|---|---|---|---|
| Mon | Industry insight | Thread | Carousel |
| Tue | Educational post | Tips tweet | Stories |
| Wed | Behind-scenes | Engagement tweets | Reel |
| Thu | Personal/opinion | Thread | Educational |
| Fri | Promotional or story | Hot take | Stories |
Batching Strategy (2-3 Hours Per Week)
| Step | Time | Output |
|---|---|---|
| Review pillar topics | 15 min | 5-7 topic ideas for the week |
| Write LinkedIn posts | 45 min | 3-5 posts drafted |
| Write Twitter content | 30 min | 2-3 threads + 5 standalone tweets |
| Create Instagram content | 30 min | 2 carousels + 1 Reel concept |
| Schedule everything | 15 min | All content scheduled |
| Buffer for reactive content | 15 min | Slots left open for real-time posts |
Content Queue Management
- Maintain 1-2 weeks of scheduled content at all times
- Review queue weekly for relevance (cancel anything no longer timely)
- Leave 2-3 open slots per week for reactive/trending content
- Evergreen content can be reshared monthly with fresh framing
- Adjust timing based on analytics data
---
Repurposing System
One Piece, Many Formats
| Source Content | Twitter/X | TikTok | ||
|---|---|---|---|---|
| Blog post | Key insight post + link in comments | Thread of takeaways | Carousel of main points | Summary video |
| Podcast episode | Quote post from guest | Quote tweet thread | Audiogram clip | Short clip |
| Webinar | Carousel of key slides | Thread of lessons | Highlights Reel | Short tips video |
| Customer case study | Story-format post | Results thread | Before/after carousel | Transformation video |
| Original data | Data analysis post | Stats thread | Infographic carousel | Data reaction video |
Repurposing Workflow
1. Publish pillar content (blog, video, podcast) 2. Extract 3-5 key insights within 24 hours 3. Create platform-native derivatives (not copy-paste — reformat) 4. Schedule across 1-2 weeks after pillar publication 5. Cross-reference — derivatives link back to pillar content 6. Track which derivative formats drive the most engagement and traffic
---
Engagement Strategy
Daily Engagement Routine (30 Minutes)
| Task | Time | Purpose |
|---|---|---|
| Respond to all comments on your posts | 5 min | Reward engagement, boost algorithmic reach |
| Comment on 5-10 posts from target accounts | 15 min | Build relationships, increase visibility |
| Share/repost with added insight | 5 min | Provide value, support network |
| Send 2-3 DMs to new connections | 5 min | Build 1:1 relationships |
Quality Commenting Rules
What works:
- Add a new insight the original post missed
- Share a related experience or data point
- Ask a thoughtful follow-up question
- Respectfully disagree with nuance and reasoning
What fails:
- "Great post!" (adds no value)
- Emoji-only responses
- Self-promotional comments unrelated to the post
- Generic agreement without adding perspective
Relationship Building Strategy
1. Identify 20-50 accounts in your space that your audience follows 2. Engage consistently (comment on their content 3-5x/week) 3. Share their content with added insight (not just retweet) 4. DM thoughtfully (reference specific content, not generic pitches) 5. Collaborate when relationship is established (co-create, guest post, podcast) 6. Reciprocate — when they engage with your content, acknowledge it
---
Analytics and Optimization
Metrics That Matter
| Metric | What It Tells You | Target |
|---|---|---|
| Engagement rate | Content resonance | 2-5% (LinkedIn), 1-3% (Twitter) |
| Follower growth rate | Audience building momentum | 2-5% monthly |
| Comments per post | Conversation quality | Higher value than likes |
| Saves/bookmarks | Content utility | High saves = high value content |
| Shares/reposts | Content amplification | Indicates strong resonance |
| Profile visits | Curiosity driving | Growing = good brand building |
| Link clicks | Traffic driving | Track with UTMs |
| DMs received | Relationship building | Quality over quantity |
Weekly Review Process
| Step | What to Analyze | Action |
|---|---|---|
| Top 3 posts | Why did they perform? (Hook? Topic? Format?) | Do more of what worked |
| Bottom 3 posts | What went wrong? (Timing? Hook? Relevance?) | Adjust or avoid pattern |
| Engagement patterns | Which days/times got most engagement? | Optimize posting schedule |
| Follower quality | Are followers matching your ICP? | Adjust content if attracting wrong audience |
| Content pillar performance | Which pillars drive most engagement? | Rebalance allocation |
Optimization Levers
| Symptom | Likely Cause | Fix |
|---|---|---|
| Low engagement | Weak hooks | Test new hook formulas |
| Declining reach | Algorithm change or fatigue | Try new formats, increase engagement activity |
| High reach, low engagement | Content attracts but does not resonate | More specific, opinionated, or actionable content |
| Followers not converting | Wrong audience | Adjust pillar mix toward more business-relevant content |
| Engagement but no traffic | No CTAs or links | Add intentional CTAs and link in comments/bio |
---
Best Practices
1. Platform-native always — Never post the same content on every platform. Adapt format, length, and tone for each.
2. Hook is everything — If the first line does not stop the scroll, nothing else matters. Write the hook first.
3. Consistency beats virality — Posting 3x/week for 12 months beats going viral once and disappearing. Show up regularly.
4. Engage more than you post — Commenting on others' content builds relationships faster than your own posts. Aim for 3:1 engagement-to-posting ratio when starting.
5. 80/20 value-to-promotion — No more than 10-20% of content should be promotional. The rest should educate, entertain, or inspire.
6. Batch create, schedule ahead — Context-switching between creating and distributing reduces quality. Batch write in 2-3 hour sessions.
7. Reshare evergreen content — Your best posts can be reshared monthly with fresh framing. Most of your audience did not see it the first time.
8. Respond to every comment — Responding within the first hour signals the algorithm that your content is generating conversation.
9. Focus on 1-2 platforms first — Master one platform before expanding. Spreading across five platforms with a small team dilutes impact.
10. Track leading indicators — Engagement rate and follower growth rate matter more than absolute follower count. A small, engaged audience beats a large, passive one.
---
Integration Points
- Content Strategy — Use for deciding what topics to cover before creating social posts.
- Content Production — Use when pillar content needs to be created before repurposing to social.
- Copywriting — Use for long-form page copy. Social Content handles short-form platform-native posts.
- Copy Editing — Use to polish high-stakes social content (campaign launches, announcements).
- Content Humanizer — Use when AI-drafted social posts sound generic or robotic.
- Brand Guidelines — Reference brand voice and visual standards for social content consistency.
- Marketing Context — Use as foundation for audience, voice, and positioning alignment.
- Ad Creative — Use for paid social ad copy. Social Content handles organic posts.
---
Troubleshooting
| Symptom | Likely Cause | Fix |
|---|---|---|
| Low engagement despite posting | Weak hooks or wrong format | Test new hook formulas from the library. Try carousels or polls (highest engagement formats). |
| Declining reach week-over-week | Algorithm fatigue or format monotony | Mix formats weekly. Instagram/LinkedIn deprioritize repetitive content patterns. |
| High reach, low engagement | Content attracts but does not resonate | Make content more specific, opinionated, or actionable. Generic posts get impressions but no interaction. |
| Followers growing but not converting | Wrong audience or no CTAs | Adjust pillar mix toward more business-relevant content. Add intentional CTAs. |
| Hashtags not driving discovery (2026) | Instagram/LinkedIn reduced hashtag weight | Use 3-5 niche hashtags max. Shares and saves now outweigh hashtag reach on most platforms. |
| Content feels stale | Not repurposing or only one content source | Use the repurposing system: one pillar piece becomes 5-10 social derivatives. |
| Low comment-to-like ratio | Posts don't invite conversation | End posts with specific questions. Polls and hot takes drive 3-5x more comments. |
---
Success Criteria
- Engagement rate above platform average: 2-5% LinkedIn, 1-3% Twitter/X, 2-5% Instagram
- Follower growth rate of 2-5% monthly (quality followers matching ICP)
- Comments per post consistently higher than likes-only engagement
- Content pillar mix: no more than 10-15% promotional content
- 1-2 weeks of scheduled content maintained at all times
- Daily engagement routine: 30 min/day responding and commenting on others' posts
- Saves/bookmarks increasing month-over-month (indicates high-value content)
---
Scope & Limitations
In Scope: Organic social media content creation, platform-specific formatting, hook writing, content calendars, pillar systems, repurposing workflows, engagement tactics, analytics.
Out of Scope: Paid social ad copy (use ad-creative), social media account management tools, influencer outreach, community platform management (Discord, Slack), video production.
---
Python Automation Tools
1. Engagement Calculator (scripts/engagement_calculator.py)
Calculates engagement rates by post and format, benchmarks against platform standards, identifies top performers.
python scripts/engagement_calculator.py posts.json
python scripts/engagement_calculator.py --sample --json2. Post Scheduler (scripts/post_scheduler.py)
Generates optimal posting schedules with pillar allocation, format suggestions, and platform-specific timing.
python scripts/post_scheduler.py --platform linkedin --posts-per-week 5
python scripts/post_scheduler.py --platform instagram --posts-per-week 5 --json3. Hashtag Analyzer (scripts/hashtag_analyzer.py)
Analyzes hashtag strategy for relevance, platform limits, and discovery potential. Flags overly broad tags.
python scripts/hashtag_analyzer.py "#saas #b2bmarketing #growth"
python scripts/hashtag_analyzer.py --file post.txt --platform instagram --json#!/usr/bin/env python3
"""
Social Media Engagement Calculator
Calculates engagement rates, benchmarks performance against
industry standards, and identifies top-performing content patterns.
Usage:
python engagement_calculator.py posts.json
python engagement_calculator.py posts.json --json
python engagement_calculator.py --sample
Input JSON:
{
"platform": "linkedin",
"posts": [
{
"id": "post_1",
"type": "text",
"impressions": 5000,
"likes": 120,
"comments": 25,
"shares": 15,
"saves": 8,
"clicks": 45,
"date": "2026-03-01"
}
]
}
"""
import argparse
import json
import sys
from pathlib import Path
BENCHMARKS = {
"linkedin": {
"engagement_rate": {"poor": 1.0, "average": 2.0, "good": 3.5, "excellent": 5.0},
"by_format": {
"carousel": {"avg_engagement": 4.5},
"text": {"avg_engagement": 2.8},
"image": {"avg_engagement": 2.2},
"video": {"avg_engagement": 3.0},
"poll": {"avg_engagement": 5.0},
"document": {"avg_engagement": 4.2},
},
},
"twitter": {
"engagement_rate": {"poor": 0.3, "average": 0.7, "good": 1.5, "excellent": 3.0},
"by_format": {
"thread": {"avg_engagement": 2.0},
"text": {"avg_engagement": 0.8},
"image": {"avg_engagement": 1.2},
"video": {"avg_engagement": 1.5},
"poll": {"avg_engagement": 2.5},
},
},
"instagram": {
"engagement_rate": {"poor": 0.5, "average": 1.5, "good": 3.0, "excellent": 5.0},
"by_format": {
"reel": {"avg_engagement": 4.0},
"carousel": {"avg_engagement": 3.5},
"image": {"avg_engagement": 1.8},
"story": {"avg_engagement": 2.0},
},
},
"tiktok": {
"engagement_rate": {"poor": 1.0, "average": 3.0, "good": 6.0, "excellent": 10.0},
"by_format": {
"video": {"avg_engagement": 5.0},
},
},
}
SAMPLE = {
"platform": "linkedin",
"follower_count": 5000,
"posts": [
{"id": "1", "type": "carousel", "impressions": 8500, "likes": 280, "comments": 45, "shares": 32, "saves": 18, "clicks": 95, "date": "2026-03-01"},
{"id": "2", "type": "text", "impressions": 3200, "likes": 85, "comments": 22, "shares": 8, "saves": 5, "clicks": 30, "date": "2026-03-03"},
{"id": "3", "type": "text", "impressions": 4100, "likes": 110, "comments": 35, "shares": 12, "saves": 10, "clicks": 55, "date": "2026-03-05"},
{"id": "4", "type": "image", "impressions": 2800, "likes": 65, "comments": 8, "shares": 3, "saves": 2, "clicks": 20, "date": "2026-03-07"},
{"id": "5", "type": "poll", "impressions": 6200, "likes": 150, "comments": 88, "shares": 25, "saves": 12, "clicks": 40, "date": "2026-03-10"},
{"id": "6", "type": "video", "impressions": 5500, "likes": 195, "comments": 42, "shares": 28, "saves": 15, "clicks": 65, "date": "2026-03-12"},
{"id": "7", "type": "carousel", "impressions": 9200, "likes": 320, "comments": 55, "shares": 40, "saves": 25, "clicks": 110, "date": "2026-03-15"},
],
}
def calc_engagement(post: dict) -> dict:
impressions = post.get("impressions", 0)
likes = post.get("likes", 0)
comments = post.get("comments", 0)
shares = post.get("shares", 0)
saves = post.get("saves", 0)
clicks = post.get("clicks", 0)
total_engagements = likes + comments + shares + saves + clicks
engagement_rate = (total_engagements / impressions * 100) if impressions > 0 else 0
# Weighted engagement (comments and shares more valuable)
weighted = likes * 1 + comments * 3 + shares * 4 + saves * 2 + clicks * 1.5
weighted_rate = (weighted / impressions * 100) if impressions > 0 else 0
return {
"id": post.get("id", ""),
"type": post.get("type", "unknown"),
"date": post.get("date", ""),
"impressions": impressions,
"total_engagements": total_engagements,
"engagement_rate": round(engagement_rate, 2),
"weighted_engagement_rate": round(weighted_rate, 2),
"breakdown": {
"likes": likes,
"comments": comments,
"shares": shares,
"saves": saves,
"clicks": clicks,
},
}
def analyze_posts(data: dict) -> dict:
platform = data.get("platform", "linkedin").lower()
follower_count = data.get("follower_count", 0)
posts = data.get("posts", [])
bench = BENCHMARKS.get(platform, BENCHMARKS["linkedin"])
post_results = [calc_engagement(p) for p in posts]
# Sort by engagement rate
post_results.sort(key=lambda x: x["engagement_rate"], reverse=True)
# Aggregates
total_impressions = sum(p["impressions"] for p in post_results)
total_engagements = sum(p["total_engagements"] for p in post_results)
avg_engagement = (total_engagements / total_impressions * 100) if total_impressions > 0 else 0
# By format
format_stats = {}
for p in post_results:
fmt = p["type"]
if fmt not in format_stats:
format_stats[fmt] = {"count": 0, "total_impressions": 0, "total_engagements": 0}
format_stats[fmt]["count"] += 1
format_stats[fmt]["total_impressions"] += p["impressions"]
format_stats[fmt]["total_engagements"] += p["total_engagements"]
for fmt in format_stats:
f = format_stats[fmt]
f["avg_engagement_rate"] = round(f["total_engagements"] / f["total_impressions"] * 100, 2) if f["total_impressions"] > 0 else 0
fmt_bench = bench.get("by_format", {}).get(fmt, {})
f["benchmark"] = fmt_bench.get("avg_engagement", "N/A")
# Classify overall performance
eng_bench = bench["engagement_rate"]
if avg_engagement >= eng_bench["excellent"]:
performance = "excellent"
elif avg_engagement >= eng_bench["good"]:
performance = "good"
elif avg_engagement >= eng_bench["average"]:
performance = "average"
else:
performance = "below_average"
# Top and bottom performers
top_3 = post_results[:3] if len(post_results) >= 3 else post_results
bottom_3 = post_results[-3:] if len(post_results) >= 3 else post_results
# Recommendations
recs = []
if performance in ("below_average", "average"):
recs.append("Try more engaging formats: carousels and polls typically outperform text posts.")
recs.append("Strengthen hooks -- the first 2 lines determine engagement on most platforms.")
best_format = max(format_stats.items(), key=lambda x: x[1]["avg_engagement_rate"])[0] if format_stats else "unknown"
recs.append(f"Best performing format: {best_format}. Consider increasing its share in your content mix.")
# Engagement quality
total_comments = sum(p["breakdown"]["comments"] for p in post_results)
total_likes = sum(p["breakdown"]["likes"] for p in post_results)
if total_likes > 0:
comment_ratio = total_comments / total_likes
if comment_ratio < 0.1:
recs.append("Low comment-to-like ratio. Add questions or prompts to drive conversations.")
elif comment_ratio > 0.3:
recs.append("Strong comment-to-like ratio. Your content is sparking conversations.")
return {
"platform": platform,
"follower_count": follower_count,
"post_count": len(post_results),
"aggregate": {
"total_impressions": total_impressions,
"total_engagements": total_engagements,
"avg_engagement_rate": round(avg_engagement, 2),
"performance_vs_benchmark": performance,
"benchmark_range": f"{eng_bench['average']}-{eng_bench['good']}%",
},
"by_format": format_stats,
"top_performers": top_3,
"bottom_performers": bottom_3,
"all_posts": post_results,
"recommendations": recs,
}
def format_human(result: dict) -> str:
lines = ["\n" + "=" * 65, " SOCIAL MEDIA ENGAGEMENT CALCULATOR", "=" * 65]
agg = result["aggregate"]
lines.append(f"\n Platform: {result['platform'].title()} | Posts: {result['post_count']} | Followers: {result['follower_count']}")
lines.append(f" Avg Engagement: {agg['avg_engagement_rate']}% ({agg['performance_vs_benchmark']})")
lines.append(f" Benchmark: {agg['benchmark_range']}")
lines.append(f"\n By Format:")
for fmt, stats in sorted(result["by_format"].items(), key=lambda x: -x[1]["avg_engagement_rate"]):
lines.append(f" {fmt:<12} {stats['avg_engagement_rate']}% avg ({stats['count']} posts) | benchmark: {stats['benchmark']}%")
lines.append(f"\n Top Performers:")
for p in result["top_performers"]:
lines.append(f" [{p['type']}] {p['engagement_rate']}% -- {p['impressions']} impressions -- {p['date']}")
if result["recommendations"]:
lines.append(f"\n Recommendations:")
for r in result["recommendations"]:
lines.append(f" > {r}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Calculate social media engagement metrics.")
parser.add_argument("file", nargs="?")
parser.add_argument("--json", action="store_true", dest="json_output")
parser.add_argument("--sample", action="store_true")
args = parser.parse_args()
if args.sample:
data = SAMPLE
elif args.file:
try:
data = json.loads(Path(args.file).read_text())
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
else:
parser.print_help()
sys.exit(1)
result = analyze_posts(data)
if args.json_output:
print(json.dumps(result, indent=2))
else:
print(format_human(result))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Hashtag Analyzer
Analyzes hashtag usage in social media posts, scores relevance,
and recommends optimal hashtag strategy per platform.
Usage:
python hashtag_analyzer.py "#marketing #growth #startup"
python hashtag_analyzer.py --file post.txt --platform instagram --json
"""
import argparse
import json
import re
import sys
from pathlib import Path
PLATFORM_LIMITS = {
"linkedin": {"max": 5, "optimal": (3, 5), "note": "3-5 hashtags. Algorithm deprioritizes posts with 10+."},
"twitter": {"max": 3, "optimal": (1, 2), "note": "1-2 hashtags. More than 3 looks spammy."},
"instagram": {"max": 30, "optimal": (3, 5), "note": "3-5 relevant hashtags in 2026. Algorithm change reduced hashtag importance."},
"tiktok": {"max": 5, "optimal": (3, 5), "note": "3-5 relevant hashtags. Mix niche and trending."},
"facebook": {"max": 5, "optimal": (1, 3), "note": "1-3 hashtags maximum. Low impact on reach."},
}
# Common overly broad hashtags (low value due to saturation)
BROAD_HASHTAGS = {
"marketing", "business", "success", "motivation", "entrepreneur",
"startup", "innovation", "technology", "leadership", "growth",
"digital", "strategy", "mindset", "goals", "hustle", "love",
"instagood", "photooftheday", "followme", "follow4follow",
"like4like", "likeforlike", "tbt", "instadaily", "picoftheday",
}
# Industry/niche hashtag patterns
NICHE_PATTERNS = [
re.compile(r"#(saas|b2b|b2c|d2c|defi|fintech|healthtech|edtech|martech|proptech)", re.IGNORECASE),
re.compile(r"#(\w+tips|\w+advice|\w+strategy|\w+hacks)", re.IGNORECASE),
re.compile(r"#(\w+community|\w+life|\w+world)", re.IGNORECASE),
]
BRANDED_PATTERN = re.compile(r"#([A-Z][a-z]+[A-Z]|[a-z]+[A-Z])", re.IGNORECASE)
def extract_hashtags(text: str) -> list:
return re.findall(r"#(\w+)", text)
def analyze_hashtag(tag: str) -> dict:
lower = tag.lower()
result = {
"hashtag": f"#{tag}",
"length": len(tag),
"type": "standard",
"quality": "medium",
"issues": [],
}
# Check if too broad
if lower in BROAD_HASHTAGS:
result["type"] = "broad"
result["quality"] = "low"
result["issues"].append("Overly broad -- high competition, low discovery value")
# Check if niche
for pattern in NICHE_PATTERNS:
if pattern.match(f"#{tag}"):
result["type"] = "niche"
result["quality"] = "high"
break
# Check if branded
if BRANDED_PATTERN.match(tag):
result["type"] = "branded"
result["quality"] = "medium"
# Length checks
if len(tag) > 30:
result["issues"].append("Too long -- hard to read and remember")
result["quality"] = "low"
elif len(tag) < 3:
result["issues"].append("Too short -- likely too generic")
result["quality"] = "low"
# All caps
if tag.isupper() and len(tag) > 3:
result["issues"].append("ALL CAPS looks spammy")
return result
def analyze_hashtags(text: str, platform: str = "linkedin") -> dict:
tags = extract_hashtags(text)
platform_config = PLATFORM_LIMITS.get(platform, PLATFORM_LIMITS["linkedin"])
tag_analyses = [analyze_hashtag(t) for t in tags]
# Count by quality
quality_dist = {"high": 0, "medium": 0, "low": 0}
type_dist = {"broad": 0, "niche": 0, "branded": 0, "standard": 0}
for ta in tag_analyses:
quality_dist[ta["quality"]] += 1
type_dist[ta["type"]] += 1
# Score
score = 100
issues = []
recommendations = []
# Count check
count = len(tags)
opt_min, opt_max = platform_config["optimal"]
if count == 0:
score -= 20
issues.append("No hashtags found. Add 3-5 relevant hashtags for discoverability.")
elif count < opt_min:
score -= 10
recommendations.append(f"Add {opt_min - count} more relevant hashtags (optimal: {opt_min}-{opt_max}).")
elif count > platform_config["max"]:
score -= 20
issues.append(f"Too many hashtags ({count}). {platform.title()} optimal is {opt_min}-{opt_max}.")
elif count > opt_max:
score -= 5
recommendations.append(f"Consider reducing to {opt_min}-{opt_max} hashtags for best results.")
# Quality check
if quality_dist["low"] > quality_dist["high"]:
score -= 15
issues.append("More broad/generic hashtags than niche ones. Replace generic tags with niche alternatives.")
if type_dist["broad"] > 2:
score -= 10
recommendations.append("Replace broad hashtags (e.g., #marketing) with specific alternatives (e.g., #b2bmarketing).")
# Duplicate check
seen = set()
dupes = []
for t in tags:
if t.lower() in seen:
dupes.append(t)
seen.add(t.lower())
if dupes:
score -= 10
issues.append(f"Duplicate hashtags: {', '.join(dupes)}")
# Mix recommendation
if type_dist["niche"] == 0 and count > 0:
recommendations.append("Add niche/industry-specific hashtags for targeted discovery.")
if type_dist["branded"] == 0 and count > 2:
recommendations.append("Consider adding a branded hashtag for community building.")
if not recommendations and score >= 80:
recommendations.append("Hashtag strategy looks solid.")
score = max(0, min(100, score))
return {
"platform": platform,
"platform_note": platform_config["note"],
"hashtag_count": count,
"optimal_range": f"{opt_min}-{opt_max}",
"score": score,
"grade": "A" if score >= 85 else "B" if score >= 70 else "C" if score >= 55 else "D" if score >= 40 else "F",
"quality_distribution": quality_dist,
"type_distribution": type_dist,
"hashtags": tag_analyses,
"issues": issues,
"recommendations": recommendations,
}
def format_human(result: dict) -> str:
lines = ["\n" + "=" * 55, " HASHTAG ANALYZER", "=" * 55]
lines.append(f"\n Platform: {result['platform'].title()} | Count: {result['hashtag_count']} (optimal: {result['optimal_range']})")
lines.append(f" Score: {result['score']}/100 ({result['grade']})")
lines.append(f" Note: {result['platform_note']}")
if result["hashtags"]:
lines.append(f"\n Hashtag Analysis:")
for h in result["hashtags"]:
q_icon = {"high": "+", "medium": "~", "low": "-"}
issues = f" -- {'; '.join(h['issues'])}" if h["issues"] else ""
lines.append(f" [{q_icon.get(h['quality'], '?')}] {h['hashtag']} ({h['type']}, {h['quality']}){issues}")
if result["issues"]:
lines.append(f"\n Issues:")
for i in result["issues"]:
lines.append(f" ! {i}")
if result["recommendations"]:
lines.append(f"\n Recommendations:")
for r in result["recommendations"]:
lines.append(f" > {r}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Analyze hashtag strategy for social media posts.")
parser.add_argument("text", nargs="?", help="Text containing hashtags")
parser.add_argument("--file", "-f", help="File containing post text")
parser.add_argument("--platform", "-p", default="linkedin", choices=list(PLATFORM_LIMITS.keys()))
parser.add_argument("--json", action="store_true", dest="json_output")
args = parser.parse_args()
if args.file:
try:
text = Path(args.file).read_text()
except FileNotFoundError:
print(f"Error: {args.file} not found", file=sys.stderr)
sys.exit(1)
elif args.text:
text = args.text
else:
parser.print_help()
sys.exit(1)
result = analyze_hashtags(text, args.platform)
if args.json_output:
print(json.dumps(result, indent=2))
else:
print(format_human(result))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Social Media Post Scheduler
Generates optimal posting schedules based on platform best practices,
content pillar allocation, and audience timezone.
Usage:
python post_scheduler.py --platform linkedin --posts-per-week 5
python post_scheduler.py --platform linkedin --posts-per-week 5 --timezone "US/Eastern" --json
python post_scheduler.py --config schedule_config.json
"""
import argparse
import json
import sys
from datetime import datetime, timedelta
from pathlib import Path
PLATFORM_OPTIMAL = {
"linkedin": {
"best_days": ["tuesday", "wednesday", "thursday"],
"good_days": ["monday", "friday"],
"avoid_days": ["saturday", "sunday"],
"best_times": ["07:00", "08:00", "09:00"],
"good_times": ["10:00", "11:00", "12:00"],
"max_per_day": 1,
"min_per_week": 3,
"max_per_week": 5,
"best_formats": ["carousel", "text", "poll", "document", "video"],
},
"twitter": {
"best_days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
"good_days": [],
"avoid_days": ["saturday", "sunday"],
"best_times": ["08:00", "09:00", "10:00", "12:00", "13:00"],
"good_times": ["14:00", "15:00", "17:00"],
"max_per_day": 5,
"min_per_week": 5,
"max_per_week": 15,
"best_formats": ["thread", "text", "image", "poll", "quote_tweet"],
},
"instagram": {
"best_days": ["tuesday", "wednesday", "thursday"],
"good_days": ["monday", "friday"],
"avoid_days": ["sunday"],
"best_times": ["09:00", "11:00", "14:00"],
"good_times": ["07:00", "12:00", "17:00"],
"max_per_day": 2,
"min_per_week": 4,
"max_per_week": 7,
"best_formats": ["reel", "carousel", "story", "image"],
},
"tiktok": {
"best_days": ["tuesday", "wednesday", "thursday", "friday"],
"good_days": ["monday", "saturday"],
"avoid_days": ["sunday"],
"best_times": ["07:00", "10:00", "14:00", "19:00"],
"good_times": ["08:00", "12:00", "17:00"],
"max_per_day": 3,
"min_per_week": 5,
"max_per_week": 14,
"best_formats": ["video"],
},
}
CONTENT_PILLARS = {
"educational": 0.30,
"industry_insights": 0.25,
"behind_the_scenes": 0.20,
"personal_opinion": 0.15,
"promotional": 0.10,
}
DAY_MAP = {
0: "monday", 1: "tuesday", 2: "wednesday",
3: "thursday", 4: "friday", 5: "saturday", 6: "sunday",
}
def generate_schedule(platform: str, posts_per_week: int, weeks: int = 2, timezone: str = "UTC", pillars: dict = None) -> dict:
config = PLATFORM_OPTIMAL.get(platform, PLATFORM_OPTIMAL["linkedin"])
pillars = pillars or CONTENT_PILLARS
# Validate
if posts_per_week < config["min_per_week"]:
posts_per_week = config["min_per_week"]
if posts_per_week > config["max_per_week"]:
posts_per_week = config["max_per_week"]
# Build available days ranked by priority
all_days = config["best_days"] + config["good_days"]
# Distribute posts across days
schedule_days = []
while len(schedule_days) < posts_per_week:
for day in all_days:
if len(schedule_days) >= posts_per_week:
break
# Check max per day
day_count = sum(1 for d in schedule_days if d == day)
if day_count < config["max_per_day"]:
schedule_days.append(day)
# Assign pillars based on allocation
pillar_slots = []
remaining = posts_per_week
for pillar, pct in sorted(pillars.items(), key=lambda x: -x[1]):
count = max(1, round(posts_per_week * pct))
count = min(count, remaining)
pillar_slots.extend([pillar] * count)
remaining -= count
# Fill any remaining
while len(pillar_slots) < posts_per_week:
pillar_slots.append("educational")
pillar_slots = pillar_slots[:posts_per_week]
# Assign times
all_times = config["best_times"] + config["good_times"]
# Generate weekly schedule
weekly_schedules = []
today = datetime.now()
next_monday = today + timedelta(days=(7 - today.weekday()) % 7)
for week in range(weeks):
week_start = next_monday + timedelta(weeks=week)
week_schedule = []
for i, (day_name, pillar) in enumerate(zip(schedule_days, pillar_slots)):
day_num = list(DAY_MAP.values()).index(day_name)
post_date = week_start + timedelta(days=day_num)
time = all_times[i % len(all_times)]
fmt = config["best_formats"][i % len(config["best_formats"])]
week_schedule.append({
"date": post_date.strftime("%Y-%m-%d"),
"day": day_name.title(),
"time": time,
"timezone": timezone,
"pillar": pillar.replace("_", " ").title(),
"suggested_format": fmt,
"status": "planned",
})
weekly_schedules.append({
"week": week + 1,
"start_date": week_start.strftime("%Y-%m-%d"),
"posts": sorted(week_schedule, key=lambda x: x["date"]),
})
result = {
"platform": platform,
"posts_per_week": posts_per_week,
"timezone": timezone,
"schedule": weekly_schedules,
"pillar_allocation": {p.replace("_", " ").title(): f"{v*100:.0f}%" for p, v in pillars.items()},
"platform_guidelines": {
"best_days": config["best_days"],
"best_times": config["best_times"],
"avoid_days": config["avoid_days"],
"max_per_day": config["max_per_day"],
},
"tips": [
"Leave 2-3 open slots per week for reactive/trending content.",
"Batch create content in 2-3 hour sessions for consistency.",
"Respond to all comments within 1 hour of posting for algorithm boost.",
f"Engage on 5-10 others' posts daily for {platform} growth.",
],
}
return result
def format_human(result: dict) -> str:
lines = ["\n" + "=" * 65, " SOCIAL MEDIA POST SCHEDULER", "=" * 65]
lines.append(f"\n Platform: {result['platform'].title()} | {result['posts_per_week']} posts/week | TZ: {result['timezone']}")
lines.append(f"\n Content Pillar Allocation:")
for p, v in result["pillar_allocation"].items():
lines.append(f" {p}: {v}")
for week in result["schedule"]:
lines.append(f"\n Week {week['week']} (starting {week['start_date']}):")
lines.append(f" {'Date':<12} {'Day':<10} {'Time':<7} {'Pillar':<22} {'Format'}")
lines.append(f" {'-'*65}")
for post in week["posts"]:
lines.append(f" {post['date']:<12} {post['day']:<10} {post['time']:<7} {post['pillar']:<22} {post['suggested_format']}")
lines.append(f"\n Tips:")
for t in result["tips"]:
lines.append(f" > {t}")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Generate optimal social media posting schedule.")
parser.add_argument("--platform", "-p", default="linkedin", choices=list(PLATFORM_OPTIMAL.keys()))
parser.add_argument("--posts-per-week", "-n", type=int, default=5)
parser.add_argument("--weeks", "-w", type=int, default=2)
parser.add_argument("--timezone", "-tz", default="UTC")
parser.add_argument("--config", help="JSON config file")
parser.add_argument("--json", action="store_true", dest="json_output")
args = parser.parse_args()
if args.config:
try:
config = json.loads(Path(args.config).read_text())
platform = config.get("platform", args.platform)
ppw = config.get("posts_per_week", args.posts_per_week)
weeks = config.get("weeks", args.weeks)
tz = config.get("timezone", args.timezone)
pillars = config.get("pillars", CONTENT_PILLARS)
except (FileNotFoundError, json.JSONDecodeError) as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
else:
platform = args.platform
ppw = args.posts_per_week
weeks = args.weeks
tz = args.timezone
pillars = CONTENT_PILLARS
result = generate_schedule(platform, ppw, weeks, tz, pillars)
if args.json_output:
print(json.dumps(result, indent=2))
else:
print(format_human(result))
if __name__ == "__main__":
main()
Related skills
FAQ
Which platforms does it cover?
LinkedIn, Twitter/X, Instagram, TikTok, Facebook and YouTube, each with its own format rules and posting cadence.
How many content pillars does it recommend?
3-5 pillars at the intersection of your expertise, audience interest and business goals, with a percentage allocation each.