OpportunityType Enum Categorization Criteria in SEO-Machine: Position-Based Classification
The OpportunityType enum in SEO-Machine categorizes keywords into six strategic buckets—quick_win, improvement, medium_term, new_content, declining, and under_performer—based strictly on current search position ranges, with each category triggering specific scoring algorithms that assign 10-100 point position scores in the OpportunityScorer class.
The OpportunityType enum is defined in data_sources/modules/opportunity_scorer.py (lines 12-19) and serves as the classification backbone for the SEO-Machine pipeline. This categorization system maps current search rankings to strategic SEO actions, enabling the automated scoring engine to calculate final priority levels ranging from CRITICAL to SKIP based on how easily each keyword can be improved. The enum is actively consumed by analysis scripts such as research_quick_wins.py and research_competitor_gaps.py to automate opportunity identification.
The Six OpportunityType Categories and Position Criteria
The enum defines six distinct SEO opportunity buckets. Each category's criteria are determined by the keyword's current position in search results and the strategic goal for that ranking.
QUICK_WIN: Position 11-20 (Second Page)
Quick wins target keywords ranking between positions 11 and 20—content sitting on the second page of search results. According to lines 5-16 of data_sources/modules/opportunity_scorer.py, these represent high-impact, low-effort opportunities because a small optimization push can land them on page one.
In the _calculate_position_score method, the scoring system applies graduated point values:
- Positions ≤12: 100 points (perfect quick win)
- Positions 13-15: 85 points
- Positions 16-18: 70 points
- Positions 19-20: 55 points
- All others: 30 points
IMPROVEMENT: First Page Positions 1-10 (Non-Top-3)
Improvement opportunities capture keywords already ranking on the first page (positions 1-10) but outside the top three. The strategic goal is moving these into positions 1-3 to maximize CTR and traffic share.
Lines 18-27 of the scorer implement the following position-based logic:
- Positions 4-5: 100 points
- Positions ≤7: 85 points
- Positions ≤10: 70 points
- All others: 40 points
MEDIUM_TERM: Position 21-50 (Outside First Two Pages)
Medium-term opportunities require significant SEO work. These keywords sit between positions 21 and 50, well outside the first two pages. The strategic goal is reaching page one, which demands substantial content enhancement and link building.
Lines 29-38 of _calculate_position_score break down the tiered scoring:
- Positions ≤30: 70 points
- Positions ≤40: 50 points
- Positions ≤50: 30 points
- All others: 10 points
NEW_CONTENT: Non-Ranking Competitor Gaps
New content opportunities identify keywords where your site does not currently rank (position >100 or missing data) but competitors do. This represents a content gap requiring new page creation rather than existing page optimization.
Unlike graduated scoring, lines 40-43 assign new content a flat 60-point baseline, acknowledging the uniform effort required to enter the SERPs from scratch.
DECLINING: Previously Strong Rankings
Declining opportunities flag keywords that historically performed well but are now dropping in rankings. While the enum definition exists for trend analysis downstream, lines 44-46 show the scoring engine treats these conservatively with a default 50-point position score when specific position data doesn't match other categories.
UNDERPERFORMER: High Rank with Low CTR
Underperformer identifies keywords ranking on the first page (high position) but generating click-through rates significantly below expected benchmarks for that position. Like declining keywords, lines 44-46 indicate these receive a default 50-point position score, though they trigger different strategic workflows for title tag and meta description optimization.
How _calculate_position_score Maps Categories to Priority
The OpportunityScorer class uses the opportunity type to determine the position score, which drives the final priority calculation (CRITICAL, HIGH, MEDIUM, LOW, or SKIP).
Each enum value routes through specific conditional logic in _calculate_position_score:
- QUICK_WIN checks
if position <= 12first, cascading through 13-15, 16-18, and 19-20 thresholds - IMPROVEMENT prioritizes the 4-5 range with 100 points, then relaxes for 6-7 and 8-10
- MEDIUM_TERM applies tiered deductions every 10 positions from 21-50
- NEW_CONTENT, DECLINING, and UNDERPERFORMER use flat scoring baselines
This position score then combines with search volume, keyword difficulty, SERP features, and trend data in the calculate_score method to produce the final prioritization.
Practical Implementation: Scoring Each Opportunity Type
Scoring a Quick Win Keyword
from data_sources.modules.opportunity_scorer import OpportunityScorer, OpportunityType
scorer = OpportunityScorer()
keyword = {
"position": 13, # falls in 11-20 range
"impressions": 1200,
"clicks": 20,
"ctr": 0.016,
"commercial_intent": 2.0
}
result = scorer.calculate_score(
keyword_data=keyword,
opportunity_type=OpportunityType.QUICK_WIN,
search_volume=2500,
difficulty=30,
serp_features=["featured_snippet"],
cluster_value=70,
trend_direction="rising",
trend_percent=30,
)
print(result["priority"], result["final_score"])
Scoring an Improvement Opportunity
For keywords already on page one but not in the top three:
result = scorer.calculate_score(
keyword_data={"position": 6, "impressions": 8000, "clicks": 120,
"ctr": 0.015, "commercial_intent": 1.8},
opportunity_type=OpportunityType.IMPROVEMENT,
search_volume=5000,
difficulty=20,
serp_features=["news_results"],
cluster_value=80,
trend_direction="stable",
)
Scoring a Medium-Term Opportunity
For keywords outside the first two pages requiring substantial work:
result = scorer.calculate_score(
keyword_data={"position": 35, "impressions": 400, "clicks": 5,
"ctr": 0.012, "commercial_intent": 1.0},
opportunity_type=OpportunityType.MEDIUM_TERM,
search_volume=1500,
difficulty=55,
serp_features=None,
cluster_value=50,
trend_direction="declining",
trend_percent=-25,
)
Scoring a New Content Gap
For competitor gaps where no current ranking exists:
result = scorer.calculate_score(
keyword_data={"position": 101, "impressions": 0, "clicks": 0,
"ctr": 0, "commercial_intent": 0.5},
opportunity_type=OpportunityType.NEW_CONTENT,
search_volume=3000,
difficulty=40,
serp_features=["top_stories"],
cluster_value=60,
trend_direction="rising",
trend_percent=80,
)
Summary
- The
OpportunityTypeenum indata_sources/modules/opportunity_scorer.pydefines six SEO opportunity categories based on current search position ranges. - QUICK_WIN targets positions 11-20 with graduated scoring from 55-100 points, emphasizing easy page-one potential.
- IMPROVEMENT focuses on first-page rankings (1-10) with 70-100 point scores to push content into top-3 positions.
- MEDIUM_TERM addresses positions 21-50 with 10-70 point scores, acknowledging the significant effort required.
- NEW_CONTENT assigns a flat 60-point baseline for non-ranking keywords identified through competitor gap analysis.
- DECLINING and UNDERPERFORMER use 50-point default scores while triggering specialized workflows for trend recovery or CTR optimization.
- The
_calculate_position_scoremethod implements these criteria at specific line ranges (5-46), feeding into the final priority calculation that drives SEO-Machine's recommendation engine.
Frequently Asked Questions
What file contains the OpportunityType enum definition?
The OpportunityType enum is defined in data_sources/modules/opportunity_scorer.py at lines 12-19. This file also contains the OpportunityScorer class and the _calculate_position_score method (lines 5-46) that implements the position-based categorization logic.
How does SEO-Machine determine if a keyword is a quick win versus a medium-term opportunity?
The classification depends entirely on current ranking position. Keywords in positions 11-20 are classified as QUICK_WIN because they require minimal effort to reach page one. Keywords in positions 21-50 are classified as MEDIUM_TERM because they need significant content work and link building to become competitive. The _calculate_position_score method checks these ranges sequentially to assign the appropriate category and corresponding point value (100 points for position ≤12 quick wins versus 70 points for position ≤30 medium-term).
What is the default position score for declining and underperformer keywords?
Both DECLINING and UNDERPERFORMER opportunity types receive a default position score of 50 points according to lines 44-46 of the scorer. While these categories exist primarily for trend analysis and CTR optimization workflows rather than position-based scoring, the 50-point baseline ensures they remain visible in the priority queue without artificially inflating or deflating their importance relative to position-based opportunities.
Can the position score thresholds be customized in the OpportunityScorer class?
The current implementation in data_sources/modules/opportunity_scorer.py uses hardcoded thresholds in the _calculate_position_score method (e.g., position ≤12 = 100 points for quick wins). To modify these criteria, you would need to edit the source code directly in the method's conditional logic at lines 5-46, as the class does not expose these ranges as configuration parameters in the current version of the repository.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →