Tutorial

How to Run a SERP Audit for Cold Email Personalization

Audit a prospect's search presence to write hyper-personalized cold emails. Find their top pages, rankings, and content gaps automatically.

Generic cold emails get ignored. Personalized emails referencing a prospect's actual online presence get replies. This tutorial builds a SERP audit pipeline that researches each prospect's company, finds their top-ranking pages, identifies content gaps, and generates personalized email opening lines. Each audit costs 2-3 search queries ($0.010-0.015) and produces actionable intel for your email.

Prerequisites

  • Python 3.9+ installed
  • requests library installed
  • A Scavio API key from scavio.dev
  • A list of prospect company names or domains

Walkthrough

Step 1: Define your prospect list

Load prospect companies to audit. Each gets a mini SERP research session.

Python
prospects = [
    {'name': 'Acme SaaS', 'domain': 'acmesaas.com', 'contact': 'jane@acmesaas.com'},
    {'name': 'TechFlow', 'domain': 'techflow.io', 'contact': 'mike@techflow.io'},
    {'name': 'DataSync Pro', 'domain': 'datasyncpro.com', 'contact': 'sarah@datasyncpro.com'},
]

queries_per_prospect = 3  # brand, competitors, content
total_cost = len(prospects) * queries_per_prospect * 0.005
print(f'{len(prospects)} prospects x {queries_per_prospect} queries each')
print(f'Estimated cost: ${total_cost:.3f}')

Step 2: Run the SERP audit for each prospect

Search for the company name, their competitors, and their content to build a profile.

Python
import requests, os, time

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']

def search(query: str) -> list:
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us', 'num_results': 10})
    return resp.json().get('organic_results', [])

def audit_prospect(prospect: dict) -> dict:
    name = prospect['name']
    domain = prospect['domain']
    # Query 1: Brand presence
    brand_results = search(f'{name} reviews')
    brand_mentions = len([r for r in brand_results if domain in r.get('link', '')])
    # Query 2: Competitor landscape
    competitor_results = search(f'{name} alternatives competitors 2026')
    competitors = [r['title'].split(' - ')[0] for r in competitor_results[:3]
                   if domain not in r.get('link', '')]
    # Query 3: Content presence
    content_results = search(f'site:{domain}')
    top_pages = [r['title'] for r in content_results[:5]]
    time.sleep(0.3)
    return {
        'prospect': name,
        'brand_mentions_in_top10': brand_mentions,
        'competitors': competitors[:3],
        'top_pages': top_pages,
        'has_blog': any('blog' in r.get('link', '').lower() for r in content_results)
    }

# Audit first prospect
audit = audit_prospect(prospects[0])
for key, val in audit.items():
    print(f'  {key}: {val}')

Step 3: Generate personalized email opening lines

Use the audit data to craft a personalized opening line for each prospect. Reference their actual content and competitive position.

Python
def generate_opening(audit: dict) -> str:
    name = audit['prospect']
    templates = []
    if audit['has_blog']:
        page = audit['top_pages'][0] if audit['top_pages'] else ''
        templates.append(
            f'I noticed {name} has been publishing content like "{page[:40]}" -- '
            f'curious if you are tracking how that ranks against {audit["competitors"][0] if audit["competitors"] else "competitors"}.'
        )
    if audit['brand_mentions_in_top10'] < 3:
        templates.append(
            f'{name} only appears {audit["brand_mentions_in_top10"]} times in the top 10 '
            f'for brand searches. That is a quick win we could help with.'
        )
    if audit['competitors']:
        templates.append(
            f'Noticed {audit["competitors"][0]} is showing up in "{name} alternatives" '
            f'searches -- are you monitoring that?'
        )
    return templates[0] if templates else f'Researched {name} and found some ranking opportunities.'

# Generate for all prospects
for p in prospects:
    audit = audit_prospect(p)
    opening = generate_opening(audit)
    print(f'To: {p["contact"]}')
    print(f'Opening: {opening}')
    print()

Python Example

Python
import requests, os, time

SCAVIO_KEY = os.environ['SCAVIO_API_KEY']

def search(query):
    resp = requests.post('https://api.scavio.dev/api/v1/search',
        headers={'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json'},
        json={'query': query, 'country_code': 'us', 'num_results': 10})
    return resp.json().get('organic_results', [])

def audit(name, domain):
    brand = search(f'{name} reviews')
    competitors = search(f'{name} alternatives 2026')
    content = search(f'site:{domain}')
    return {
        'mentions': len([r for r in brand if domain in r.get('link', '')]),
        'competitors': [r['title'].split(' - ')[0] for r in competitors[:3]],
        'top_pages': [r['title'] for r in content[:3]]
    }

result = audit('Acme SaaS', 'acmesaas.com')
print(f'Brand mentions: {result["mentions"]}')
print(f'Competitors: {", ".join(result["competitors"])}')

JavaScript Example

JavaScript
const SCAVIO_KEY = process.env.SCAVIO_API_KEY;

async function search(query) {
  const resp = await fetch('https://api.scavio.dev/api/v1/search', {
    method: 'POST',
    headers: { 'x-api-key': SCAVIO_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ query, country_code: 'us', num_results: 10 })
  });
  return (await resp.json()).organic_results || [];
}

async function audit(name, domain) {
  const brand = await search(`${name} reviews`);
  const competitors = await search(`${name} alternatives 2026`);
  return {
    mentions: brand.filter(r => r.link?.includes(domain)).length,
    competitors: competitors.slice(0, 3).map(r => r.title.split(' - ')[0])
  };
}

audit('Acme SaaS', 'acmesaas.com').then(r => {
  console.log(`Mentions: ${r.mentions}`);
  console.log(`Competitors: ${r.competitors.join(', ')}`);
});

Expected Output

JSON
3 prospects x 3 queries each
Estimated cost: $0.045

  prospect: Acme SaaS
  brand_mentions_in_top10: 2
  competitors: ['RivalCRM', 'BetterSaaS', 'CloudTools']
  top_pages: ['Acme SaaS - All-in-One CRM', 'Acme Blog: Sales Tips']
  has_blog: True

To: jane@acmesaas.com
Opening: I noticed Acme SaaS has been publishing content like "Acme Blog: Sales Tips" -- curious if you are tracking how that ranks against RivalCRM.

Related Tutorials

Frequently Asked Questions

Most developers complete this tutorial in 15 to 30 minutes. You will need a Scavio API key (free tier works) and a working Python or JavaScript environment.

Python 3.9+ installed. requests library installed. A Scavio API key from scavio.dev. A list of prospect company names or domains. A Scavio API key gives you 250 free credits per month.

Yes. The free tier includes 250 credits per month, which is more than enough to complete this tutorial and prototype a working solution.

Scavio has a native LangChain package (langchain-scavio), an MCP server, and a plain REST API that works with any HTTP client. This tutorial uses the raw REST API, but you can adapt to your framework of choice.

Start Building

Audit a prospect's search presence to write hyper-personalized cold emails. Find their top pages, rankings, and content gaps automatically.