Skip to main content

Overview

The OpportunityScorer class provides a comprehensive scoring system for prioritizing SEO opportunities. It considers 8 weighted factors to calculate opportunity scores and identify the highest-value content optimization tasks.

Installation

from data_sources.modules.opportunity_scorer import OpportunityScorer, OpportunityType

Scoring Methodology

The scorer uses 8 weighted factors:
  • Volume Score (25%): Search demand/impressions
  • Position Score (20%): Proximity to target position
  • Intent Score (20%): Commercial value
  • Competition Score (15%): Ranking difficulty
  • Cluster Score (10%): Strategic topic value
  • CTR Score (5%): Improvement potential
  • Freshness Score (5%): Content update requirements
  • Trend Score (5%): Rising/declining interest

Opportunity Types

from data_sources.modules.opportunity_scorer import OpportunityType

OpportunityType.QUICK_WIN        # Position 11-20, close to page 1
OpportunityType.IMPROVEMENT      # Position 1-10, can reach #1-3
OpportunityType.MEDIUM_TERM      # Position 21-50, needs significant work
OpportunityType.NEW_CONTENT      # Not ranking, competitor gap
OpportunityType.DECLINING        # Was good, now dropping
OpportunityType.UNDERPERFORMER   # High ranking, low CTR

Initialization

scorer = OpportunityScorer()

Methods

calculate_score

Calculate comprehensive opportunity score.
result = scorer.calculate_score(
    keyword_data={
        'keyword': 'podcast hosting',
        'position': 12.3,
        'impressions': 1500,
        'clicks': 15,
        'ctr': 0.01,
        'commercial_intent': 2.5
    },
    opportunity_type=OpportunityType.QUICK_WIN,
    search_volume=2000,
    difficulty=45,
    serp_features=['featured_snippet', 'people_also_ask'],
    cluster_value=75,
    trend_direction='rising',
    trend_percent=25
)
keyword_data
dict
required
GSC data with position, impressions, clicks, ctr
keyword
str
Keyword text
position
float
Average position
impressions
int
Total impressions
clicks
int
Total clicks
ctr
float
Click-through rate (0-1)
commercial_intent
float
Commercial intent score (0.1-3.0)
opportunity_type
OpportunityType
default:"QUICK_WIN"
Type of opportunity being scored
search_volume
int
Monthly search volume (from DataForSEO)
difficulty
int
SEO difficulty 0-100 (from DataForSEO)
serp_features
list
SERP features present
cluster_value
int
Strategic value of topic cluster (0-100)
trend_direction
str
‘rising’, ‘stable’, or ‘declining’
trend_percent
float
Percent change in search volume
result
dict
final_score
float
Final weighted score (0-100)
score_breakdown
dict
Individual scores for each factor
priority
str
CRITICAL, HIGH, MEDIUM, LOW, or SKIP
primary_factor
str
Primary factor driving the score
score_explanation
str
Human-readable explanation

calculate_potential_traffic

Calculate potential traffic increase from ranking improvement.
traffic = scorer.calculate_potential_traffic(
    current_position=12.3,
    target_position=7,
    impressions=1500,
    current_clicks=15
)
current_position
float
required
Current average position
target_position
int
required
Target position to reach
impressions
int
required
Monthly impressions
current_clicks
int
required
Current monthly clicks
traffic
dict
current_clicks
int
Current monthly clicks
current_position
float
Current position
current_ctr
float
Current CTR percentage
current_expected_ctr
float
Expected CTR for current position
target_position
int
Target position
target_expected_ctr
float
Expected CTR at target position
potential_clicks
int
Projected clicks at target position
additional_clicks
int
Additional clicks gained
percent_increase
float
Percentage increase

Expected CTR by Position

The scorer uses industry-average CTR by position:
PositionExpected CTR
131.6%
215.7%
310.5%
47.5%
55.9%
64.8%
74.1%
83.5%
93.1%
102.7%
11-201.8% - 0.7%

Priority Levels

  • CRITICAL: Score >= 80 OR (Quick win + high intent + very close to page 1)
  • HIGH: Score >= 65
  • MEDIUM: Score >= 45
  • LOW: Score >= 25
  • SKIP: Score < 25 (not worth pursuing)

Volume Score Tiers

if volume >= 5000:   return 100
elif volume >= 2000: return 90
elif volume >= 1000: return 80
elif volume >= 500:  return 65
elif volume >= 250:  return 50
elif volume >= 100:  return 35
elif volume >= 50:   return 20
else:                return 10

Competition Score (Difficulty Inverted)

if difficulty <= 20: return 100  # Very easy
elif difficulty <= 35: return 85   # Easy
elif difficulty <= 50: return 70   # Moderate
elif difficulty <= 65: return 50   # Difficult
elif difficulty <= 80: return 30   # Very difficult
else:                  return 10   # Extremely difficult

Example Usage

from data_sources.modules.opportunity_scorer import OpportunityScorer, OpportunityType

scorer = OpportunityScorer()

# Score a quick win opportunity
keyword_data = {
    'keyword': 'podcast hosting software',
    'position': 12.3,
    'impressions': 1500,
    'clicks': 15,
    'ctr': 0.01,
    'commercial_intent': 2.5  # High commercial intent
}

result = scorer.calculate_score(
    keyword_data=keyword_data,
    opportunity_type=OpportunityType.QUICK_WIN,
    search_volume=2000,
    difficulty=45,
    serp_features=['featured_snippet', 'people_also_ask'],
    cluster_value=75,
    trend_direction='rising',
    trend_percent=25
)

print("Opportunity Score Analysis")
print("=" * 60)
print(f"Keyword: {keyword_data['keyword']}")
print(f"Final Score: {result['final_score']}/100")
print(f"Priority: {result['priority']}")
print(f"Primary Factor: {result['primary_factor']}")
print(f"\nScore Breakdown:")
for factor, score in result['score_breakdown'].items():
    print(f"  {factor}: {score}/100")
print(f"\nExplanation: {result['score_explanation']}")

# Calculate traffic potential
traffic = scorer.calculate_potential_traffic(
    current_position=keyword_data['position'],
    target_position=7,
    impressions=keyword_data['impressions'],
    current_clicks=keyword_data['clicks']
)

print(f"\nTraffic Potential:")
print(f"  Current: {traffic['current_clicks']} clicks/month (position {traffic['current_position']})")
print(f"  Potential: {traffic['potential_clicks']} clicks/month (position {traffic['target_position']})")
print(f"  Gain: +{traffic['additional_clicks']} clicks (+{traffic['percent_increase']}%)")

Example Output

Opportunity Score Analysis
============================================================
Keyword: podcast hosting software
Final Score: 78.5/100
Priority: HIGH
Primary Factor: volume

Score Breakdown:
  volume_score: 90.0/100
  position_score: 85.0/100
  intent_score: 83.3/100
  competition_score: 70.0/100
  cluster_score: 75.0/100
  ctr_score: 85.0/100
  freshness_score: 50.0/100
  trend_score: 70.0/100

Explanation: Strong volume, position, intent. Weak freshness

Traffic Potential:
  Current: 15 clicks/month (position 12.3)
  Potential: 62 clicks/month (position 7)
  Gain: +47 clicks (+313.3%)

Integration with GSC

Use with Google Search Console data:
from data_sources.modules.google_search_console import GoogleSearchConsole
from data_sources.modules.opportunity_scorer import OpportunityScorer, OpportunityType

gsc = GoogleSearchConsole()
scorer = OpportunityScorer()

# Get quick wins from GSC
quick_wins = gsc.get_quick_wins(days=30)

# Score each opportunity
for kw in quick_wins[:10]:
    result = scorer.calculate_score(
        keyword_data=kw,
        opportunity_type=OpportunityType.QUICK_WIN,
        cluster_value=50  # Default cluster value
    )
    
    print(f"\n{kw['keyword']}")
    print(f"  Score: {result['final_score']}/100 ({result['priority']})")
    print(f"  Position: {kw['position']}")
    print(f"  Impressions: {kw['impressions']:,}")

Source Code Reference

Location: data_sources/modules/opportunity_scorer.py:23 See also:

Build docs developers (and LLMs) love