
Growth Marketer
- 392 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
growth-marketer is a Claude Code skill that plans full-funnel growth experiments across acquisition, activation, retention, and referral using cohort analysis and channel tests for developers scaling product usage and re
About
growth-marketer is a Claude Code skill that structures end-to-end growth marketing work for software products. It helps developers and product leads design experiments across acquisition, activation, retention, and referral, applying cohort analysis, channel tests, and conversion optimization to prioritize what moves users and revenue. The skill frames hypotheses, measurement plans, and iteration cycles so agent-assisted sessions produce actionable growth roadmaps instead of generic marketing copy. Reach for it when a shipped product needs structured funnel diagnosis, experiment backlogs, or retention-focused campaign planning. It complements analytics implementation skills but does not replace tracking instrumentation or ad platform setup.
- Full-funnel experiment design
- Acquisition channel testing
- Activation and onboarding optimization
- Retention and referral loop planning
- Cohort and conversion analysis
Growth Marketer by the numbers
- 392 all-time installs (skills.sh)
- Ranked #211 of 853 Sales & Marketing skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/borghei/claude-skills --skill growth-marketerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 392 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
How do you plan full-funnel growth experiments for a SaaS product?
Plan full-funnel experiments across acquisition, activation, retention, and referral using cohort analysis, channel tests, and conversion optimization to scale users and revenue.
Who is it for?
Developers and product engineers who own growth metrics and need structured experiment plans across acquisition through referral.
Skip if: Teams still validating product scope or building core features without an existing user base to measure.
When should I use this skill?
A developer asks to improve retention, run channel tests, design referral loops, or build a cohort-based growth experiment plan.
What you get
Experiment backlog, cohort analysis framework, channel test plan, and conversion optimization recommendations.
Files
Growth Marketer
The agent operates as a senior growth marketer, delivering experiment-driven strategies for scalable user acquisition, activation, retention, referral, and revenue optimization.
Workflow
1. Define North Star Metric - Identify the single metric that reflects customer value and leads to revenue. Checkpoint: the metric must be measurable, actionable, and correlated with retention. 2. Map the AARRR funnel - Quantify current performance at each stage (Acquisition, Activation, Retention, Referral, Revenue). Checkpoint: every stage has a baseline number and a target. 3. Identify biggest lever - Find the funnel stage with the largest drop-off or lowest performance vs. benchmark. This becomes the focus area. 4. Design experiments - Write hypotheses using the format: "If we [change], then [metric] will [direction] by [amount] because [reasoning]." Prioritize using ICE scoring. 5. Calculate sample size and run - Determine required sample per variant for statistical significance (95% confidence, 80% power). Launch the experiment. 6. Analyze results - Evaluate lift, p-value, and guardrail metrics. Decision: Ship, Iterate, or Kill. 7. Model growth trajectory - Forecast user growth incorporating acquisition rate, churn, and viral coefficient. Validate that LTV:CAC > 3:1 for sustainability.
AARRR Funnel (Pirate Metrics)
| Stage | Key Question | Metrics | Benchmark |
|---|---|---|---|
| Acquisition | How do users find us? | Traffic, CAC, channel mix | CAC < 1/3 LTV |
| Activation | Great first experience? | Activation rate, time to value | 40%+ activation |
| Retention | Do users come back? | D1/D7/D30 retention, churn | SaaS: D30 30% |
| Referral | Do users tell others? | Viral coefficient (K), NPS | K-factor > 0.5 |
| Revenue | How do we monetize? | ARPU, LTV, conversion rate | LTV:CAC > 3:1 |
Experimentation Framework
Experiment Document Template
# Experiment: Onboarding Checklist v2
## Hypothesis
If we add a progress bar to the onboarding checklist, then activation rate
will increase by 15% because users respond to completion motivation.
## Metrics
- Primary: 7-day activation rate
- Secondary: Time to first value action
- Guardrails: Support ticket volume, bounce rate
## Design
- Type: A/B test
- Sample: 8,200 per variant (5% baseline, 15% MDE, 95% confidence)
- Duration: 14 days
- Segments: New signups only
## Results
| Variant | Users | Activation | Lift | p-value |
|-----------|--------|------------|-------|---------|
| Control | 8,350 | 5.1% | - | - |
| Treatment | 8,280 | 6.2% | +21% | 0.003 |
## Decision: ShipICE Prioritization
| Experiment | Impact (1-10) | Confidence (1-10) | Ease (1-10) | ICE Score |
|---|---|---|---|---|
| Onboarding checklist v2 | 8 | 7 | 9 | 24 |
| Referral incentive test | 6 | 8 | 7 | 21 |
| Pricing page redesign | 9 | 5 | 6 | 20 |
Sample Size Calculator
from scipy import stats
def sample_size(baseline_rate, mde, alpha=0.05, power=0.8):
"""Calculate required sample size per variant for an A/B test.
Args:
baseline_rate: Current conversion rate (e.g. 0.05 for 5%)
mde: Minimum detectable effect as proportion (e.g. 0.15 for 15% lift)
alpha: Significance level (default 0.05)
power: Statistical power (default 0.8)
Returns:
Required users per variant (int)
Example:
>>> sample_size(0.05, 0.15)
8218
"""
effect_size = mde * baseline_rate
z_alpha = stats.norm.ppf(1 - alpha / 2)
z_beta = stats.norm.ppf(power)
n = 2 * ((z_alpha + z_beta) ** 2) * baseline_rate * (1 - baseline_rate) / (effect_size ** 2)
return int(n)Acquisition Channel Analysis
| Channel | CAC | Volume | Quality | Scalability |
|---|---|---|---|---|
| Organic Search | $20 | High | High | Medium |
| Paid Search | $50 | Medium | High | High |
| Social Organic | $10 | Medium | Medium | Low |
| Social Paid | $40 | High | Medium | High |
| Content | $15 | Medium | High | Medium |
| Referral | $5 | Low | Very High | Medium |
| Partnerships | $30 | Medium | High | Medium |
Retention Benchmarks
| Category | D1 | D7 | D30 |
|---|---|---|---|
| SaaS | 60% | 40% | 30% |
| Social | 50% | 30% | 20% |
| E-commerce | 25% | 15% | 10% |
| Games | 35% | 15% | 8% |
Cohort Analysis Example
Week 0 Week 1 Week 2 Week 3 Week 4
Jan W1 100% 45% 35% 28% 25%
Jan W2 100% 48% 38% 32% 28%
Jan W3 100% 52% 42% 35% 31%
Jan W4 100% 55% 45% 38% 34%
Insight: Week-over-week improvement correlates with onboarding
changes shipped in Jan W3.Viral Growth
K-Factor = invites per user (i) x conversion rate of invites (c)
- K > 1: True viral growth (each user brings >1 new user)
- K = 0.5-1: Viral boost (amplifies paid acquisition)
- K < 0.5: Minimal viral effect
Growth Forecast Model
def growth_forecast(current_users, monthly_growth_rate, months):
"""Forecast user base over time with compound growth.
Example:
>>> growth_forecast(10000, 0.10, 12)[-1]
31384
"""
users = [current_users]
for _ in range(months):
users.append(int(users[-1] * (1 + monthly_growth_rate)))
return usersScripts
# Experiment analyzer
python scripts/experiment_analyzer.py --experiment exp_001 --data results.csv
# Funnel analyzer
python scripts/funnel_analyzer.py --events events.csv --output funnel.html
# Cohort generator
python scripts/cohort_generator.py --users users.csv --metric retention
# Growth model
python scripts/growth_model.py --current 10000 --growth 0.1 --months 12Reference Materials
references/experimentation.md- A/B testing guidereferences/acquisition.md- Channel playbooksreferences/retention.md- Retention strategiesreferences/viral.md- Viral mechanics
---
Troubleshooting
| Symptom | Likely Cause | Resolution |
|---|---|---|
| K-factor below 0.1 despite referral program | Invite UX has too much friction or incentive misaligned with user value | Reduce invite flow to one click; align incentive with product value (usage credits > cash) |
| Activation rate below 20% for new signups | Time-to-value too long or onboarding not guiding users to aha moment | Map activation events, identify first value action, build guided onboarding to reach it in under 5 minutes |
| Growth stalls after initial PLG ramp | Free tier captures low-intent users who never convert; paid conversion rate below 3% | Tighten free tier limits around high-value features, add contextual upgrade prompts at usage gates |
| A/B test results not reaching significance | Sample size too small for the minimum detectable effect being tested | Use sample size calculator; increase traffic to test or accept larger MDE |
| Cohort retention curves flatten at under 15% | Product does not build enough habit; no ongoing value loop | Implement engagement hooks (notifications, reports, streaks); investigate which features drive retention |
| Experiments consistently show no lift | Testing cosmetic changes rather than meaningful value propositions | Focus experiments on activation flow, pricing, and value communication — not button colors |
---
Success Criteria
- North Star Metric identified, measurable, and reviewed weekly with cross-functional team
- Activation rate above 40% for new signups within first 7 days
- LTV:CAC ratio sustained above 3:1 across all acquisition channels
- K-factor above 0.5, providing meaningful viral amplification of paid acquisition
- Experiment velocity of 2+ tests per sprint with documented hypotheses and outcomes
- D30 retention at or above SaaS benchmark (30%) for primary user segment
- Growth model accurately forecasts within 15% of actual for 3-month projections
---
Scope & Limitations
In Scope: AARRR funnel optimization, experiment design and prioritization (ICE/RICE), viral growth modeling, PLG strategy, retention analysis, cohort analysis, growth forecasting, acquisition channel analysis, sample size calculation.
Out of Scope: Brand strategy (see brand-strategist skill), content creation (see content-creator skill), paid ad campaign management (see paid-ads skill), product design and engineering implementation, pricing strategy.
Limitations: Growth loop models use simplified compound growth assumptions — real growth has diminishing returns and market saturation effects. Viral coefficient calculations assume uniform user behavior; actual viral spread varies by segment. Sample size calculator uses normal approximation; for very low conversion rates, exact tests may be needed.
---
Scripts
| Script | Purpose | Usage |
|---|---|---|
scripts/growth_loop_modeler.py | Model viral, PLG, and content growth loops with forecasts | python scripts/growth_loop_modeler.py --type viral --users 1000 --k-factor 0.6 --months 12 |
scripts/viral_coefficient_calculator.py | Calculate K-factor, branching factor, and improvement scenarios | python scripts/viral_coefficient_calculator.py --invites 5000 --conversions 800 --users 2000 |
scripts/experiment_prioritizer.py | Prioritize growth experiments using ICE or RICE scoring | python scripts/experiment_prioritizer.py experiments.json --framework ice --demo |
#!/usr/bin/env python3
"""Experiment Prioritizer - Prioritize growth experiments using ICE/RICE scoring.
Scores experiment ideas using ICE (Impact, Confidence, Ease) or RICE
(Reach, Impact, Confidence, Effort) frameworks, ranks them, and generates
a prioritized experiment roadmap.
Usage:
python experiment_prioritizer.py experiments.json
python experiment_prioritizer.py experiments.json --framework rice --json
python experiment_prioritizer.py --demo
"""
import argparse
import json
import sys
def score_ice(experiments):
"""Score experiments using ICE framework."""
results = []
for exp in experiments:
impact = exp.get("impact", 5)
confidence = exp.get("confidence", 5)
ease = exp.get("ease", 5)
# Validate scores
impact = max(1, min(10, impact))
confidence = max(1, min(10, confidence))
ease = max(1, min(10, ease))
ice_score = (impact + confidence + ease) / 3
results.append({
"name": exp.get("name", "Unnamed"),
"hypothesis": exp.get("hypothesis", ""),
"metric": exp.get("metric", ""),
"impact": impact,
"confidence": confidence,
"ease": ease,
"ice_score": round(ice_score, 2),
"category": _categorize_score(ice_score, "ice"),
})
results.sort(key=lambda x: x["ice_score"], reverse=True)
return results
def score_rice(experiments):
"""Score experiments using RICE framework."""
results = []
for exp in experiments:
reach = exp.get("reach", 1000) # Users affected per quarter
impact = exp.get("impact", 1) # 0.25, 0.5, 1, 2, 3
confidence = exp.get("confidence", 80) # Percentage
effort = exp.get("effort", 1) # Person-months
# Validate
impact = max(0.25, min(3, impact))
confidence = max(10, min(100, confidence))
effort = max(0.25, min(12, effort))
rice_score = (reach * impact * (confidence / 100)) / effort
results.append({
"name": exp.get("name", "Unnamed"),
"hypothesis": exp.get("hypothesis", ""),
"metric": exp.get("metric", ""),
"reach": reach,
"impact": impact,
"confidence": confidence,
"effort": effort,
"rice_score": round(rice_score, 1),
"category": _categorize_score(rice_score, "rice"),
})
results.sort(key=lambda x: x["rice_score"], reverse=True)
return results
def _categorize_score(score, framework):
if framework == "ice":
if score >= 8:
return "must_do"
elif score >= 6:
return "should_do"
elif score >= 4:
return "could_do"
return "backlog"
else: # RICE
if score >= 5000:
return "must_do"
elif score >= 1000:
return "should_do"
elif score >= 200:
return "could_do"
return "backlog"
def calculate_sample_size(baseline_rate, mde, alpha=0.05, power=0.8):
"""Calculate required sample size per variant (normal approximation)."""
# Z-scores
z_alpha = 1.96 if alpha == 0.05 else 2.576 # 95% or 99%
z_beta = 0.842 if power == 0.8 else 1.282 # 80% or 90%
effect = baseline_rate * mde
n = 2 * ((z_alpha + z_beta) ** 2) * baseline_rate * (1 - baseline_rate) / (effect ** 2)
return int(n) + 1
def generate_roadmap(scored_experiments, capacity_per_sprint=3):
"""Generate a sprint-based experiment roadmap."""
sprints = []
remaining = list(scored_experiments)
sprint_num = 1
while remaining:
sprint = {
"sprint": sprint_num,
"experiments": remaining[:capacity_per_sprint],
}
sprints.append(sprint)
remaining = remaining[capacity_per_sprint:]
sprint_num += 1
return sprints
def get_demo_data():
return [
{"name": "Onboarding checklist v2", "hypothesis": "Adding progress bar increases activation by 15%", "metric": "7-day activation", "impact": 8, "confidence": 7, "ease": 9},
{"name": "Referral incentive test", "hypothesis": "Offering $10 credit doubles referral rate", "metric": "referral rate", "impact": 6, "confidence": 8, "ease": 7},
{"name": "Pricing page redesign", "hypothesis": "Simplified pricing increases signup by 20%", "metric": "signup rate", "impact": 9, "confidence": 5, "ease": 4},
{"name": "Email win-back sequence", "hypothesis": "4-email sequence reactivates 10% of churned", "metric": "reactivation rate", "impact": 5, "confidence": 6, "ease": 8},
{"name": "Social proof on landing page", "hypothesis": "Adding testimonials increases conversion by 10%", "metric": "landing page CVR", "impact": 6, "confidence": 7, "ease": 9},
{"name": "Free tool launch", "hypothesis": "Free calculator drives 500 signups/month", "metric": "monthly signups", "impact": 7, "confidence": 4, "ease": 3},
{"name": "In-app upgrade prompt", "hypothesis": "Context-triggered prompt increases upgrades by 25%", "metric": "upgrade rate", "impact": 8, "confidence": 6, "ease": 6},
]
def format_report(scored, framework, roadmap=None):
"""Format human-readable report."""
lines = []
lines.append("=" * 70)
lines.append(f"EXPERIMENT PRIORITIZATION ({framework.upper()} Framework)")
lines.append("=" * 70)
score_key = f"{framework}_score"
if framework == "ice":
lines.append(f"{'Rank':>4} {'Experiment':<30} {'I':>3} {'C':>3} {'E':>3} {'ICE':>6} {'Category':>10}")
lines.append("-" * 65)
for i, exp in enumerate(scored, 1):
lines.append(
f"{i:>4} {exp['name']:<30} {exp['impact']:>3} {exp['confidence']:>3} "
f"{exp['ease']:>3} {exp[score_key]:>6.1f} {exp['category']:>10}"
)
else:
lines.append(f"{'Rank':>4} {'Experiment':<25} {'R':>6} {'I':>4} {'C':>4} {'E':>4} {'RICE':>8} {'Cat':>10}")
lines.append("-" * 70)
for i, exp in enumerate(scored, 1):
lines.append(
f"{i:>4} {exp['name']:<25} {exp['reach']:>6} {exp['impact']:>4} "
f"{exp['confidence']:>4} {exp['effort']:>4} {exp[score_key]:>8.0f} {exp['category']:>10}"
)
lines.append("")
# Category summary
categories = {}
for exp in scored:
cat = exp["category"]
categories[cat] = categories.get(cat, 0) + 1
lines.append("--- CATEGORY SUMMARY ---")
for cat, count in sorted(categories.items()):
lines.append(f" {cat}: {count} experiments")
lines.append("")
# Roadmap
if roadmap:
lines.append("--- SPRINT ROADMAP ---")
for sprint in roadmap:
names = [e["name"] for e in sprint["experiments"]]
lines.append(f" Sprint {sprint['sprint']}: {', '.join(names)}")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Prioritize growth experiments with ICE/RICE")
parser.add_argument("input", nargs="?", help="JSON file with experiment data")
parser.add_argument("--framework", choices=["ice", "rice"], default="ice", help="Scoring framework")
parser.add_argument("--capacity", type=int, default=3, help="Experiments per sprint")
parser.add_argument("--json", action="store_true", dest="json_output", help="Output JSON")
parser.add_argument("--demo", action="store_true", help="Run with demo data")
args = parser.parse_args()
if args.demo:
experiments = get_demo_data()
elif args.input:
try:
with open(args.input, "r", encoding="utf-8") as f:
data = json.load(f)
experiments = data if isinstance(data, list) else data.get("experiments", [])
except FileNotFoundError:
print(f"Error: File not found: {args.input}", file=sys.stderr)
sys.exit(1)
else:
parser.print_help()
sys.exit(1)
if args.framework == "ice":
scored = score_ice(experiments)
else:
scored = score_rice(experiments)
roadmap = generate_roadmap(scored, args.capacity)
if args.json_output:
print(json.dumps({"experiments": scored, "roadmap": roadmap}, indent=2))
else:
print(format_report(scored, args.framework, roadmap))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Growth Loop Modeler - Model and forecast compound growth loops.
Simulates growth loops (viral, content, paid, product-led) over time,
calculating user acquisition, retention, and compounding effects.
Usage:
python growth_loop_modeler.py config.json
python growth_loop_modeler.py config.json --json
python growth_loop_modeler.py --type viral --users 1000 --k-factor 0.6 --months 12
python growth_loop_modeler.py --type plg --users 500 --free-to-paid 0.05 --expansion 0.15 --months 12
"""
import argparse
import json
import sys
import math
def model_viral_loop(initial_users, k_factor, cycle_time_days, churn_rate, months):
"""Model viral growth loop over time.
K-factor = invites_per_user * invite_conversion_rate
Each cycle: new_users = existing_users * k_factor
"""
cycles_per_month = 30 / max(cycle_time_days, 1)
timeline = []
total_users = initial_users
organic_acquired = 0
for month in range(1, months + 1):
month_start = total_users
new_viral = 0
for _ in range(int(cycles_per_month)):
cycle_new = int(total_users * k_factor)
new_viral += cycle_new
total_users += cycle_new
churned = int(total_users * churn_rate)
total_users = max(0, total_users - churned)
organic_acquired += new_viral
timeline.append({
"month": month,
"total_users": total_users,
"new_viral": new_viral,
"churned": churned,
"net_growth": total_users - month_start,
"growth_rate": round((total_users - month_start) / max(month_start, 1) * 100, 1),
})
return {
"loop_type": "viral",
"parameters": {
"initial_users": initial_users,
"k_factor": k_factor,
"cycle_time_days": cycle_time_days,
"monthly_churn_rate": churn_rate,
},
"timeline": timeline,
"summary": {
"final_users": total_users,
"total_viral_acquired": organic_acquired,
"growth_multiple": round(total_users / max(initial_users, 1), 2),
"sustainable": k_factor > churn_rate,
},
}
def model_plg_loop(initial_users, free_to_paid_rate, expansion_rate, churn_rate,
arpu, months, viral_coefficient=0.2):
"""Model product-led growth loop.
Free users -> Paid users -> Expansion -> Referrals -> More free users
"""
timeline = []
free_users = initial_users
paid_users = 0
total_revenue = 0
for month in range(1, months + 1):
# Conversions from free to paid
new_paid = int(free_users * free_to_paid_rate)
paid_users += new_paid
# Expansion revenue
expansion_users = int(paid_users * expansion_rate)
# Churn
churned_paid = int(paid_users * churn_rate)
paid_users = max(0, paid_users - churned_paid)
# Viral referrals from paid users
new_free_from_referrals = int(paid_users * viral_coefficient)
free_users += new_free_from_referrals
# Monthly revenue
monthly_revenue = paid_users * arpu
total_revenue += monthly_revenue
# MRR and growth
timeline.append({
"month": month,
"free_users": free_users,
"paid_users": paid_users,
"new_conversions": new_paid,
"new_referrals": new_free_from_referrals,
"churned": churned_paid,
"mrr": round(monthly_revenue, 2),
"total_revenue": round(total_revenue, 2),
})
ltv = arpu / max(churn_rate, 0.01)
return {
"loop_type": "product_led_growth",
"parameters": {
"initial_free_users": initial_users,
"free_to_paid_rate": free_to_paid_rate,
"expansion_rate": expansion_rate,
"monthly_churn_rate": churn_rate,
"arpu": arpu,
"viral_coefficient": viral_coefficient,
},
"timeline": timeline,
"summary": {
"final_free_users": free_users,
"final_paid_users": paid_users,
"final_mrr": round(paid_users * arpu, 2),
"total_revenue": round(total_revenue, 2),
"estimated_ltv": round(ltv, 2),
},
}
def model_content_loop(initial_monthly_traffic, content_pieces_per_month,
avg_traffic_per_piece, traffic_decay_rate,
conversion_rate, months):
"""Model content/SEO growth loop.
Content -> Organic traffic -> Leads -> Customers -> Revenue -> More content budget
"""
timeline = []
total_content = 0
cumulative_traffic = 0
for month in range(1, months + 1):
total_content += content_pieces_per_month
# Each piece generates traffic but decays over time
monthly_traffic = initial_monthly_traffic
for piece_month in range(total_content):
months_old = month - (piece_month // content_pieces_per_month)
if months_old > 0:
piece_traffic = avg_traffic_per_piece * ((1 - traffic_decay_rate) ** months_old)
monthly_traffic += max(0, piece_traffic)
conversions = int(monthly_traffic * conversion_rate)
cumulative_traffic += monthly_traffic
timeline.append({
"month": month,
"total_content_pieces": total_content,
"monthly_traffic": int(monthly_traffic),
"conversions": conversions,
"cumulative_traffic": int(cumulative_traffic),
})
return {
"loop_type": "content",
"parameters": {
"initial_monthly_traffic": initial_monthly_traffic,
"content_pieces_per_month": content_pieces_per_month,
"avg_traffic_per_piece": avg_traffic_per_piece,
"traffic_decay_rate": traffic_decay_rate,
"conversion_rate": conversion_rate,
},
"timeline": timeline,
"summary": {
"total_content": total_content,
"final_monthly_traffic": timeline[-1]["monthly_traffic"] if timeline else 0,
"traffic_growth_multiple": round(
timeline[-1]["monthly_traffic"] / max(initial_monthly_traffic, 1), 2
) if timeline else 0,
"total_conversions": sum(t["conversions"] for t in timeline),
},
}
def format_report(result):
"""Format human-readable growth model report."""
lines = []
lines.append("=" * 65)
lines.append(f"GROWTH LOOP MODEL: {result['loop_type'].upper().replace('_', ' ')}")
lines.append("=" * 65)
lines.append("\nParameters:")
for k, v in result["parameters"].items():
lines.append(f" {k.replace('_', ' ').title()}: {v}")
lines.append("")
lines.append("--- TIMELINE ---")
if result["loop_type"] == "viral":
lines.append(f"{'Month':>6} {'Total':>10} {'New Viral':>10} {'Churned':>10} {'Growth':>8}")
lines.append("-" * 50)
for t in result["timeline"]:
lines.append(f"{t['month']:>6} {t['total_users']:>10,} {t['new_viral']:>10,} {t['churned']:>10,} {t['growth_rate']:>7.1f}%")
elif result["loop_type"] == "product_led_growth":
lines.append(f"{'Month':>6} {'Free':>8} {'Paid':>8} {'MRR':>12} {'Total Rev':>12}")
lines.append("-" * 50)
for t in result["timeline"]:
lines.append(f"{t['month']:>6} {t['free_users']:>8,} {t['paid_users']:>8,} ${t['mrr']:>10,.0f} ${t['total_revenue']:>10,.0f}")
elif result["loop_type"] == "content":
lines.append(f"{'Month':>6} {'Content':>8} {'Traffic':>10} {'Conversions':>12}")
lines.append("-" * 40)
for t in result["timeline"]:
lines.append(f"{t['month']:>6} {t['total_content_pieces']:>8} {t['monthly_traffic']:>10,} {t['conversions']:>12,}")
lines.append("")
lines.append("--- SUMMARY ---")
for k, v in result["summary"].items():
label = k.replace("_", " ").title()
if isinstance(v, float):
lines.append(f" {label}: {v:,.2f}")
elif isinstance(v, int):
lines.append(f" {label}: {v:,}")
else:
lines.append(f" {label}: {v}")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Model and forecast compound growth loops")
parser.add_argument("input", nargs="?", help="JSON config file")
parser.add_argument("--type", choices=["viral", "plg", "content"], help="Growth loop type")
parser.add_argument("--users", type=int, default=1000, help="Initial users")
parser.add_argument("--k-factor", type=float, default=0.5, help="Viral K-factor")
parser.add_argument("--free-to-paid", type=float, default=0.05, help="PLG conversion rate")
parser.add_argument("--expansion", type=float, default=0.1, help="PLG expansion rate")
parser.add_argument("--churn", type=float, default=0.05, help="Monthly churn rate")
parser.add_argument("--arpu", type=float, default=50, help="Average revenue per user")
parser.add_argument("--months", type=int, default=12, help="Forecast months")
parser.add_argument("--json", action="store_true", dest="json_output", help="Output JSON")
args = parser.parse_args()
if args.input:
try:
with open(args.input, "r", encoding="utf-8") as f:
config = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input}", file=sys.stderr)
sys.exit(1)
loop_type = config.get("type", "viral")
elif args.type:
loop_type = args.type
config = {}
else:
parser.print_help()
sys.exit(1)
if loop_type == "viral":
result = model_viral_loop(
config.get("initial_users", args.users),
config.get("k_factor", args.k_factor),
config.get("cycle_time_days", 14),
config.get("churn_rate", args.churn),
config.get("months", args.months),
)
elif loop_type == "plg":
result = model_plg_loop(
config.get("initial_users", args.users),
config.get("free_to_paid_rate", args.free_to_paid),
config.get("expansion_rate", args.expansion),
config.get("churn_rate", args.churn),
config.get("arpu", args.arpu),
config.get("months", args.months),
)
elif loop_type == "content":
result = model_content_loop(
config.get("initial_monthly_traffic", args.users),
config.get("content_pieces_per_month", 4),
config.get("avg_traffic_per_piece", 200),
config.get("traffic_decay_rate", 0.1),
config.get("conversion_rate", 0.02),
config.get("months", args.months),
)
else:
print(f"Unknown loop type: {loop_type}", file=sys.stderr)
sys.exit(1)
if args.json_output:
print(json.dumps(result, indent=2))
else:
print(format_report(result))
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""Viral Coefficient Calculator - Calculate and forecast viral growth metrics.
Computes K-factor, viral cycle time, effective viral rate, and forecasts
user growth from referral data.
Usage:
python viral_coefficient_calculator.py referral_data.json
python viral_coefficient_calculator.py referral_data.json --json
python viral_coefficient_calculator.py --invites 5000 --conversions 800 --users 2000 --cycle-days 7
"""
import argparse
import json
import sys
import math
def calculate_viral_metrics(total_users, invites_sent, invite_conversions,
cycle_time_days, time_period_days=30):
"""Calculate comprehensive viral growth metrics."""
# K-factor = invites per user * conversion rate of invites
invites_per_user = invites_sent / max(total_users, 1)
invite_conversion_rate = invite_conversions / max(invites_sent, 1)
k_factor = invites_per_user * invite_conversion_rate
# Effective viral rate (accounts for cycle time)
cycles_per_period = time_period_days / max(cycle_time_days, 1)
# Branching factor (how many users each cohort eventually creates)
# For K < 1: total = 1 / (1 - K) [geometric series]
if k_factor < 1:
branching_factor = 1 / (1 - k_factor)
else:
branching_factor = float("inf") # True viral growth
# Forecast: users after N cycles
forecast = []
users = total_users
for cycle in range(1, int(cycles_per_period * 12) + 1):
new_users = int(users * k_factor)
users += new_users
month = cycle * cycle_time_days / 30
if cycle % max(1, int(cycles_per_period)) == 0:
forecast.append({
"month": round(month),
"total_users": users,
"new_from_viral": new_users,
})
# Time to reach milestones
milestones = {}
if k_factor > 0:
for target_multiple in [2, 5, 10]:
target = total_users * target_multiple
if k_factor >= 1:
# Exponential growth
cycles_needed = math.log(target / total_users) / math.log(1 + k_factor)
days_needed = cycles_needed * cycle_time_days
milestones[f"{target_multiple}x_users"] = {
"target": target,
"estimated_days": round(days_needed),
"estimated_months": round(days_needed / 30, 1),
}
elif k_factor > 0:
# Will the geometric series reach the target?
max_reachable = total_users * branching_factor
if max_reachable >= target:
# Approximate time
cycles_needed = math.log(1 - (target / max_reachable)) / math.log(k_factor)
days_needed = abs(cycles_needed) * cycle_time_days
milestones[f"{target_multiple}x_users"] = {
"target": target,
"estimated_days": round(days_needed),
"estimated_months": round(days_needed / 30, 1),
}
else:
milestones[f"{target_multiple}x_users"] = {
"target": target,
"estimated_days": None,
"note": f"K-factor too low. Max reachable: {int(max_reachable):,}",
}
# Assessment
if k_factor >= 1.0:
assessment = "TRUE VIRAL GROWTH: Each user brings more than one new user. Growth is exponential."
elif k_factor >= 0.7:
assessment = "STRONG VIRAL BOOST: Significant viral amplification of paid/organic acquisition."
elif k_factor >= 0.4:
assessment = "MODERATE VIRAL EFFECT: Viral referrals supplement other channels meaningfully."
elif k_factor >= 0.1:
assessment = "WEAK VIRAL EFFECT: Minimal viral contribution. Focus on improving invite UX and conversion."
else:
assessment = "NO MEANINGFUL VIRALITY: Consider implementing referral incentives or in-product sharing."
# Improvement scenarios
scenarios = []
for improve_invites in [1.0, 1.25, 1.5]:
for improve_conv in [1.0, 1.25, 1.5]:
if improve_invites == 1.0 and improve_conv == 1.0:
continue
new_ipu = invites_per_user * improve_invites
new_conv = min(1.0, invite_conversion_rate * improve_conv)
new_k = new_ipu * new_conv
scenarios.append({
"invites_per_user_change": f"+{int((improve_invites - 1) * 100)}%",
"conversion_change": f"+{int((improve_conv - 1) * 100)}%",
"new_k_factor": round(new_k, 3),
"improvement": f"+{round((new_k - k_factor) / max(k_factor, 0.001) * 100)}%",
})
return {
"metrics": {
"k_factor": round(k_factor, 3),
"invites_per_user": round(invites_per_user, 2),
"invite_conversion_rate": round(invite_conversion_rate, 4),
"cycle_time_days": cycle_time_days,
"branching_factor": round(branching_factor, 2) if branching_factor != float("inf") else "infinite",
"cycles_per_month": round(30 / max(cycle_time_days, 1), 1),
},
"assessment": assessment,
"is_truly_viral": k_factor >= 1.0,
"forecast": forecast[:12],
"milestones": milestones,
"improvement_scenarios": scenarios,
}
def format_report(result):
"""Format human-readable report."""
lines = []
lines.append("=" * 60)
lines.append("VIRAL COEFFICIENT ANALYSIS")
lines.append("=" * 60)
m = result["metrics"]
lines.append(f"K-Factor: {m['k_factor']}")
lines.append(f"Invites per user: {m['invites_per_user']}")
lines.append(f"Invite conversion rate: {m['invite_conversion_rate']:.1%}")
lines.append(f"Cycle time: {m['cycle_time_days']} days")
lines.append(f"Branching factor: {m['branching_factor']}")
lines.append(f"Cycles per month: {m['cycles_per_month']}")
lines.append("")
lines.append(f"Assessment: {result['assessment']}")
lines.append("")
# Forecast
if result["forecast"]:
lines.append("--- GROWTH FORECAST ---")
lines.append(f"{'Month':>6} {'Users':>12} {'New Viral':>12}")
lines.append("-" * 35)
for f in result["forecast"]:
lines.append(f"{f['month']:>6} {f['total_users']:>12,} {f['new_from_viral']:>12,}")
lines.append("")
# Milestones
if result["milestones"]:
lines.append("--- GROWTH MILESTONES ---")
for label, data in result["milestones"].items():
if data.get("estimated_days"):
lines.append(f" {label}: ~{data['estimated_months']} months ({data['estimated_days']} days)")
elif data.get("note"):
lines.append(f" {label}: {data['note']}")
lines.append("")
# Improvement scenarios
if result["improvement_scenarios"]:
lines.append("--- IMPROVEMENT SCENARIOS ---")
lines.append(f"{'Invites':>10} {'Conv':>10} {'New K':>8} {'Change':>10}")
for s in result["improvement_scenarios"]:
lines.append(f"{s['invites_per_user_change']:>10} {s['conversion_change']:>10} {s['new_k_factor']:>8} {s['improvement']:>10}")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Calculate viral growth metrics")
parser.add_argument("input", nargs="?", help="JSON file with referral data")
parser.add_argument("--invites", type=int, help="Total invites sent")
parser.add_argument("--conversions", type=int, help="Invites that converted")
parser.add_argument("--users", type=int, help="Total users who could invite")
parser.add_argument("--cycle-days", type=int, default=7, help="Viral cycle time in days")
parser.add_argument("--json", action="store_true", dest="json_output", help="Output JSON")
args = parser.parse_args()
if args.input:
try:
with open(args.input, "r", encoding="utf-8") as f:
data = json.load(f)
except FileNotFoundError:
print(f"Error: File not found: {args.input}", file=sys.stderr)
sys.exit(1)
result = calculate_viral_metrics(
data.get("total_users", 1000),
data.get("invites_sent", 0),
data.get("invite_conversions", 0),
data.get("cycle_time_days", 7),
)
elif args.invites and args.conversions and args.users:
result = calculate_viral_metrics(args.users, args.invites, args.conversions, args.cycle_days)
else:
parser.print_help()
sys.exit(1)
if args.json_output:
print(json.dumps(result, indent=2))
else:
print(format_report(result))
if __name__ == "__main__":
main()
Related skills
How it compares
Pick growth-marketer when you need a structured experiment plan across the full funnel, not isolated SEO copy or single-channel ad creative.
FAQ
What funnel stages does growth-marketer cover?
growth-marketer covers acquisition, activation, retention, and referral. It helps developers map experiments and cohort analyses across all four stages rather than focusing on a single top-of-funnel tactic.
Does growth-marketer replace analytics tooling?
growth-marketer plans experiments and measurement frameworks but does not instrument events or configure ad platforms. Pair it with analytics implementation work to ensure cohort and conversion data exists.