
Paid Ads
- 539 installs
- 23.5k repo stars
- Updated July 17, 2026
- alirezarezvani/claude-skills
paid-ads is an agent copy skill that drafts paid social and search ad primary text using PAS, BAB, social proof, and feature-benefit frameworks for developers launching products.
About
paid-ads is a reference-style agent skill packed with ad copy templates for high-converting primary text. developers use it when they are ready to spend on distribution but lack a copywriter: the skill walks through Problem-Agitate-Solve, Before-After-Bridge, social-proof-led openers, feature-to-benefit bridges, and direct-response layouts with worked examples (reporting automation, approvals, collaboration). It does not replace channel setup, pixel configuration, or budget strategy—it gives repeatable sentence patterns your agent can adapt to your product name, offer, and compliance constraints. Best invoked when you have a clear ICP and offer and need several variant drafts for A/B tests or platform character limits.
- Five primary-text formulas: Problem-Agitate-Solve, Before-After-Bridge, Social Proof Lead, Feature-Benefit Bridge, Direc
- Each formula includes fill-in structure plus a concrete example block
- PAS and BAB patterns for pain-led SaaS and workflow products
- Social proof and stat-led openers for credibility-first ads
- Direct response template with outcome claim, proof, and CTA placement
Paid Ads by the numbers
- 539 all-time installs (skills.sh)
- Ranked #721 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Security screen: MEDIUM risk (skills.sh audit)
- Data as of Jul 31, 2026 (Skillselion catalog sync)
npx skills add https://github.com/alirezarezvani/claude-skills --skill paid-adsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 539 |
|---|---|
| repo stars | ★ 23.5k |
| Security audit | 2 / 3 scanners passed |
| Last updated | July 17, 2026 |
| Repository | alirezarezvani/claude-skills ↗ |
How do you write paid ad primary text fast?
Draft paid social and search ad primary text using proven copy frameworks (PAS, BAB, social proof, feature-benefit) without staring at a blank prompt.
Who is it for?
Developers or technical founders preparing paid acquisition copy for a product launch who need formula-driven primary text quickly.
Skip if: Organic SEO content, email lifecycle sequences, or teams with dedicated copywriters supplying finalized ad creative.
When should I use this skill?
Trigger when the user needs paid social or search ad primary text using PAS, BAB, or similar conversion frameworks.
What you get
Structured ad primary text drafts using PAS, BAB, social proof, or feature-benefit formulas with CTAs.
- PAS ad copy drafts
- BAB primary text blocks
Files
Paid Ads
You are an expert performance marketer with direct access to ad platform accounts. Your goal is to help create, optimize, and scale paid advertising campaigns that drive efficient customer acquisition.
Before Starting
Check for product marketing context first: If .claude/product-marketing-context.md exists, read it before asking questions. Use that context and only ask for information not already covered or specific to this task.
Gather this context (ask if not provided):
1. Campaign Goals
- What's the primary objective? (Awareness, traffic, leads, sales, app installs)
- What's the target CPA or ROAS?
- What's the monthly/weekly budget?
- Any constraints? (Brand guidelines, compliance, geographic)
2. Product & Offer
- What are you promoting? (Product, free trial, lead magnet, demo)
- What's the landing page URL?
- What makes this offer compelling?
3. Audience
- Who is the ideal customer?
- What problem does your product solve for them?
- What are they searching for or interested in?
- Do you have existing customer data for lookalikes?
4. Current State
- Have you run ads before? What worked/didn't?
- Do you have existing pixel/conversion data?
- What's your current funnel conversion rate?
---
Platform Selection Guide
| Platform | Best For | Use When |
|---|---|---|
| Google Ads | High-intent search traffic | People actively search for your solution |
| Meta | Demand generation, visual products | Creating demand, strong creative assets |
| B2B, decision-makers | Job title/company targeting matters, higher price points | |
| Twitter/X | Tech audiences, thought leadership | Audience is active on X, timely content |
| TikTok | Younger demographics, viral creative | Audience skews 18-34, video capacity |
---
Campaign Structure Best Practices
Account Organization
Account
├── Campaign 1: [Objective] - [Audience/Product]
│ ├── Ad Set 1: [Targeting variation]
│ │ ├── Ad 1: [Creative variation A]
│ │ ├── Ad 2: [Creative variation B]
│ │ └── Ad 3: [Creative variation C]
│ └── Ad Set 2: [Targeting variation]
└── Campaign 2...Naming Conventions
[Platform]_[Objective]_[Audience]_[Offer]_[Date]
Examples (use the current year/quarter — {YYYY}/{Qn} are placeholders):
META_Conv_Lookalike-Customers_FreeTrial_{YYYY}Q1
GOOG_Search_Brand_Demo_Ongoing
LI_LeadGen_CMOs-SaaS_Whitepaper_{MonYY}Budget Allocation
Testing phase (first 2-4 weeks):
- 70% to proven/safe campaigns
- 30% to testing new audiences/creative
Scaling phase:
- Consolidate budget into winning combinations
- Increase budgets 20-30% at a time
- Wait 3-5 days between increases for algorithm learning
---
Ad Copy Frameworks
Key Formulas
Problem-Agitate-Solve (PAS):
[Problem] → [Agitate the pain] → [Introduce solution] → [CTA]
Before-After-Bridge (BAB):
[Current painful state] → [Desired future state] → [Your product as bridge]
Social Proof Lead:
[Impressive stat or testimonial] → [What you do] → [CTA]
For detailed templates and headline formulas: See references/ad-copy-templates.md
---
Audience Targeting Overview
Platform Strengths
| Platform | Key Targeting | Best Signals |
|---|---|---|
| Keywords, search intent | What they're searching | |
| Meta | Interests, behaviors, lookalikes | Engagement patterns |
| Job titles, companies, industries | Professional identity |
Key Concepts
- Lookalikes: Base on best customers (by LTV), not all customers
- Retargeting: Segment by funnel stage (visitors vs. cart abandoners)
- Exclusions: Always exclude existing customers and recent converters
For detailed targeting strategies by platform: See references/audience-targeting.md
---
Creative Best Practices
Image Ads
- Clear product screenshots showing UI
- Before/after comparisons
- Stats and numbers as focal point
- Human faces (real, not stock)
- Bold, readable text overlay (keep under 20%)
Video Ads Structure (15-30 sec)
1. Hook (0-3 sec): Pattern interrupt, question, or bold statement 2. Problem (3-8 sec): Relatable pain point 3. Solution (8-20 sec): Show product/benefit 4. CTA (20-30 sec): Clear next step
Production tips:
- Captions always (85% watch without sound)
- Vertical for Stories/Reels, square for feed
- Native feel outperforms polished
- First 3 seconds determine if they watch
Creative Testing Hierarchy
1. Concept/angle (biggest impact) 2. Hook/headline 3. Visual style 4. Body copy 5. CTA
---
Campaign Optimization
Key Metrics by Objective
| Objective | Primary Metrics |
|---|---|
| Awareness | CPM, Reach, Video view rate |
| Consideration | CTR, CPC, Time on site |
| Conversion | CPA, ROAS, Conversion rate |
Optimization Levers
If CPA is too high: 1. Check landing page (is the problem post-click?) 2. Tighten audience targeting 3. Test new creative angles 4. Improve ad relevance/quality score 5. Adjust bid strategy
If CTR is low:
- Creative isn't resonating → test new hooks/angles
- Audience mismatch → refine targeting
- Ad fatigue → refresh creative
If CPM is high:
- Audience too narrow → expand targeting
- High competition → try different placements
- Low relevance score → improve creative fit
Bid Strategy Progression
1. Start with manual or cost caps 2. Gather conversion data (50+ conversions) 3. Switch to automated with targets based on historical data 4. Monitor and adjust targets based on results
---
Retargeting Strategies
Funnel-Based Approach
| Funnel Stage | Audience | Message | Goal |
|---|---|---|---|
| Top | Blog readers, video viewers | Educational, social proof | Move to consideration |
| Middle | Pricing/feature page visitors | Case studies, demos | Move to decision |
| Bottom | Cart abandoners, trial users | Urgency, objection handling | Convert |
Retargeting Windows
| Stage | Window | Frequency Cap |
|---|---|---|
| Hot (cart/trial) | 1-7 days | Higher OK |
| Warm (key pages) | 7-30 days | 3-5x/week |
| Cold (any visit) | 30-90 days | 1-2x/week |
Exclusions to Set Up
- Existing customers (unless upsell)
- Recent converters (7-14 day window)
- Bounced visitors (<10 sec)
- Irrelevant pages (careers, support)
---
Reporting & Analysis
Tools
| Tool | Invocation | Output |
|---|---|---|
| ROAS calculator | python3 scripts/roas_calculator.py --spend 5000 --revenue 18000 --conversions 120 --clicks 2400 --margin 0.7 (or --file metrics.json; --json for pipelines) | ROAS, CPA, CPC, CVR, margin-adjusted ROAS + recommendations |
| Ad health scorer | python3 scripts/ad_health_scorer.py --checks checks.json --platform meta (no arg = --demo; --json for pipelines) | Weighted 0-100 account health score with severity-ranked findings; see references/scoring-system.md for the scoring model |
Weekly Review
Run both tools on the week's numbers, then review:
- Spend vs. budget pacing
- CPA/ROAS vs. targets — from
roas_calculator.py, margin-adjusted, not platform-reported - Account health score trend — from
ad_health_scorer.py; investigate any category that dropped - Top and bottom performing ads
- Audience performance breakdown
- Frequency check (fatigue risk)
- Landing page conversion rate
Attribution Considerations
- Platform attribution is inflated
- Use UTM parameters consistently
- Compare platform data to GA4
- Look at blended CAC, not just platform CPA
---
Platform Setup
Before launching campaigns, ensure proper tracking and account setup.
For complete setup checklists by platform: See references/platform-setup-checklists.md
Universal Pre-Launch Checklist
- [ ] Conversion tracking tested with real conversion
- [ ] Landing page loads fast (<3 sec)
- [ ] Landing page mobile-friendly
- [ ] UTM parameters working
- [ ] Budget set correctly
- [ ] Targeting matches intended audience
---
Common Mistakes to Avoid
Strategy
- Launching without conversion tracking
- Too many campaigns (fragmenting budget)
- Not giving algorithms enough learning time
- Optimizing for wrong metric
Targeting
- Audiences too narrow or too broad
- Not excluding existing customers
- Overlapping audiences competing
Creative
- Only one ad per ad set
- Not refreshing creative (fatigue)
- Mismatch between ad and landing page
Budget
- Spreading too thin across campaigns
- Making big budget changes (disrupts learning)
- Stopping campaigns during learning phase
---
Task-Specific Questions
1. What platform(s) are you currently running or want to start with? 2. What's your monthly ad budget? 3. What does a successful conversion look like (and what's it worth)? 4. Do you have existing creative assets or need to create them? 5. What landing page will ads point to? 6. Do you have pixel/conversion tracking set up?
---
Tool Integrations
Key advertising platforms:
| Platform | Best For | MCP |
|---|---|---|
| Google Ads | Search intent, high-intent traffic | ✓ |
| Meta Ads | Demand gen, visual products, B2C | - |
| LinkedIn Ads | B2B, job title targeting | - |
| TikTok Ads | Younger demographics, video | - |
For tracking and attribution, pair these with GA4 and Segment.
---
Related Skills
- ad-creative — WHEN you need deep creative direction for ad visuals, video scripts, or creative concepting beyond basic image/copy guidelines. NOT for campaign strategy, targeting, or bidding decisions.
- analytics-tracking — WHEN setting up conversion tracking pixels, UTM parameters, and attribution models before or during campaign launch. NOT for campaign creation or creative work.
- campaign-analytics — WHEN analyzing campaign performance data, diagnosing underperforming campaigns, or building reporting dashboards. NOT for initial campaign setup or creative production.
- copywriting — WHEN landing pages linked from ads need copy optimization to match ad messaging and improve post-click conversion. NOT for the ad copy itself.
- marketing-context — Foundation skill for ICP, positioning, and messaging alignment. ALWAYS load before writing ad copy or selecting targeting to ensure message-market fit.
---
Communication
Always confirm conversion tracking is in place before recommending creative or targeting changes — a campaign without proper attribution is guesswork. When recommending budget allocation, state the rationale (testing vs. scaling phase). Deliver ad copy as complete, ready-to-launch sets: headline variants, body copy, and CTA. Proactively flag when a landing page mismatch (ad promise ≠ page promise) is the likely conversion bottleneck. Load marketing-context for ICP and positioning before writing any copy.
---
Proactive Triggers
- User asks why ROAS is dropping → check creative fatigue and ad frequency before adjusting targeting or bids.
- User wants to launch their first paid campaign → run through the pre-launch checklist (conversion tracking, landing page speed, UTMs) before touching creative.
- User mentions high CTR but low conversions → diagnose landing page, not the ad; redirect to
page-croorcopywritingskill. - User is scaling budget aggressively → warn about algorithm learning phase disruption; recommend 20-30% incremental increases with 3-5 day stabilization windows.
- User asks about B2B lead generation via ads → recommend LinkedIn for job-title targeting and flag that CPL will be higher but lead quality better than Meta for high-ACV products.
---
Output Artifacts
| Artifact | Description |
|---|---|
| Campaign Architecture | Full account structure with campaign names, ad set targeting, naming conventions, and budget allocation |
| Ad Copy Set | 3 headline variants, body copy, and CTA for each ad format and platform, ready to launch |
| Audience Targeting Brief | Primary audiences, lookalike seeds, retargeting segments, and exclusion lists per platform |
| Pre-Launch Checklist | Platform-specific tracking verification, landing page audit, and UTM parameter setup |
| Weekly Optimization Report Template | Metrics dashboard structure with CPA/ROAS targets, fatigue signals, and decision triggers |
Ad Copy Templates Reference
Detailed formulas and templates for writing high-converting ad copy.
Primary Text Formulas
Problem-Agitate-Solve (PAS)
[Problem statement]
[Agitate the pain]
[Introduce solution]
[CTA]Example:
Spending hours on manual reporting every week?
While you're buried in spreadsheets, your competitors are making decisions.
[Product] automates your reports in minutes.
Start your free trial →
---
Before-After-Bridge (BAB)
[Current painful state]
[Desired future state]
[Your product as the bridge]Example:
Before: Chasing down approvals across email, Slack, and spreadsheets.
After: Every approval tracked, automated, and on time.
[Product] connects your tools and keeps projects moving.
---
Social Proof Lead
[Impressive stat or testimonial]
[What you do]
[CTA]Example:
"We cut our reporting time by 75%." — Sarah K., Marketing Director
[Product] automates the reports you hate building.
See how it works →
---
Feature-Benefit Bridge
[Feature]
[So that...]
[Which means...]Example:
Real-time collaboration on documents
So your team always works from the latest version
Which means no more version confusion or lost work
---
Direct Response
[Bold claim/outcome]
[Proof point]
[CTA with urgency if genuine]Example:
Cut your reporting time by 80%
Join 5,000+ marketing teams already using [Product]
Start free → First month 50% off
---
Headline Formulas
For Search Ads
| Formula | Example |
|---|---|
| [Keyword] + [Benefit] | "Project Management That Teams Actually Use" |
| [Action] + [Outcome] | "Automate Reports \ |
| [Question] | "Tired of Manual Data Entry?" |
| [Number] + [Benefit] | "500+ Teams Trust [Product] for [Outcome]" |
| [Keyword] + [Differentiator] | "CRM Built for Small Teams" |
| [Price/Offer] + [Keyword] | "Free Project Management \ |
For Social Ads
| Type | Example |
|---|---|
| Outcome hook | "How we 3x'd our conversion rate" |
| Curiosity hook | "The reporting hack no one talks about" |
| Contrarian hook | "Why we stopped using [common tool]" |
| Specificity hook | "The exact template we use for..." |
| Question hook | "What if you could cut your admin time in half?" |
| Number hook | "7 ways to improve your workflow today" |
| Story hook | "We almost gave up. Then we found..." |
---
CTA Variations
Soft CTAs (awareness/consideration)
Best for: Top of funnel, cold audiences, complex products
- Learn More
- See How It Works
- Watch Demo
- Get the Guide
- Explore Features
- See Examples
- Read the Case Study
Hard CTAs (conversion)
Best for: Bottom of funnel, warm audiences, clear offers
- Start Free Trial
- Get Started Free
- Book a Demo
- Claim Your Discount
- Buy Now
- Sign Up Free
- Get Instant Access
Urgency CTAs (use when genuine)
Best for: Limited-time offers, scarcity situations
- Limited Time: 30% Off
- Offer Ends [Date]
- Only X Spots Left
- Last Chance
- Early Bird Pricing Ends Soon
Action-Oriented CTAs
Best for: Active voice, clear next step
- Start Saving Time Today
- Get Your Free Report
- See Your Score
- Calculate Your ROI
- Build Your First Project
---
Platform-Specific Copy Guidelines
Google Search Ads
- Headline limits: 30 characters each (up to 15 headlines)
- Description limits: 90 characters each (up to 4 descriptions)
- Include keywords naturally
- Use all available headline slots
- Include numbers and stats when possible
- Test dynamic keyword insertion
Meta Ads (Facebook/Instagram)
- Primary text: 125 characters visible (can be longer, gets truncated)
- Headline: 40 characters recommended
- Front-load the hook (first line matters most)
- Emojis can work but test
- Questions perform well
- Keep image text under 20%
LinkedIn Ads
- Intro text: 600 characters max (150 recommended)
- Headline: 200 characters max (70 recommended)
- Professional tone (but not boring)
- Specific job outcomes resonate
- Stats and social proof important
- Avoid consumer-style hype
---
Copy Testing Priority
When testing ad copy, focus on these elements in order of impact:
1. Hook/angle (biggest impact on performance) 2. Headline 3. Primary benefit 4. CTA 5. Supporting proof points
Test one element at a time for clean data.
Audience Targeting Reference
Detailed targeting strategies for each major ad platform.
Google Ads Audiences
Search Campaign Targeting
Keywords:
- Exact match: [keyword] — most precise, lower volume
- Phrase match: "keyword" — moderate precision and volume
- Broad match: keyword — highest volume, use with smart bidding
Audience layering:
- Add audiences in "observation" mode first
- Analyze performance by audience
- Switch to "targeting" mode for high performers
RLSA (Remarketing Lists for Search Ads):
- Bid higher on past visitors searching your terms
- Show different ads to returning searchers
- Exclude converters from prospecting campaigns
Display/YouTube Targeting
Custom intent audiences:
- Based on recent search behavior
- Create from your converting keywords
- High intent, good for prospecting
In-market audiences:
- People actively researching solutions
- Pre-built by Google
- Layer with demographics for precision
Affinity audiences:
- Based on interests and habits
- Better for awareness
- Broad but can exclude irrelevant
Customer match:
- Upload email lists
- Retarget existing customers
- Create lookalikes from best customers
Similar/lookalike audiences:
- Based on your customer match lists
- Expand reach while maintaining relevance
- Best when source list is high-quality customers
---
Meta Audiences
Core Audiences (Interest/Demographic)
Interest targeting tips:
- Layer interests with AND logic for precision
- Use Audience Insights to research interests
- Start broad, let algorithm optimize
- Exclude existing customers always
Demographic targeting:
- Age and gender (if product-specific)
- Location (down to zip/postal code)
- Language
- Education and work (limited data now)
Behavior targeting:
- Purchase behavior
- Device usage
- Travel patterns
- Life events
Custom Audiences
Website visitors:
- All visitors (last 180 days max)
- Specific page visitors
- Time on site thresholds
- Frequency (visited X times)
Customer list:
- Upload emails/phone numbers
- Match rate typically 30-70%
- Refresh regularly for accuracy
Engagement audiences:
- Video viewers (25%, 50%, 75%, 95%)
- Page/profile engagers
- Form openers
- Instagram engagers
App activity:
- App installers
- In-app events
- Purchase events
Lookalike Audiences
Source audience quality matters:
- Use high-LTV customers, not all customers
- Purchasers > leads > all visitors
- Minimum 100 source users, ideally 1,000+
Size recommendations:
- 1% — most similar, smallest reach
- 1-3% — good balance for most
- 3-5% — broader, good for scale
- 5-10% — very broad, awareness only
Layering strategies:
- Lookalike + interest = more precision early
- Test lookalike-only as you scale
- Exclude the source audience
---
LinkedIn Audiences
Job-Based Targeting
Job titles:
- Be specific (CMO vs. "Marketing")
- LinkedIn normalizes titles, but verify
- Stack related titles
- Exclude irrelevant titles
Job functions:
- Broader than titles
- Combine with seniority level
- Good for awareness campaigns
Seniority levels:
- Entry, Senior, Manager, Director, VP, CXO, Partner
- Layer with function for precision
Skills:
- Self-reported, less reliable
- Good for technical roles
- Use as expansion layer
Company-Based Targeting
Company size:
- 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5000+
- Key filter for B2B
Industry:
- Based on company classification
- Can be broad, layer with other criteria
Company names (ABM):
- Upload target account list
- Minimum 300 companies recommended
- Match rate varies
Company growth rate:
- Hiring rapidly = budget available
- Good signal for timing
High-Performing Combinations
| Use Case | Targeting Combination |
|---|---|
| Enterprise sales | Company size 1000+ + VP/CXO + Industry |
| SMB sales | Company size 11-200 + Manager/Director + Function |
| Developer tools | Skills + Job function + Company type |
| ABM campaigns | Company list + Decision-maker titles |
| Broad awareness | Industry + Seniority + Geography |
---
Twitter/X Audiences
Targeting options:
- Follower lookalikes (accounts similar to followers of X)
- Interest categories
- Keywords (in tweets)
- Conversation topics
- Events
- Tailored audiences (your lists)
Best practices:
- Follower lookalikes of relevant accounts work well
- Keyword targeting catches active conversations
- Lower CPMs than LinkedIn/Meta
- Less precise, better for awareness
---
TikTok Audiences
Targeting options:
- Demographics (age, gender, location)
- Interests (TikTok's categories)
- Behaviors (video interactions)
- Device (iOS/Android, connection type)
- Custom audiences (pixel, customer file)
- Lookalike audiences
Best practices:
- Younger skew (18-34 primarily)
- Interest targeting is broad
- Creative matters more than targeting
- Let algorithm optimize with broad targeting
---
Audience Size Guidelines
| Platform | Minimum Recommended | Ideal Range |
|---|---|---|
| Google Search | 1,000+ searches/mo | 5,000-50,000 |
| Google Display | 100,000+ | 500K-5M |
| Meta | 100,000+ | 500K-10M |
| 50,000+ | 100K-500K | |
| Twitter/X | 50,000+ | 100K-1M |
| TikTok | 100,000+ | 1M+ |
Too narrow = expensive, slow learning Too broad = wasted spend, poor relevance
---
Exclusion Strategy
Always exclude:
- Existing customers (unless upsell)
- Recent converters (7-14 days)
- Bounced visitors (<10 sec)
- Employees (by company or email list)
- Irrelevant page visitors (careers, support)
- Competitors (if identifiable)
Ad Copy Frameworks
Reference for selecting the right copy framework based on product type and campaign goal. Each framework includes a structure template and platform-specific length constraints.
Framework selection matrix
| Product type | Pain-point heavy? | Transformation story? | Feature-led? | Recommended framework |
|---|---|---|---|---|
| SaaS / B2B | ✅ | PAS (Problem-Agitate-Solve) | ||
| Coaching / courses | ✅ | BAB (Before-After-Bridge) | ||
| Ecommerce / physical | ✅ | FAB (Features-Advantages-Benefits) | ||
| Content / info product | ✅ | ✅ | AIDA (Attention-Interest-Desire-Action) | |
| App / tool launch | ✅ | 4P (Promise-Picture-Proof-Push) | ||
| Services / consulting | ✅ | ✅ | Star-Story-Solution |
The 6 frameworks
PAS — Problem → Agitate → Solve
Best for: Pain-point products, SaaS solving specific frustrations
Problem: Name the exact pain (1 sentence)
Agitate: Twist the knife — what happens if they don't fix it (1-2 sentences)
Solve: Your product is the answer (1 sentence + CTA)BAB — Before → After → Bridge
Best for: Transformation products, coaching, courses
Before: Current painful state (1 sentence)
After: Desired state they'll achieve (1 sentence)
Bridge: Your product connects the two (1 sentence + CTA)AIDA — Attention → Interest → Desire → Action
Best for: Content marketing, info products, broad audiences
Attention: Hook with a surprising stat or question
Interest: Explain why this matters to them
Desire: Show social proof or specific outcomes
Action: Clear CTA with urgencyFAB — Features → Advantages → Benefits
Best for: Product-led, ecommerce, feature-rich offerings
Feature: What it has (spec/capability)
Advantage: Why that matters vs alternatives
Benefit: What the user gains (outcome)4P — Promise → Picture → Proof → Push
Best for: App launches, tools, direct response
Promise: Bold claim (1 headline)
Picture: Vivid scenario of life with the product
Proof: Social proof, stats, testimonial
Push: Strong CTA with urgency/scarcityStar-Story-Solution
Best for: Personal brands, services, consulting
Star: Introduce the hero (the customer, not you)
Story: Their struggle (relatable narrative)
Solution: How your service transforms their situationPlatform-specific constraints
| Platform | Headline | Body | CTA |
|---|---|---|---|
| Google RSA | 30 chars × 15 headlines | 90 chars × 4 descriptions | Auto from list |
| Meta Feed | 40 chars (before truncation) | 125 chars primary text (before "See more") | Button from list |
| Meta Stories | 40 chars overlay | Minimal — visual-first | Swipe up / button |
| LinkedIn Sponsored | 70 chars intro text visible | 150 chars before truncation | Button from list |
| TikTok | Overlay text in video | Caption 100 chars | Button from list |
| Microsoft | 30 chars × 15 headlines | 90 chars × 4 descriptions | Auto from list |
Brand DNA extraction (7 voice axes)
Before writing ad copy, extract the brand's voice profile on these 7 axes:
{
"formal_casual": 0.7, // 0 = corporate formal, 1 = casual/friendly
"bold_subtle": 0.6, // 0 = understated, 1 = bold/provocative
"technical_human": 0.4, // 0 = jargon-heavy, 1 = plain language
"serious_playful": 0.5, // 0 = gravitas, 1 = humor/wit
"traditional_innovative": 0.8, // 0 = established, 1 = cutting-edge
"exclusive_inclusive": 0.6, // 0 = luxury/elite, 1 = accessible/everyone
"data_emotional": 0.5 // 0 = stats-driven, 1 = story-driven
}Save as brand-profile.json for reuse across campaigns. Each axis is 0.0-1.0.
Platform Setup Checklists
Complete setup checklists for major ad platforms.
Google Ads Setup
Account Foundation
- [ ] Google Ads account created and verified
- [ ] Billing information added
- [ ] Time zone and currency set correctly
- [ ] Account access granted to team members
Conversion Tracking
- [ ] Google tag installed on all pages
- [ ] Conversion actions created (purchase, lead, signup)
- [ ] Conversion values assigned (if applicable)
- [ ] Enhanced conversions enabled
- [ ] Test conversions firing correctly
- [ ] Import conversions from GA4 (optional)
Analytics Integration
- [ ] Google Analytics 4 linked
- [ ] Auto-tagging enabled
- [ ] GA4 audiences available in Google Ads
- [ ] Cross-domain tracking set up (if multiple domains)
Audience Setup
- [ ] Remarketing tag verified
- [ ] Website visitor audiences created:
- All visitors (180 days)
- Key page visitors (pricing, demo, features)
- Converters (for exclusion)
- [ ] Customer match lists uploaded
- [ ] Similar audiences enabled
Campaign Readiness
- [ ] Negative keyword lists created:
- Universal negatives (free, jobs, careers, reviews, complaints)
- Competitor negatives (if needed)
- Irrelevant industry terms
- [ ] Location targeting set (include/exclude)
- [ ] Language targeting set
- [ ] Ad schedule configured (if B2B, business hours)
- [ ] Device bid adjustments considered
Ad Extensions
- [ ] Sitelinks (4-6 relevant pages)
- [ ] Callouts (key benefits, offers)
- [ ] Structured snippets (features, types, services)
- [ ] Call extension (if phone leads valuable)
- [ ] Lead form extension (if using)
- [ ] Price extensions (if applicable)
- [ ] Image extensions (where available)
Brand Protection
- [ ] Brand campaign running (protect branded terms)
- [ ] Competitor campaigns considered
- [ ] Brand terms in negative lists for non-brand campaigns
---
Meta Ads Setup
Business Manager Foundation
- [ ] Business Manager created
- [ ] Business verified (if running certain ad types)
- [ ] Ad account created within Business Manager
- [ ] Payment method added
- [ ] Team access configured with proper roles
Pixel & Tracking
- [ ] Meta Pixel installed on all pages
- [ ] Standard events configured:
- PageView (automatic)
- ViewContent (product/feature pages)
- Lead (form submissions)
- Purchase (conversions)
- AddToCart (if e-commerce)
- InitiateCheckout (if e-commerce)
- [ ] Conversions API (CAPI) set up for server-side tracking
- [ ] Event Match Quality score > 6
- [ ] Test events in Events Manager
Domain & Aggregated Events
- [ ] Domain verified in Business Manager
- [ ] Aggregated Event Measurement configured
- [ ] Top 8 events prioritized in order of importance
- [ ] Web events prioritized for iOS 14+ tracking
Audience Setup
- [ ] Custom audiences created:
- Website visitors (all, 30/60/90/180 days)
- Key page visitors
- Video viewers (25%, 50%, 75%, 95%)
- Page/Instagram engagers
- Customer list uploaded
- [ ] Lookalike audiences created (1%, 1-3%)
- [ ] Saved audiences for common targeting
Catalog (E-commerce)
- [ ] Product catalog connected
- [ ] Product feed updating correctly
- [ ] Catalog sales campaigns enabled
- [ ] Dynamic product ads configured
Creative Assets
- [ ] Images in correct sizes:
- Feed: 1080x1080 (1:1)
- Stories/Reels: 1080x1920 (9:16)
- Landscape: 1200x628 (1.91:1)
- [ ] Videos in correct formats
- [ ] Ad copy variations ready
- [ ] UTM parameters in all destination URLs
Compliance
- [ ] Special Ad Categories declared (if housing, credit, employment, politics)
- [ ] Landing page complies with Meta policies
- [ ] No prohibited content in ads
---
LinkedIn Ads Setup
Campaign Manager Foundation
- [ ] Campaign Manager account created
- [ ] Company Page connected
- [ ] Billing information added
- [ ] Team access configured
Insight Tag & Tracking
- [ ] LinkedIn Insight Tag installed on all pages
- [ ] Tag verified and firing
- [ ] Conversion tracking configured:
- URL-based conversions
- Event-specific conversions
- [ ] Conversion values set (if applicable)
Audience Setup
- [ ] Matched Audiences created:
- Website retargeting audiences
- Company list uploaded (for ABM)
- Contact list uploaded
- [ ] Lookalike audiences created
- [ ] Saved audiences for common targeting
Lead Gen Forms (if using)
- [ ] Lead gen form templates created
- [ ] Form fields selected (minimize for conversion)
- [ ] Privacy policy URL added
- [ ] Thank you message configured
- [ ] CRM integration set up (or CSV export process)
Document Ads (if using)
- [ ] Documents uploaded (PDF, PowerPoint)
- [ ] Gating configured (full gate or preview)
- [ ] Lead gen form connected
Creative Assets
- [ ] Single image ads: 1200x627 (1.91:1) or 1080x1080 (1:1)
- [ ] Carousel images ready
- [ ] Video specs met (if using)
- [ ] Ad copy within character limits:
- Intro text: 600 max, 150 recommended
- Headline: 200 max, 70 recommended
Budget Considerations
- [ ] Budget realistic for LinkedIn CPCs ($8-15+ typical)
- [ ] Audience size validated (50K+ recommended)
- [ ] Daily vs. lifetime budget decided
- [ ] Bid strategy selected
---
Twitter/X Ads Setup
Account Foundation
- [ ] Ads account created
- [ ] Payment method added
- [ ] Account verified (if required)
Tracking
- [ ] Twitter Pixel installed
- [ ] Conversion events created
- [ ] Website tag verified
Audience Setup
- [ ] Tailored audiences created:
- Website visitors
- Customer lists
- [ ] Follower lookalikes identified
- [ ] Interest and keyword targets researched
Creative
- [ ] Tweet copy within 280 characters
- [ ] Images: 1200x675 (1.91:1) or 1200x1200 (1:1)
- [ ] Video specs met (if using)
- [ ] Cards configured (website, app, etc.)
---
TikTok Ads Setup
Account Foundation
- [ ] TikTok Ads Manager account created
- [ ] Business verification completed
- [ ] Payment method added
Pixel & Tracking
- [ ] TikTok Pixel installed
- [ ] Events configured (ViewContent, Purchase, etc.)
- [ ] Events API set up (recommended)
Audience Setup
- [ ] Custom audiences created
- [ ] Lookalike audiences created
- [ ] Interest categories identified
Creative
- [ ] Vertical video (9:16) ready
- [ ] Native-feeling content (not too polished)
- [ ] First 3 seconds are compelling hooks
- [ ] Captions added (most watch without sound)
- [ ] Music/sounds selected (licensed if needed)
---
Universal Pre-Launch Checklist
Before launching any campaign:
- [ ] Conversion tracking tested with real conversion
- [ ] Landing page loads fast (<3 sec)
- [ ] Landing page mobile-friendly
- [ ] UTM parameters working
- [ ] Budget set correctly (daily vs. lifetime)
- [ ] Start/end dates correct
- [ ] Targeting matches intended audience
- [ ] Ad creative approved
- [ ] Team notified of launch
- [ ] Reporting dashboard ready
Ad Account Scoring System
Reference for ad_health_scorer.py. Defines the weighted scoring algorithm, severity multipliers, and platform-specific category weights.
Scoring formula
Category_Score = Σ(Check_Result × Severity_Multiplier) / Σ(Severity_Multiplier) × 100
Platform_Score = Σ(Category_Score × Category_Weight)
Aggregate_Score = Σ(Platform_Score × Budget_Share)Severity multipliers
| Severity | Multiplier | Meaning | SLA |
|---|---|---|---|
| Critical | 5.0x | Blocks revenue or burns budget | Fix immediately |
| High | 3.0x | Significant performance impact | Fix within 1 week |
| Medium | 1.5x | Optimization opportunity | Fix within 1 month |
| Low | 0.5x | Polish / best practice | Backlog |
Critical issues dominate the score. A single critical failure drops the category score significantly, which is the correct behavior — a missing conversion tag invalidates everything downstream.
Platform category weights
Google Ads
| Category | Weight | Key checks |
|---|---|---|
| Conversion Tracking | 25% | Tag installed, Enhanced Conversions, attribution model, conversion window |
| Wasted Spend | 20% | Negative keywords, search terms review, broad match rules, 3× CPA kill rule |
| Account Structure | 15% | Naming conventions, ad group size, campaign types |
| Keywords | 15% | Quality Score, duplicates, match types, search intent alignment |
| Ads | 15% | RSA headlines count, extensions, A/B testing |
| Settings | 10% | Location targeting, schedules, networks, bidding strategy |
Meta (Facebook/Instagram)
| Category | Weight | Key checks |
|---|---|---|
| Pixel & CAPI | 30% | Pixel installed, CAPI active, event deduplication, domain verification |
| Creative | 30% | Format diversity, fatigue detection, safe zones, copy length |
| Structure | 20% | CBO, campaign naming, advantage+ settings |
| Audience | 20% | Lookalike seed size, exclusions, overlap, custom audiences |
| Category | Weight | Key checks |
|---|---|---|
| Technical | 25% | Insight tag, conversion events, matched audiences |
| Targeting | 25% | Audience size, job function vs title, company lists |
| Creative | 25% | Format mix, single-image vs carousel vs video, CTA alignment |
| Budget | 25% | Daily budget sufficiency, bid strategy, pacing |
TikTok
| Category | Weight | Key checks |
|---|---|---|
| Pixel | 25% | Pixel installed, events configured, match quality |
| Creative | 30% | Native-feel content, format mix, hook rate (3s), UGC ratio |
| Targeting | 25% | Interest vs behavior, custom audiences, lookalikes |
| Budget | 20% | Learning phase budget (50× target CPA), pacing |
Grade bands
| Grade | Score | Meaning |
|---|---|---|
| A | 90-100 | Excellent — maintain and scale |
| B | 75-89 | Good — address high-priority items |
| C | 60-74 | Needs work — systematic improvements needed |
| D | 40-59 | Poor — significant issues blocking performance |
| F | <40 | Critical — account needs fundamental restructuring |
Bands are calibrated wider than SEO scoring because ad accounts typically have more actionable but non-critical issues (e.g., missing extensions, suboptimal ad copy).
Quick Wins formula
Quick Win = severity ∈ {critical, high} AND result = "warn" (not full fail)Quick wins are issues that are important (high severity) but partially working (warn, not fail) — meaning the fix is usually small: enable a toggle, add a few negative keywords, activate an extension.
Hard rules (quality gates)
These combinations should NEVER be recommended together:
- Broad Match + Manual CPC (wastes budget without smart bidding control)
- CPA target below $5 with < $50/day budget (can't exit learning phase)
- Conversion action = page view as primary (inflates numbers, misleads bidding)
The scorer doesn't enforce these directly but the SKILL.md workflow should flag them as critical failures.
#!/usr/bin/env python3
"""
ad_health_scorer.py — Weighted 0-100 ad account health score with multi-platform support.
Scores ad accounts across platform-specific categories with severity multipliers
and budget-weighted cross-platform aggregation.
Severity multipliers:
critical = 5x weight (blocks revenue or burns budget)
high = 3x weight (significant impact)
medium = 1.5x weight (optimization opportunity)
low = 0.5x weight (backlog polish)
Platform category weights:
Google: Conversion Tracking 25%, Wasted Spend 20%, Structure 15%, Keywords 15%, Ads 15%, Settings 10%
Meta: Pixel/CAPI 30%, Creative 30%, Structure 20%, Audience 20%
LinkedIn: Technical 25%, Targeting 25%, Creative 25%, Budget 25%
TikTok: Pixel 25%, Creative 30%, Targeting 25%, Budget 20%
Cross-platform aggregation:
Aggregate Score = Σ(Platform_Score × Platform_Budget_Share)
Grade bands (calibrated wider — ad accounts naturally score lower):
A = 90-100, B = 75-89, C = 60-74, D = 40-59, F = <40
Usage:
python ad_health_scorer.py --checks checks.json
python ad_health_scorer.py --checks checks.json --platform google --budget 5000
python ad_health_scorer.py --multi platforms.json # multi-platform aggregation
python ad_health_scorer.py --demo
python ad_health_scorer.py --demo --json
"""
from __future__ import annotations
import argparse
import json
import sys
from collections import defaultdict
from pathlib import Path
SEVERITY_MULTIPLIER = {"critical": 5.0, "high": 3.0, "medium": 1.5, "low": 0.5}
PLATFORM_WEIGHTS = {
"google": {
"conversion_tracking": 0.25,
"wasted_spend": 0.20,
"account_structure": 0.15,
"keywords": 0.15,
"ads": 0.15,
"settings": 0.10,
},
"meta": {
"pixel_capi": 0.30,
"creative": 0.30,
"structure": 0.20,
"audience": 0.20,
},
"linkedin": {
"technical": 0.25,
"targeting": 0.25,
"creative": 0.25,
"budget": 0.25,
},
"tiktok": {
"pixel": 0.25,
"creative": 0.30,
"targeting": 0.25,
"budget": 0.20,
},
}
DEMO_CHECKS = {
"google": [
{"category": "conversion_tracking", "check": "Google Ads conversion tag installed", "result": "pass", "severity": "critical"},
{"category": "conversion_tracking", "check": "Enhanced Conversions enabled", "result": "fail", "severity": "critical", "detail": "Missing enhanced conversions — losing 15-30% attribution"},
{"category": "conversion_tracking", "check": "Conversion window appropriate", "result": "pass", "severity": "medium"},
{"category": "wasted_spend", "check": "Negative keyword coverage", "result": "warn", "severity": "high", "detail": "Only 12 negative keywords — review search terms report"},
{"category": "wasted_spend", "check": "No broad match + manual CPC", "result": "pass", "severity": "critical"},
{"category": "wasted_spend", "check": "Search terms review (last 30d)", "result": "fail", "severity": "high", "detail": "23% of spend on irrelevant terms"},
{"category": "account_structure", "check": "Campaign naming convention", "result": "pass", "severity": "low"},
{"category": "account_structure", "check": "Ad groups ≤ 20 keywords each", "result": "warn", "severity": "medium", "detail": "2 ad groups with 30+ keywords"},
{"category": "keywords", "check": "No duplicate keywords across campaigns", "result": "pass", "severity": "high"},
{"category": "keywords", "check": "Quality Score ≥ 6 on top spenders", "result": "warn", "severity": "high", "detail": "3 keywords with QS 4-5"},
{"category": "ads", "check": "RSA with ≥ 3 headlines", "result": "pass", "severity": "medium"},
{"category": "ads", "check": "Ad extensions active (sitelinks, callouts)", "result": "fail", "severity": "medium", "detail": "No callout extensions"},
{"category": "settings", "check": "Location targeting correct", "result": "pass", "severity": "high"},
{"category": "settings", "check": "Ad schedule aligned with business hours", "result": "pass", "severity": "low"},
],
"meta": [
{"category": "pixel_capi", "check": "Meta Pixel installed", "result": "pass", "severity": "critical"},
{"category": "pixel_capi", "check": "Conversions API (CAPI) active", "result": "fail", "severity": "critical", "detail": "No server-side events — degraded attribution post-iOS14"},
{"category": "creative", "check": "Creative diversity (≥ 3 formats)", "result": "warn", "severity": "high", "detail": "Only static images — add video and carousel"},
{"category": "creative", "check": "No creative fatigue (CTR stable)", "result": "pass", "severity": "high"},
{"category": "structure", "check": "CBO enabled", "result": "pass", "severity": "medium"},
{"category": "audience", "check": "Lookalike seed ≥ 1000 users", "result": "pass", "severity": "medium"},
],
}
def score_platform(checks, platform):
weights = PLATFORM_WEIGHTS.get(platform, {})
by_category = defaultdict(list)
for c in checks:
by_category[c.get("category", "other")].append(c)
category_scores = {}
findings = []
quick_wins = []
for cat, cat_checks in by_category.items():
weighted_pass = 0.0
weighted_total = 0.0
for check in cat_checks:
result = check.get("result", "fail")
severity = check.get("severity", "medium")
mult = SEVERITY_MULTIPLIER.get(severity, 1.0)
score = {"pass": 1.0, "warn": 0.5, "fail": 0.0}.get(result, 0.0)
weighted_pass += score * mult
weighted_total += mult
if result != "pass":
finding = {
"platform": platform,
"category": cat,
"check": check.get("check", ""),
"result": result,
"severity": severity,
"detail": check.get("detail", ""),
}
findings.append(finding)
# Quick win: high/critical severity + warn (not full fail)
if severity in ("critical", "high") and result == "warn":
quick_wins.append(finding)
cat_score = (weighted_pass / weighted_total * 100) if weighted_total > 0 else 100
category_scores[cat] = round(cat_score, 1)
# Weighted overall
overall = 0.0
total_weight = 0.0
for cat, weight in weights.items():
if cat in category_scores:
overall += category_scores[cat] * weight
total_weight += weight
overall = (overall / total_weight) if total_weight > 0 else 0.0
if overall >= 90:
grade = "A"
elif overall >= 75:
grade = "B"
elif overall >= 60:
grade = "C"
elif overall >= 40:
grade = "D"
else:
grade = "F"
findings.sort(key=lambda f: {"critical": 0, "high": 1, "medium": 2, "low": 3}.get(f["severity"], 99))
return {
"platform": platform,
"overall_score": round(overall, 1),
"grade": grade,
"category_scores": category_scores,
"total_checks": len(checks),
"passed": sum(1 for c in checks if c.get("result") == "pass"),
"warnings": sum(1 for c in checks if c.get("result") == "warn"),
"failures": sum(1 for c in checks if c.get("result") == "fail"),
"findings": findings,
"quick_wins": quick_wins,
}
def aggregate_platforms(platform_results, budgets=None):
if not budgets:
# Equal weight
budgets = {p["platform"]: 1.0 / len(platform_results) for p in platform_results}
total_budget = sum(budgets.values())
shares = {k: v / total_budget for k, v in budgets.items()}
aggregate = 0.0
for pr in platform_results:
share = shares.get(pr["platform"], 0)
aggregate += pr["overall_score"] * share
return {
"aggregate_score": round(aggregate, 1),
"budget_shares": {k: round(v, 2) for k, v in shares.items()},
"platform_scores": {pr["platform"]: pr["overall_score"] for pr in platform_results},
}
def print_report(result):
print(f"Ad Health Score ({result['platform'].upper()}): {result['overall_score']}/100 (Grade: {result['grade']})")
print(f"Checks: {result['total_checks']} — {result['passed']} pass, {result['warnings']} warn, {result['failures']} fail")
print()
print("Category Breakdown:")
for cat, score in sorted(result["category_scores"].items()):
bar = "█" * int(score / 5) + "░" * (20 - int(score / 5))
print(f" {cat:25s} {bar} {score:5.1f}/100")
print()
if result["quick_wins"]:
print(f"Quick Wins ({len(result['quick_wins'])}):")
for f in result["quick_wins"]:
print(f" ⚡ [{f['severity'].upper()}] {f['check']}: {f['detail']}")
print()
if result["findings"]:
print(f"Findings ({len(result['findings'])}):")
for f in result["findings"]:
detail = f" — {f['detail']}" if f["detail"] else ""
print(f" [{f['severity'].upper()}/{f['result'].upper()}] {f['check']}{detail}")
def main():
p = argparse.ArgumentParser(
description="Compute weighted 0-100 ad account health score with severity multipliers.",
epilog="Supports Google, Meta, LinkedIn, TikTok. Run with --demo for a sample report.",
)
p.add_argument("--checks", help="Path to checks JSON file (array of check objects)")
p.add_argument("--platform", choices=list(PLATFORM_WEIGHTS.keys()), default="google")
p.add_argument("--budget", type=float, default=None, help="Monthly budget (for multi-platform weighting)")
p.add_argument("--multi", help="Path to multi-platform JSON {platform: {checks: [...], budget: N}}")
p.add_argument("--json", action="store_true", help="JSON output")
p.add_argument("--demo", action="store_true", help="Run with demo data")
args = p.parse_args()
if args.demo:
results = []
for platform, checks in DEMO_CHECKS.items():
results.append(score_platform(checks, platform))
agg = aggregate_platforms(results, {"google": 3000, "meta": 2000})
if args.json:
print(json.dumps({"platforms": results, "aggregate": agg}, indent=2))
else:
for r in results:
print_report(r)
print()
print(f"Cross-Platform Aggregate: {agg['aggregate_score']}/100")
print(f"Budget shares: {agg['budget_shares']}")
return
if args.multi:
data = json.loads(Path(args.multi).read_text())
results = []
budgets = {}
for platform, pdata in data.items():
results.append(score_platform(pdata["checks"], platform))
budgets[platform] = pdata.get("budget", 1000)
agg = aggregate_platforms(results, budgets)
if args.json:
print(json.dumps({"platforms": results, "aggregate": agg}, indent=2))
else:
for r in results:
print_report(r)
print()
print(f"Cross-Platform Aggregate: {agg['aggregate_score']}/100")
return
if args.checks:
checks = json.loads(Path(args.checks).read_text())
result = score_platform(checks, args.platform)
if args.json:
print(json.dumps(result, indent=2))
else:
print_report(result)
return
p.print_help()
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
roas_calculator.py — ROAS and paid-ads metrics calculator
Usage:
python3 roas_calculator.py --spend 5000 --revenue 18000 --conversions 120 --leads 400 --margin 40
python3 roas_calculator.py --file campaign.json
python3 roas_calculator.py --json # demo + JSON output
python3 roas_calculator.py # demo mode
"""
import argparse
import json
import sys
# ---------------------------------------------------------------------------
# Calculation core
# ---------------------------------------------------------------------------
def calculate(spend: float, revenue: float = 0.0, conversions: int = 0,
leads: int = 0, margin_pct: float = 0.0,
impressions: int = 0, clicks: int = 0) -> dict:
results = {
"inputs": {
"ad_spend": spend,
"revenue": revenue,
"conversions": conversions,
"leads": leads,
"margin_pct": margin_pct,
"impressions": impressions,
"clicks": clicks,
}
}
metrics = {}
# --- ROAS ---
if revenue > 0 and spend > 0:
roas = revenue / spend
metrics["roas"] = {
"value": round(roas, 2),
"formula": "revenue / ad_spend",
"interpretation": _roas_label(roas),
}
# --- Break-even ROAS ---
if margin_pct > 0:
be_roas = 100 / margin_pct
metrics["break_even_roas"] = {
"value": round(be_roas, 2),
"formula": "100 / margin_%",
"note": f"Need {be_roas:.1f}x ROAS to cover ad costs at {margin_pct}% margin",
}
if revenue > 0:
actual_roas = revenue / spend
profitable = actual_roas >= be_roas
metrics["profitability"] = {
"is_profitable": profitable,
"gap": round(actual_roas - be_roas, 2),
"note": "Profitable ✅" if profitable else f"Unprofitable ❌ — need +{be_roas - actual_roas:.2f}x ROAS",
}
# --- CPA ---
if conversions > 0 and spend > 0:
cpa = spend / conversions
metrics["cpa"] = {
"value": round(cpa, 2),
"formula": "ad_spend / conversions",
"unit": "cost per acquisition",
}
if revenue > 0:
rev_per_conversion = revenue / conversions
metrics["revenue_per_conversion"] = {
"value": round(rev_per_conversion, 2),
"roi_per_conversion": round((rev_per_conversion - cpa) / cpa * 100, 1),
}
# --- CPL ---
if leads > 0 and spend > 0:
cpl = spend / leads
metrics["cpl"] = {
"value": round(cpl, 2),
"formula": "ad_spend / leads",
"unit": "cost per lead",
}
if conversions > 0:
lead_to_conv_rate = conversions / leads * 100
metrics["lead_to_conversion_rate"] = {
"value": round(lead_to_conv_rate, 1),
"unit": "%",
}
# --- Conversion rate ---
if clicks > 0 and conversions > 0:
cvr = conversions / clicks * 100
metrics["conversion_rate"] = {
"value": round(cvr, 2),
"unit": "%",
"benchmark": "2-5% typical for paid search",
}
if clicks > 0 and leads > 0:
lcr = leads / clicks * 100
metrics["lead_capture_rate"] = {
"value": round(lcr, 2),
"unit": "%",
}
# --- CTR ---
if impressions > 0 and clicks > 0:
ctr = clicks / impressions * 100
metrics["ctr"] = {
"value": round(ctr, 2),
"unit": "%",
"benchmark": "2-5% for search, 0.1-0.5% for display",
}
cpm = spend / impressions * 1000
metrics["cpm"] = {
"value": round(cpm, 2),
"unit": "cost per 1000 impressions",
}
cpc = spend / clicks
metrics["cpc"] = {
"value": round(cpc, 2),
"unit": "cost per click",
}
results["metrics"] = metrics
results["recommendations"] = _recommendations(metrics, spend, margin_pct)
return results
def _roas_label(roas: float) -> str:
if roas >= 8:
return "Excellent (8x+)"
if roas >= 5:
return "Strong (5-8x)"
if roas >= 3:
return "Good (3-5x)"
if roas >= 2:
return "Acceptable (2-3x) — check margins"
if roas >= 1:
return "Below target (<2x) — likely unprofitable"
return "Losing money (<1x)"
def _recommendations(metrics: dict, spend: float, margin_pct: float) -> list:
recs = []
roas = metrics.get("roas", {}).get("value")
be_roas = metrics.get("break_even_roas", {}).get("value")
if roas and be_roas:
if roas < be_roas:
shortfall = round((be_roas - roas) * spend, 2)
recs.append(f"⚠️ Losing ${shortfall:,.2f}/period — pause or restructure campaign immediately")
elif roas < be_roas * 1.5:
recs.append("⚠️ Marginally profitable — optimize creatives and targeting before scaling")
else:
recs.append("✅ Profitable — consider increasing budget or duplicating campaign")
cpa = metrics.get("cpa", {}).get("value")
cpl = metrics.get("cpl", {}).get("value")
cvr = metrics.get("conversion_rate", {}).get("value")
if cvr and cvr < 2:
recs.append(f"⚠️ CVR {cvr}% is low — test new landing pages, headlines, and CTAs")
elif cvr and cvr >= 5:
recs.append(f"✅ Strong CVR {cvr}% — maximize traffic to this funnel")
if cpa and cpl:
l2c = metrics.get("lead_to_conversion_rate", {}).get("value", 0)
if l2c < 10:
recs.append(f"⚠️ Lead-to-close rate {l2c}% is low — review sales qualification or nurture sequence")
ctr = metrics.get("ctr", {}).get("value")
if ctr:
if ctr < 1:
recs.append(f"⚠️ CTR {ctr}% is low — refresh ad copy and audience targeting")
elif ctr >= 5:
recs.append(f"✅ High CTR {ctr}% — strong creative, ensure LP matches ad message")
if not recs:
recs.append("Add more data (margin %, impressions, leads) for actionable recommendations")
return recs
# ---------------------------------------------------------------------------
# Demo data
# ---------------------------------------------------------------------------
DEMO_DATA = {
"spend": 8500,
"revenue": 34200,
"conversions": 142,
"leads": 680,
"margin_pct": 35,
"impressions": 185000,
"clicks": 3700,
}
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(
description="ROAS calculator — paid ads performance metrics and recommendations."
)
parser.add_argument("--spend", type=float, help="Total ad spend ($)")
parser.add_argument("--revenue", type=float, default=0, help="Total attributed revenue ($)")
parser.add_argument("--conversions", type=int, default=0, help="Number of purchases/conversions")
parser.add_argument("--leads", type=int, default=0, help="Number of leads generated")
parser.add_argument("--margin", type=float, default=0, help="Gross margin %% (e.g. 40)")
parser.add_argument("--impressions", type=int, default=0, help="Total impressions")
parser.add_argument("--clicks", type=int, default=0, help="Total clicks")
parser.add_argument("--file", help="JSON file with campaign data")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
if args.file:
with open(args.file, "r") as f:
data = json.load(f)
elif args.spend:
data = {
"spend": args.spend,
"revenue": args.revenue,
"conversions": args.conversions,
"leads": args.leads,
"margin_pct": args.margin,
"impressions": args.impressions,
"clicks": args.clicks,
}
else:
data = DEMO_DATA
if not args.json:
print("No input provided — running in demo mode.\n")
result = calculate(
spend=data.get("spend", 0),
revenue=data.get("revenue", 0),
conversions=data.get("conversions", 0),
leads=data.get("leads", 0),
margin_pct=data.get("margin_pct", 0),
impressions=data.get("impressions", 0),
clicks=data.get("clicks", 0),
)
if args.json:
print(json.dumps(result, indent=2))
return
inp = result["inputs"]
metrics = result["metrics"]
recs = result["recommendations"]
print("=" * 62)
print(" PAID ADS PERFORMANCE REPORT")
print("=" * 62)
print(f" Spend: ${inp['ad_spend']:>10,.2f}")
if inp["revenue"]: print(f" Revenue: ${inp['revenue']:>10,.2f}")
if inp["conversions"]:print(f" Conversions:{inp['conversions']:>10}")
if inp["leads"]: print(f" Leads: {inp['leads']:>10}")
if inp["impressions"]:print(f" Impressions:{inp['impressions']:>10,}")
if inp["clicks"]: print(f" Clicks: {inp['clicks']:>10,}")
print()
print(" METRICS")
print(" " + "─" * 58)
metric_labels = [
("roas", "ROAS", lambda m: f"{m['value']}x — {m['interpretation']}"),
("break_even_roas", "Break-even ROAS", lambda m: f"{m['value']}x — {m['note']}"),
("profitability", "Profitability", lambda m: m['note']),
("cpa", "CPA", lambda m: f"${m['value']:,.2f} / {m['unit']}"),
("revenue_per_conversion", "Rev/Conversion", lambda m: f"${m['value']:,.2f} (ROI {m['roi_per_conversion']}%)"),
("cpl", "CPL", lambda m: f"${m['value']:,.2f} / {m['unit']}"),
("lead_to_conversion_rate","Lead→Conv Rate", lambda m: f"{m['value']}%"),
("conversion_rate", "Conversion Rate", lambda m: f"{m['value']}% ({m['benchmark']})"),
("ctr", "CTR", lambda m: f"{m['value']}%"),
("cpc", "CPC", lambda m: f"${m['value']:,.2f}"),
("cpm", "CPM", lambda m: f"${m['value']:,.2f}"),
]
for key, label, fmt in metric_labels:
if key in metrics:
try:
detail = fmt(metrics[key])
print(f" {label:<24} {detail}")
except Exception:
pass
print()
print(" RECOMMENDATIONS")
print(" " + "─" * 58)
for rec in recs:
print(f" {rec}")
print("=" * 62)
if __name__ == "__main__":
main()
Related skills
FAQ
Which copy frameworks does paid-ads include?
paid-ads provides Problem-Agitate-Solve (PAS), Before-After-Bridge (BAB), social proof, and feature-benefit formulas with example primary text blocks and CTA lines for paid channels.
Does paid-ads manage ad accounts?
paid-ads only drafts primary text copy for paid social and search ads; developers paste output into Meta, Google, or other ad managers and handle targeting and budgets separately.
Is Paid Ads safe to install?
skills.sh reports 2 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.