Skip to content

A CLI tool for monitoring SEO performance from the terminal.

Notifications You must be signed in to change notification settings

rajakhoury/seo-cli

Repository files navigation

SEO CLI

A simple CLI to monitor search performance and detect issues from your terminal.

Tests Coverage

Report Screenshot

Why

  • Unified Data: Pulls GSC + GA4 metrics into a single view.
  • Automated Checks: Flags cannibalization, content decay, and anomalies.
  • Health Score: Summarizes site performance (0-100) based on traffic, rank, and issues.
  • Shareable reports: Generates clean, shareable HTML reports via S3 with simple recommendations.

Designed for SEOs who prefer the terminal for quick SEO checks across one or many properties.

Features

  • Multi-account Connect multiple Google accounts and manage properties per project.
  • Data Ownership: Backfill and store historical data locally, bypassing the 16-month GSC limit.
  • Private Sharing: Publish reports to S3 with a single command.

Table of Contents


How It Works

1. Health Score (0–100)

A quick snapshot of overall site performance. It blends traffic trends (40%), average positions (30%), and open issues (30%) to show whether a site looks healthy or needs attention.

  • >80 = Green (Healthy)
  • 50-79 = Yellow (Warning)
  • <50 = Red (Critical)

2. Automated Checks

Runs a few checks on every data pull to catch common problems:

  • Cannibalization: Flags pages competing with each other when secondary pages take more than 10% of impressions.
  • Content Decay: Finds pages that are down more than 20% from their peak traffic.
  • Anomalies: Uses statistical checks (z-scores) to catch unusual traffic spikes or drops.

3. Opportunity Scanners

Looks for easy wins and missed opportunities:

  • Striking Distance: Keywords ranking on page two (positions 11–20) that could move up.
  • Low CTR: Pages ranking well (top 15) but getting fewer clicks than expected.

See Metrics Reference below for formulas and thresholds.


Reporting

Two simple ways to check your sites’ health:

1. CLI Status (Quick Check)

Use this for a fast check while you’re working: seo status <project-slug>

Terminal Status Demo

Displays:

  • Health Score
  • Traffic & Position Trends
  • Top Issues

2. Full HTML Report

Generates a modern HTML file that can be shared via S3 privately: seo report <project-slug>

The report includes 5 detailed tabs:

Tab What's In It
Overview KPI cards, weekly trends chart, position distribution, branded vs non-branded, device/country breakdown
Content & Keywords Top pages with GSC + GA4 metrics combined, top queries, engagement opportunities
Movements Keyword flux (new/lost), top gainers/losers, detected anomalies
Action Plan Cannibalization, Content Decay, Striking Distance, and CTR opportunities
Glossary Built-in reference

Commands Quick Reference

# Authentication
seo auth                    # Connect Google account
seo auth status             # Show connected accounts
seo auth remove <email>     # Remove account

# Properties
seo properties              # List discovered properties
seo properties sync         # Re-fetch properties from Google

# Projects
seo projects                # List all projects
seo projects new            # Create new project
seo projects show <slug>    # Show project details
seo projects edit <slug>    # Edit project settings
seo projects delete <slug>  # Delete a project
seo projects brand <slug> list               # Show brand keywords
seo projects brand <slug> add <keywords...>  # Add keywords
seo projects brand <slug> remove <keywords...> # Remove keywords
seo projects brand <slug> clear              # Remove all brand keywords

# Data
seo pull [slug] [--start <YYYY-MM-DD>] [--end <YYYY-MM-DD>] [--all]
seo backfill <slug> [--months <n>]  # Fetch historical data (up to 16 months)

# Analysis
seo status [slug] [--days <n>] [--limit <n>] [--no-queries] [--no-pages] [--full]
seo report <slug> [--days <n>] [--pdf]  # Generate HTML/PDF report

# Publish
seo publish <project-slug|file-name>     # Upload report to S3 and get a shareable private link

# Advanced
seo dev analyze <slug>  # Re-run checks on local data

Command Documentation

Authentication (auth)

seo auth

Opens browser for Google OAuth. You approve permissions for Search Console and Analytics.

seo auth status

Shows connected accounts with token status.

Email               Connected   Token Status
user@example.com    2025-12-06  Valid
team@example.com    2025-12-06  Needs refresh

seo auth remove <email>

Disconnects the specified account and removes stored credentials.

Properties (properties)

seo properties

Lists discovered GSC and GA4 properties for all connected accounts.

Google Search Console Properties:
┌─────┬────────────────────┬────────────────────┐
│ ID  │ Property           │ Account            │
├─────┼────────────────────┼────────────────────┤
│ 101 │ mysite.com         │ owner@example.com  │
│ 102 │ acme.com           │ owner@example.com  │
└─────┴────────────────────┴────────────────────┘

Google Analytics 4 Properties:
┌─────┬────────────────────┬────────────────────┐
│ ID  │ Property           │ Account            │
├─────┼────────────────────┼────────────────────┤
│ 201 │ MySite GA4         │ owner@example.com  │
│ 202 │ Acme GA4           │ owner@example.com  │
└─────┴────────────────────┴────────────────────┘

seo properties sync

Re-fetches properties from Google for all connected accounts.

Projects (projects)

seo projects

Lists all projects.

┌────────┬───────────────┬────────────────────┬────────────────────┬───────────┐
│ Slug   │ Name          │ GSC Property       │ GA4 Property       │ Created   │
├────────┼───────────────┼────────────────────┼────────────────────┼───────────┤
│ mysite │ MySite        │ mysite.com         │ Not connected      │ 12/7/2025 │
│ acme   │ Acme Corp     │ acme.com           │ Acme GA4           │ 12/7/2025 │
└────────┴───────────────┴────────────────────┴────────────────────┴───────────┘

seo projects new

Creates a new project interactively. Choose one GSC property (required), optionally a GA4 property, and optionally add brand keywords.

? Project name: Demo Detailer
? Slug: demo
? Select GSC property: demo.com (owner@example.com)
? Select GA4 property (optional): Demo GA4
? Brand keywords (comma-separated, blank to skip): demo detailer, demo.com

✓ Project 'demo' created
  • GSC: demo.com
  • GA4: Demo GA4
  • Brand: demo detailer, demo.com

seo projects show <slug>

Shows project details including connected properties and brand keywords.

seo projects edit <slug>

Edit project name or GA4 property.

seo projects delete <slug>

Delete project.

seo projects brand <slug> list|add|remove|clear

Manage brand keywords (add, remove, list, clear).

seo projects brand mysite add "brand name, brand variant"
seo projects brand mysite remove "old keyword"
seo projects brand mysite clear

Data Commands

seo pull [slug]

Pulls data from GSC and GA4, aggregates weekly metrics, and runs analysis.

Options:

  • --all Pull all projects
  • --start <YYYY-MM-DD> Start date
  • --end <YYYY-MM-DD> End date

Defaults to last 7 days ending 3 days ago.

Date range: 2025-12-01 to 2025-12-05

Demo
Fetching GSC data...
GSC: 1,234 records
Aggregating weekly...
Fetching GA4 data...
GA4: 567 records
Analyzing data...
Analyzed: 2 anomalies, 1 cannibalization, 0 decay

✓ Data pull complete

seo backfill <slug>

Fetch historical data up to 16 months. Processes monthly chunks.

Options:

  • --months <n> Number of months (default: 16)
Backfilling 16 months of data for Demo
Processing 16 date chunks...

[1/16] 520 records
[2/16] 480 records
...

Backfill complete: 8,900 total records

Status & Reports

seo status

Quick SEO status summary for all projects.

┌──────────┬────────┬────────┬───────────┬────────┬────────┐
│ Project  │ Clicks │ Change │ Position  │ Issues │ Health │
├──────────┼────────┼────────┼───────────┼────────┼────────┤
│ demo     │ 12.4k  │ +15.2%↑│ 12.4      │ 4 ⚠    │ 82     │
└──────────┴────────┴────────┴───────────┴────────┴────────┘

seo status <slug>

Shows expanded metrics, issues, and top pages for one project.

Options:

  • -d, --days <n> Days to analyze (default: 7)
  • --limit <n> Top queries/pages to show (default: 20)
  • --no-queries Hide queries section
  • --no-pages Hide pages section
  • --full Show full URLs without truncation
Demo Detailer
Last 30 days | Health Score: 82
Window: Nov 12 → Dec 12, 2025

Search Console
┌─────────────┬──────────────────┐
│ Clicks      │ 12,450 (+15.2% ↑)│
├─────────────┼──────────────────┤
│ Impressions │ 345,210 (+8.4% ↑)│
├─────────────┼──────────────────┤
│ CTR         │ 3.6% (+0.2% ↑)   │
├─────────────┼──────────────────┤
│ Position    │ 12.4 (-1.2)      │
└─────────────┴──────────────────┘

Analytics
Sessions: 14,200 (+12.5% ↑)  •  Users: 11,500  •  Engagement: 58.4%  •  Conversions: 342

Issues (4)
  • 2 Anomaly Alerts
  • 1 Cannibalization Issues
  • 1 Content Decay
  Run "seo report demo" for details.

Top Queries
┌──────────────────────────────┬────────┬────────┬───────┬──────┐
│ Query                        │ Clicks │   Impr │   CTR │  Pos │
├──────────────────────────────┼────────┼────────┼───────┼──────┤
│ demo detailer                │  1,850 │  3,500 │ 52.8% │  1.0 │
│ mobile detailing near me     │  1,240 │ 12,500 │  9.9% │  2.1 │
│ car detailing metro city     │    850 │  5,600 │ 15.2% │  1.4 │
│ demo.com reviews             │    620 │  1,200 │ 51.6% │  1.1 │
│ interior car cleaning cost   │    420 │  3,100 │ 13.5% │  3.2 │
└──────────────────────────────┴────────┴────────┴───────┴──────┘

Top Pages
┌──────────────────────────────┬────────┬────────┬──────┐
│ Page                         │ Clicks │   Impr │  Pos │
├──────────────────────────────┼────────┼────────┼──────┤
│ /services/interior-detailing │  2,100 │ 45,000 │  4.2 │
│ /services/ceramic-coating    │  1,850 │ 32,000 │  3.5 │
│ /pricing                     │  1,500 │ 12,000 │  2.1 │
│ /blog/how-often-should-i-det │    940 │ 28,000 │  6.8 │
└──────────────────────────────┴────────┴────────┴──────┘

seo report <slug>

Generates HTML report (and optional PDF).

Options:

  • --days <n> Days to include (default: 30)
  • --pdf Also generate PDF
Fetching data for 2025-11-04 → 2025-12-04...
✓ Data ready (9,450 GSC records)
Generating report...
✓ Report generated: ./reports/demo-2025-12-07.html

Reports saved to ./reports/<slug>-<date>.html

Publish (publish)

seo publish [target]

Publishes an HTML report to AWS S3 and returns a time-limited shareable URL.

Arguments:

  • target Project slug (e.g., demo) to publish the latest report, or a specific file name (e.g., demo-2025-12-07.html) in ./reports/.

Examples:

seo publish demo
seo publish demo-2025-11-11.html

Output:

✔ Shareable Link:
https://s3-.../reports/demo/demo-2025-12-07.html?X-Amz-...

Link expires in 7 days.

Notes:

  • Requires .env variables: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_BUCKET_NAME.
  • If the target cannot be resolved, ensure the project slug is correct or the file exists in reports/.

Advanced

seo dev analyze <slug>

Re-runs checks on existing data without pulling from Google. Useful for testing.


Installation

Requirements

  • Node.js 20+
  • Google account with Search Console access

Setup

git clone https://github.com/rajakhoury/seo-cli.git
cd seo-cli
npm install

Copy .env.example to .env and fill in your Google OAuth credentials.

Initialize the database:

npx prisma migrate dev --name init

Build:

npm run build

Run CLI globally (optional):

# Link the CLI to use 'seo' command anywhere
npm link

# Usage
seo auth
seo projects new
seo pull mysite

Alternatively, you can run (locally from project root) without linking using node dist/index.js <command>.

Google Cloud Setup

  1. Go to Google Cloud Console
  2. Create a new project (or use existing)
  3. Enable APIs:
    • Google Search Console API
    • Google Analytics Data API
  4. Go to APIs & Services → Credentials
  5. Create OAuth 2.0 Client ID → Select Desktop app type
  6. Download the JSON file
  7. Open OAuth consent screen → Add yourself as a Test user
  8. Add these scopes:
    • https://www.googleapis.com/auth/webmasters.readonly
    • https://www.googleapis.com/auth/analytics.readonly
    • https://www.googleapis.com/auth/userinfo.email
  9. Add the credentials to your .env file

AWS S3 Setup (optional, for report sharing)

If you want to share reports via private URLs:

  1. Create an S3 bucket (e.g., seo-cli-reports)
  2. Create an IAM user with S3 permissions
  3. Add access keys to .env: AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, AWS_BUCKET_NAME

Data Configuration

API Limits & Data Depth

Fetches up to the API maximum per request to capture long-tail query data.

Service Current Setting API Maximum
GSC 25,000 rows 25,000
GA4 25,000 rows 100,000

Adjusting for Speed If you prefer faster pulls over complete data depth, you can lower these values in src/services/gsc-service.ts (rowLimit) and src/services/ga4-service.ts (limit).


Technical Details

  • Database: SQLite (stored in data/seo-cli.db)
  • APIs: Google Search Console API, Google Analytics Data API (v1beta)
  • Data freshness: 3-day stability window to align GSC and GA4 data

Reset Database

To start fresh:

rm -rf data prisma/migrations
npx prisma migrate dev --name init
npm run build

Metrics Reference

Core Metrics

Metric Source Definition & Calculation
Clicks GSC Total clicks from search. Calculated as the sum of daily clicks.
Impressions GSC Total times the site appeared in search results. Sum of daily impressions.
CTR GSC (Total Clicks ÷ Total Impressions) × 100. Calculated from totals, never averaged.
Avg Position GSC Sum(Position × Impressions) ÷ Total Impressions. Impression-weighted average.
Sessions GA4 A group of user interactions. Ends after 30 minutes of inactivity.
Engagement Rate GA4 % of sessions lasting >10s, having 2+ pageviews, or a conversion.
Conversion Rate GA4 (Conversions ÷ Sessions) × 100.
Est. Search Vol Derived Impressions ÷ (Expected CTR ÷ 100). Estimated using position-based CTR curves.

Health Score Logic

Formula: (Traffic Score × 0.4) + (Position Score × 0.3) + (Issues Score × 0.3)

Component Weight Calculation (Capped 0-100)
Traffic Score 40% Base 50 + (Clicks Change %)
Position Score 30% Base 50 - (Position Change × 10)
Issues Score 30% Base 100 - (Count of Open Issues × 10)

Issue Detection Logic

Keyword Cannibalization

Note: Not all overlap is bad. In Local SEO, having multiple pages ranking for "roof repair near me" (e.g., /austin and /dallas pages) is often desired. Always verify before merging.

  • Trigger: Secondary page impressions > 10% of Primary page. Min 100 total impressions.
  • Cannibalization Score: Secondary Impressions ÷ Primary Impressions
  • Priority: Total Impressions × Cannibalization Score

Content Decay

  • Trigger: >20% decline from peak (peak must be >2 months ago and >50 clicks).
  • Formula: (Peak Clicks - Current Clicks) ÷ Peak Clicks
  • Likely Causes: Ranking Drop (Pos worsened 5+), CTR Decline (Impr stable, CTR down), or Competitor activity.

Anomaly Detection

  • Method: Modified Z-Score (robust against outliers).
  • Trigger: |Z| > 3.5 (roughly 99.7% confidence).
  • Baseline: Last 30 days of data (requires min 7 days).

Opportunity Calculations

Type Definition Potential Impact Calculation
Striking Distance Keywords in Pos 11-20 (Page 2). Impressions × 8% (8% = estimated Page 1 CTR gain)
CTR Opportunity High impressions (>500), low CTR. Pos ≤ 15. Deserved Clicks - Actual Clicks (using Curve B below)
Content Refresh Recovering lost traffic from decay. Peak Clicks - Current Clicks

Reference: Expected CTR Curves

Used for search volume estimation (Curve A) and CTR opportunity checks (Curve B).

Position Curve A (Vol Est) Curve B (Opp Check)
1 27.6% 28%
2 15.8% 15%
3 11.0% 11%
4 8.4% 8%
5 6.3% 6%
6-10 4.9% → 2.2% 5% → 2%
11-20 1.5% -

Roadmap

Future enhancements:

  • Dependency upgrades: Upgrade outdated packages.
  • Adjustable Data Depth: Add a DATA_DEPTH environment variable to control how much data is pulled from APIs.
    • Lite: 5k rows (fast daily checks)
    • Standard: 25k rows (default)
    • Complete: 100k rows (GA4 max; GSC remains capped at 25k)
  • Faster report generation: Add a --no-fetch flag to seo report to rebuild reports from existing data without pulling.

Contributing

Pull requests welcome. For major changes, open an issue first.


License

MIT © Raja Khoury

About

A CLI tool for monitoring SEO performance from the terminal.

Resources

Stars

Watchers

Forks

Packages

No packages published