Systems Library / Marketing Automation / How to Build AI-Powered Ad Performance Predictions
Marketing Automation paid advertising

How to Build AI-Powered Ad Performance Predictions

Use historical data to predict which ads will perform before spending money.

Jay Banlasan

Jay Banlasan

The AI Systems Guy

You can use AI to predict ad performance before launch by feeding historical patterns into a scoring model. I run every new ad concept through this before spending a dollar. It does not guarantee winners, but it filters out likely losers and saves real money.

The model works because ad performance is not random. Certain headline types, copy lengths, and visual styles consistently outperform others for a given audience.

What You Need Before Starting

Step 1: Structure Your Historical Data

import sqlite3

def build_training_data(db_path):
    conn = sqlite3.connect(db_path)
    rows = conn.execute("""
        SELECT ad_name, 
            SUM(spend) as spend, SUM(impressions) as impressions,
            SUM(clicks) as clicks, SUM(leads) as leads,
            CASE WHEN SUM(clicks) > 0 THEN SUM(clicks)*100.0/SUM(impressions) ELSE 0 END as ctr,
            CASE WHEN SUM(leads) > 0 THEN SUM(spend)/SUM(leads) ELSE 999 END as cpa
        FROM ad_daily
        WHERE spend > 20
        GROUP BY ad_name
        HAVING SUM(impressions) > 500
    """).fetchall()
    conn.close()
    
    ads = []
    for r in rows:
        ads.append({
            "name": r[0], "spend": r[1], "impressions": r[2],
            "clicks": r[3], "leads": r[4], "ctr": round(r[5], 2), "cpa": round(r[6], 2),
            "winner": r[5] > 1.5 and r[6] < 30,
        })
    return ads

Step 2: Extract Ad Attributes

Parse your naming convention to get structured attributes:

def parse_ad_attributes(ad_name):
    parts = [p.strip() for p in ad_name.split("|")]
    attributes = {
        "number": parts[0] if len(parts) > 0 else "",
        "format": parts[1] if len(parts) > 1 else "",
        "copy_framework": parts[2] if len(parts) > 2 else "",
        "angle": parts[3] if len(parts) > 3 else "",
    }
    return attributes

Step 3: Build the Prediction Prompt

import anthropic
import json

def predict_performance(new_ad, historical_ads):
    client = anthropic.Anthropic()
    
    winners = [a for a in historical_ads if a["winner"]][:15]
    losers = [a for a in historical_ads if not a["winner"]][:10]
    
    winners_text = "\n".join([f"- {a['name']}: CTR {a['ctr']}%, CPA ${a['cpa']}" for a in winners])
    losers_text = "\n".join([f"- {a['name']}: CTR {a['ctr']}%, CPA ${a['cpa']}" for a in losers])
    
    prompt = f"""Predict the performance of a new ad based on historical patterns.

WINNING ADS (CTR > 1.5%, CPA < $30):
{winners_text}

LOSING ADS:
{losers_text}

NEW AD TO EVALUATE:
Name: {new_ad['name']}
Format: {new_ad.get('format', 'unknown')}
Copy Framework: {new_ad.get('copy_framework', 'unknown')}
Angle: {new_ad.get('angle', 'unknown')}
Headline: {new_ad.get('headline', 'N/A')}
Primary text preview: {new_ad.get('primary_text', 'N/A')[:200]}

Analyze patterns in winners vs losers. Then predict:
1. Predicted CTR range (low-high)
2. Predicted CPA range (low-high)
3. Confidence level (high/medium/low)
4. Key risk factors
5. Suggested improvements

Return JSON: {{"predicted_ctr_low": N, "predicted_ctr_high": N, "predicted_cpa_low": N, "predicted_cpa_high": N, "confidence": "...", "risks": [...], "improvements": [...]}}"""

    resp = client.messages.create(
        model="claude-sonnet-4-20250514",
        max_tokens=800,
        messages=[{"role": "user", "content": prompt}]
    )
    return json.loads(resp.content[0].text)

Step 4: Score and Rank New Concepts

def score_batch(new_ads, historical_ads):
    results = []
    for ad in new_ads:
        prediction = predict_performance(ad, historical_ads)
        prediction["ad_name"] = ad["name"]
        
        # Simple composite score
        avg_ctr = (prediction["predicted_ctr_low"] + prediction["predicted_ctr_high"]) / 2
        avg_cpa = (prediction["predicted_cpa_low"] + prediction["predicted_cpa_high"]) / 2
        confidence_mult = {"high": 1.0, "medium": 0.7, "low": 0.4}.get(prediction["confidence"], 0.5)
        
        prediction["composite_score"] = round((avg_ctr * 10 - avg_cpa * 0.1) * confidence_mult, 2)
        results.append(prediction)
    
    results.sort(key=lambda x: x["composite_score"], reverse=True)
    return results

Step 5: Compare Predictions to Actuals

After running ads, compare predictions to reality:

def evaluate_predictions(predictions, actuals):
    for pred in predictions:
        actual = actuals.get(pred["ad_name"])
        if not actual:
            continue
        
        ctr_accurate = pred["predicted_ctr_low"] <= actual["ctr"] <= pred["predicted_ctr_high"]
        cpa_accurate = pred["predicted_cpa_low"] <= actual["cpa"] <= pred["predicted_cpa_high"]
        
        print(f"{pred['ad_name']}:")
        print(f"  CTR: Predicted {pred['predicted_ctr_low']}-{pred['predicted_ctr_high']}%, Actual {actual['ctr']}% {'CORRECT' if ctr_accurate else 'MISSED'}")
        print(f"  CPA: Predicted ${pred['predicted_cpa_low']}-${pred['predicted_cpa_high']}, Actual ${actual['cpa']} {'CORRECT' if cpa_accurate else 'MISSED'}")

The system gets better over time as you feed it more data. After 100+ ads, the predictions become surprisingly accurate for filtering out clear losers.

What to Build Next

Add image analysis to the prediction model by describing the visual treatment. Then build a recommendation engine that suggests which proven elements to combine for the next batch of ads.

Related Reading

Want this system built for your business?

Get a free assessment. We will map every system your business needs and show you the ROI.

Get Your Free Assessment

Related Systems