DEV Community

Cover image for Build a Reddit Ad Spy Tool to Find Winning Ad Angles
Olamide Olaniyan
Olamide Olaniyan

Posted on

Build a Reddit Ad Spy Tool to Find Winning Ad Angles

Want to know what your competitors are advertising on Reddit?

Reddit's ad library is public. Most people don't know this.

In this tutorial, we'll build a Reddit Ad Spy Tool that:

  1. Searches for ads from any brand or keyword
  2. Extracts winning ad copy and creative strategies
  3. Tracks ad spend patterns over time

Stop guessing what works. See exactly what's running.

Why Reddit Ads?

Reddit ads are interesting because:

  • Lower CPMs than Facebook/Instagram (often 50-70% cheaper)
  • Highly targeted by subreddit and interest
  • Less competition (most brands ignore Reddit)
  • Public ad library (anyone can see what's running)

If you're not advertising on Reddit, your competitors might be.

If you're not spying on Reddit ads, you're leaving money on the table.

The Stack

  • Node.js: Runtime
  • SociaVault API: To fetch Reddit ads
  • OpenAI API: For ad copy analysis

Step 1: Setup

mkdir reddit-ad-spy
cd reddit-ad-spy
npm init -y
npm install axios dotenv openai
Enter fullscreen mode Exit fullscreen mode

Create .env:

SOCIAVAULT_API_KEY=your_sociavault_key
OPENAI_API_KEY=your_openai_key
Enter fullscreen mode Exit fullscreen mode

Step 2: Search for Ads

Let's start by searching for ads by keyword or brand.

Create index.js:

require('dotenv').config();
const axios = require('axios');
const OpenAI = require('openai');

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
const SOCIAVAULT_BASE = 'https://api.sociavault.com';

async function searchAds(query, limit = 50) {
  console.log(`πŸ” Searching Reddit ads for "${query}"...`);

  try {
    const response = await axios.get(`${SOCIAVAULT_BASE}/v1/scrape/reddit/ads/search`, {
      params: { query, limit },
      headers: { 'Authorization': `Bearer ${process.env.SOCIAVAULT_API_KEY}` }
    });

    const ads = response.data.data || [];
    console.log(`βœ… Found ${ads.length} ads`);

    return ads.map(ad => ({
      id: ad.id,
      advertiser: ad.advertiser_name || ad.author,
      title: ad.title,
      body: ad.body || ad.selftext,
      subreddit: ad.subreddit,
      url: ad.url,
      thumbnail: ad.thumbnail,
      impressions: ad.impressions,
      spend: ad.spend,
      startDate: ad.start_date,
      endDate: ad.end_date
    }));
  } catch (error) {
    console.error('Error searching ads:', error.message);
    return [];
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Get Ad Details

Get more details about a specific advertiser:

async function getAdsFromBrand(advertiser) {
  console.log(`πŸ“₯ Fetching all ads from ${advertiser}...`);

  try {
    const response = await axios.get(`${SOCIAVAULT_BASE}/v1/scrape/reddit/ads`, {
      params: { advertiser },
      headers: { 'Authorization': `Bearer ${process.env.SOCIAVAULT_API_KEY}` }
    });

    return response.data.data || [];
  } catch (error) {
    console.error('Error fetching brand ads:', error.message);
    return [];
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Analyze Ad Copy Patterns

This is where it gets powerful. Let's use AI to extract winning formulas:

async function analyzeAdCopy(ads) {
  console.log('πŸ€– Analyzing ad copy patterns...');

  if (ads.length === 0) return null;

  const prompt = `
    Analyze these Reddit ads and extract patterns.

    Return JSON with:
    {
      "headlines": {
        "patterns": [top 5 headline formulas used],
        "powerWords": [most common power words],
        "averageLength": number
      },
      "hooks": [top 5 opening hooks that grab attention],
      "ctas": [most common calls to action],
      "angles": [
        {
          "type": "angle type (pain point, benefit, curiosity, etc.)",
          "example": "example from the ads",
          "frequency": "how often this appears"
        }
      ],
      "subredditTargeting": {
        "pattern": "what types of subreddits are targeted",
        "topSubreddits": [most common subreddits]
      },
      "recommendations": [5 specific ad copy recommendations based on what works]
    }

    Ads:
    ${JSON.stringify(ads.map(a => ({ 
      title: a.title, 
      body: a.body, 
      subreddit: a.subreddit,
      advertiser: a.advertiser
    })))}
  `;

  const completion = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: prompt }],
    response_format: { type: 'json_object' }
  });

  return JSON.parse(completion.choices[0].message.content);
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Competitor Deep Dive

Analyze a specific competitor's ad strategy:

async function analyzeCompetitor(brandName) {
  console.log(`\n🎯 Analyzing competitor: ${brandName}\n`);

  // Search for their ads
  const ads = await searchAds(brandName);

  if (ads.length === 0) {
    console.log('No ads found for this brand.');
    return null;
  }

  // Basic stats
  const subreddits = [...new Set(ads.map(a => a.subreddit).filter(Boolean))];
  const dateRange = ads
    .filter(a => a.startDate)
    .map(a => new Date(a.startDate))
    .sort((a, b) => a - b);

  console.log('═══════════════════════════════════════');
  console.log('πŸ“Š CAMPAIGN OVERVIEW');
  console.log('═══════════════════════════════════════\n');

  console.log(`πŸ“’ Total ads found: ${ads.length}`);
  console.log(`🎯 Subreddits targeted: ${subreddits.length}`);

  if (dateRange.length > 1) {
    console.log(`πŸ“… Active since: ${dateRange[0].toDateString()}`);
  }

  console.log('\n🎯 Target Subreddits:');
  subreddits.forEach(s => console.log(`  β€’ r/${s}`));

  // Sample of their ad copy
  console.log('\nπŸ“ Sample Ad Copy:');
  ads.slice(0, 3).forEach((ad, i) => {
    console.log(`\n${i + 1}. "${ad.title}"`);
    if (ad.body) {
      console.log(`   ${ad.body.substring(0, 150)}...`);
    }
    console.log(`   β†’ r/${ad.subreddit}`);
  });

  // AI Analysis
  const analysis = await analyzeAdCopy(ads);

  if (analysis) {
    console.log('\n═══════════════════════════════════════');
    console.log('🧠 AI ANALYSIS');
    console.log('═══════════════════════════════════════\n');

    console.log('πŸ“Œ Headline Patterns:');
    analysis.headlines.patterns.forEach((p, i) => {
      console.log(`  ${i + 1}. ${p}`);
    });

    console.log('\n🎣 Top Hooks:');
    analysis.hooks.forEach((h, i) => {
      console.log(`  ${i + 1}. ${h}`);
    });

    console.log('\n🎯 Ad Angles Used:');
    analysis.angles.forEach((a, i) => {
      console.log(`  ${i + 1}. [${a.type}] "${a.example}"`);
    });

    console.log('\nπŸ’‘ Recommendations:');
    analysis.recommendations.forEach((r, i) => {
      console.log(`  ${i + 1}. ${r}`);
    });
  }

  return { ads, analysis, subreddits };
}
Enter fullscreen mode Exit fullscreen mode

Step 6: Industry Research

Search ads across your entire industry:

async function researchIndustry(keywords) {
  console.log('\nπŸ”¬ Industry Ad Research\n');
  console.log('═══════════════════════════════════════\n');

  const allAds = [];

  for (const keyword of keywords) {
    const ads = await searchAds(keyword, 25);
    allAds.push(...ads);

    // Small delay between searches
    await new Promise(r => setTimeout(r, 500));
  }

  // Deduplicate
  const uniqueAds = allAds.filter((ad, index, self) =>
    index === self.findIndex(a => a.id === ad.id)
  );

  console.log(`πŸ“Š Total unique ads found: ${uniqueAds.length}`);

  // Analyze all industry ads together
  const analysis = await analyzeAdCopy(uniqueAds);

  if (analysis) {
    console.log('\n═══════════════════════════════════════');
    console.log('πŸ† INDUSTRY BEST PRACTICES');
    console.log('═══════════════════════════════════════\n');

    console.log('πŸ“Œ Winning Headline Formulas:');
    analysis.headlines.patterns.forEach((p, i) => {
      console.log(`  ${i + 1}. ${p}`);
    });

    console.log('\n⚑ Power Words:');
    console.log(`  ${analysis.headlines.powerWords.join(', ')}`);

    console.log('\n🎯 Most Common Angles:');
    analysis.angles.forEach((a, i) => {
      console.log(`  ${i + 1}. ${a.type}: ${a.frequency}`);
    });

    console.log('\nπŸŽͺ Hot Subreddits to Target:');
    analysis.subredditTargeting.topSubreddits.forEach(s => {
      console.log(`  β€’ r/${s}`);
    });

    console.log('\nπŸ“‹ Your Action Items:');
    analysis.recommendations.forEach((r, i) => {
      console.log(`  ${i + 1}. ${r}`);
    });
  }

  return { ads: uniqueAds, analysis };
}
Enter fullscreen mode Exit fullscreen mode

Step 7: Generate Ad Ideas

Based on what's working, generate your own ad copy:

async function generateAdIdeas(analysis, yourProduct, targetSubreddits) {
  console.log('\n✍️ Generating ad ideas for your product...\n');

  const prompt = `
    Based on this analysis of successful Reddit ads, generate ad copy for a new product.

    Product: ${yourProduct}
    Target subreddits: ${targetSubreddits.join(', ')}

    Winning patterns from analysis:
    - Headlines: ${JSON.stringify(analysis.headlines.patterns)}
    - Hooks: ${JSON.stringify(analysis.hooks)}
    - Angles: ${JSON.stringify(analysis.angles)}

    Generate JSON with:
    {
      "adVariations": [
        {
          "headline": "attention-grabbing headline",
          "body": "compelling body copy (2-3 sentences)",
          "cta": "call to action",
          "targetSubreddit": "r/suggested_subreddit",
          "angle": "which angle this uses"
        }
      ]
    }

    Generate 5 different ad variations using different angles.
  `;

  const completion = await openai.chat.completions.create({
    model: 'gpt-4o-mini',
    messages: [{ role: 'user', content: prompt }],
    response_format: { type: 'json_object' }
  });

  const ideas = JSON.parse(completion.choices[0].message.content);

  console.log('═══════════════════════════════════════');
  console.log('πŸ’‘ GENERATED AD IDEAS');
  console.log('═══════════════════════════════════════\n');

  ideas.adVariations.forEach((ad, i) => {
    console.log(`πŸ“’ Variation ${i + 1} [${ad.angle}]`);
    console.log(`   Headline: "${ad.headline}"`);
    console.log(`   Body: "${ad.body}"`);
    console.log(`   CTA: "${ad.cta}"`);
    console.log(`   Target: ${ad.targetSubreddit}\n`);
  });

  return ideas;
}
Enter fullscreen mode Exit fullscreen mode

Step 8: Putting It All Together

async function main() {
  // Option 1: Spy on a specific competitor
  await analyzeCompetitor('notion');

  // Option 2: Research your entire industry
  // await researchIndustry(['saas', 'productivity', 'project management']);

  // Option 3: Full workflow - research + generate
  /*
  const research = await researchIndustry(['data api', 'scraping tool', 'automation']);

  if (research.analysis) {
    await generateAdIdeas(
      research.analysis,
      'SociaVault - Social media data API for developers',
      ['webdev', 'programming', 'SideProject', 'startups']
    );
  }
  */
}

main();
Enter fullscreen mode Exit fullscreen mode

Sample Output

🎯 Analyzing competitor: notion

πŸ” Searching Reddit ads for "notion"...
βœ… Found 23 ads

═══════════════════════════════════════
πŸ“Š CAMPAIGN OVERVIEW
═══════════════════════════════════════

πŸ“’ Total ads found: 23
🎯 Subreddits targeted: 12
πŸ“… Active since: Mon Oct 15 2023

🎯 Target Subreddits:
  β€’ r/productivity
  β€’ r/startups
  β€’ r/Entrepreneur
  β€’ r/smallbusiness
  β€’ r/freelance
  β€’ r/webdev

πŸ“ Sample Ad Copy:

1. "Your second brain, but better"
   Stop losing ideas in scattered notes. Notion keeps everything...
   β†’ r/productivity

2. "Why top startups are ditching spreadsheets"
   From seed to Series A, your docs should grow with you...
   β†’ r/startups

═══════════════════════════════════════
🧠 AI ANALYSIS
═══════════════════════════════════════

πŸ“Œ Headline Patterns:
  1. "Your X, but better" - improvement angle
  2. "Why [audience] are doing X" - social proof
  3. "Stop [pain point]" - pain agitation
  4. Question hooks targeting frustrations
  5. Stat-based claims with specific numbers

🎣 Top Hooks:
  1. Calling out a common frustration
  2. Promising a transformation
  3. Creating curiosity with "why" questions
  4. Using "you" to make it personal
  5. Mentioning a relatable scenario

🎯 Ad Angles Used:
  1. [Pain Point] "Stop losing ideas in scattered notes"
  2. [Social Proof] "Why top startups are..."
  3. [Transformation] "From chaos to clarity"
  4. [Curiosity] "The tool nobody told you about"
  5. [Benefit] "Save 10 hours a week"

πŸ’‘ Recommendations:
  1. Lead with pain points specific to each subreddit
  2. Use "stop/start" framing for clear transformation
  3. Include social proof when possible
  4. Keep headlines under 60 characters
  5. Target subreddits by profession, not just interest
Enter fullscreen mode Exit fullscreen mode

Why This Matters

Ad spy tools like AdSpy and BigSpy cost $100-300/month.

You just built a Reddit-specific version that:

  • Shows you exactly what competitors are running
  • Extracts winning copy patterns automatically
  • Generates ideas based on proven formulas

Total cost? Maybe $10-20/month in API calls.

Next Steps

  1. Get your SociaVault API Key
  2. Spy on your top 3 competitors
  3. Research your industry keywords
  4. Generate and test your own ads

Stop guessing. Start spying.

Top comments (0)