Scoring System

Scoring System

How Capyseo calculates SEO scores.

Overview

Capyseo assigns a score from 0-100 based on:

  • Number of issues found
  • Severity of issues
  • Issue weights
  • Page coverage

Higher score = better SEO health

Score Calculation

Score = 100 - (Weighted Issue Penalty)

Severity Weights

Severity Weight Description
error 10 Critical issues, major impact
warning 5 Important issues, moderate impact
info 1 Minor issues, low impact

Formula

Penalty = Σ (issue.severity.weight) Score = max(0, 100 - Penalty)

Example

Found: - 2 errors (10 × 2 = 20) - 3 warnings (5 × 3 = 15) - 5 info (1 × 5 = 5) Total penalty: 40 Score: 100 - 40 = 60

Score Ranges

Score Grade Status
90-100 A Excellent
80-89 B Good
70-79 C Fair
60-69 D Needs work
0-59 F Poor

Per-Page vs Overall Score

Per-Page Score

Each page gets its own score:

{ "pages": [ { "url": "/index.html", "score": 95 }, { "url": "/about.html", "score": 78 }, { "url": "/contact.html", "score": 85 } ] }

Overall Score

The overall score is the average of all page scores:

Overall = (95 + 78 + 85) / 3 = 86

Weighted Average

With many pages, each contributes equally:

Overall = Σ(page.score) / page.count

Rule Weights

Some rules can have custom weights via configuration:

// capyseo.config.js export default { rules: { 'meta-title': { severity: 'error', weight: 15, // Higher impact }, 'image-alt': { severity: 'warning', weight: 3, // Lower impact }, }, };

Score Modifiers

Maximum Penalty Cap

Issues on a single rule are capped:

If page has 20 missing alt texts: - Each is 5 points (warning) - Capped at 25 points total (not 100)

Minimum Score

Score never goes below 0:

100 - 150 penalties = 0 (not -50)

Score Breakdown

In verbose mode, see score breakdown:

Score Breakdown: ├── Meta Tags: 95/100 ├── Images: 80/100 ├── Headings: 100/100 ├── Links: 90/100 ├── Content: 85/100 ├── Social: 70/100 ├── Structured Data: 100/100 ├── Mobile: 95/100 ├── Security: 100/100 ├── Performance: 75/100 └── URL: 100/100 Overall Score: 87/100

Category Scores

Each category gets its own score:

interface CategoryScore { category: string; score: number; issues: number; errors: number; warnings: number; info: number; }

Category score calculation:

CategoryScore = 100 - (category.penalties / category.maxPenalty × 100)

JSON Output

Get detailed scoring in JSON:

capyseo analyze ./dist --format json
{ "score": 87, "pages": [ { "url": "/index.html", "score": 95, "breakdown": { "meta-tags": 100, "images": 90, "headings": 100 } } ], "summary": { "totalPages": 10, "averageScore": 87, "errors": 2, "warnings": 15, "info": 30 } }

Improving Scores

Priority Order

Fix issues in this order for maximum impact:

  1. Errors (10 points each)

    • Missing meta title
    • Missing meta description
    • Broken links
  2. Warnings (5 points each)

    • Missing alt text
    • H1 issues
    • Open Graph
  3. Info (1 point each)

    • Minor optimizations
    • Style suggestions

Quick Wins

Issue Points Fix
Add meta title +10 <title>...</title>
Add meta description +10 <meta name="description">
Add alt text (each) +5 <img alt="...">
Fix heading order +5 Proper H1-H6
Add Open Graph +5 <meta property="og:...">

Diminishing Returns

Once core issues are fixed, focus shifts to optimization. Going from 85 to 95 is harder than 50 to 75.

CI Thresholds

Set appropriate thresholds based on project maturity:

# New project capyseo analyze ./dist --ci --min-score 60 # Established project capyseo analyze ./dist --ci --min-score 80 # Production-ready capyseo analyze ./dist --ci --min-score 90

Tracking Over Time

Monitor score trends:

# Output to file with timestamp capyseo analyze ./dist --format json -o "seo-$(date +%Y%m%d).json"

Compare:

jq '.score' seo-20240101.json # 72 jq '.score' seo-20240201.json # 85 # Improved by 13 points!

Score vs Issues

High score doesn't mean zero issues:

Score Possible Issues
100 0 issues
95 ~5 info issues
90 ~2 warnings or ~10 info
80 ~2 errors or ~4 warnings
70 Multiple errors/warnings

Excluding from Score

Disable rules that don't apply:

// capyseo.config.js export default { rules: { 'twitter-card': { enabled: false }, // Don't use Twitter 'json-ld': { severity: 'info' }, // Lower impact }, };

Understanding Low Scores

If score is unexpectedly low:

  1. Check for many pages with same issue

    • Fix once in template
  2. Check for high-severity rules

    • One error = -10 points
  3. Check excluded pages

    • Error pages, drafts
  4. Review rule configuration

    • Adjust severities as needed

Best Practices

  1. Start realistic - Don't require 100 on day one
  2. Increase gradually - Raise threshold as you fix issues
  3. Focus on errors first - Biggest impact
  4. Track trends - Monitor improvements
  5. Customize weights - Match your priorities