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:
- Searches for ads from any brand or keyword
- Extracts winning ad copy and creative strategies
- 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
Create .env:
SOCIAVAULT_API_KEY=your_sociavault_key
OPENAI_API_KEY=your_openai_key
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 [];
}
}
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 [];
}
}
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);
}
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 };
}
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 };
}
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;
}
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();
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
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
- Get your SociaVault API Key
- Spy on your top 3 competitors
- Research your industry keywords
- Generate and test your own ads
Stop guessing. Start spying.
Top comments (0)