Concepts

Reference for the data model, scoring, and classification logic used throughout the Leto SEO API and dashboard.

Health score

Every site receives a composite health score from 0 to 100 computed once per day (05:30 UTC). The score is a weighted sum of component scores, each normalized to the same 0–100 scale before combining. The weights differ by site maturity — early-stage sites emphasize crawlability and impressions; established sites weight traffic and conversions more heavily.

RangeLabel
80100Healthy
5079Needs Attention
049Critical

Component weights

Early stage(≤100 organic sessions/mo)

indexing_progress30%
keyword_impression_trend30%
lighthouse_cwv25%
engagement_quality15%

Established(>100 organic sessions/mo)

traffic_trend30%
ranking_trend25%
cwv_status25%
conversion_trend20%

Source: src/lib/constants.ts → HEALTH_WEIGHTS, HEALTH_SCORE_RANGES

Core Web Vitals thresholds

Leto uses Google’s official CWV thresholds. Metrics are collected via PageSpeed Insights (lab data, weekly Monday scan). TBT (Total Blocking Time) is the lab proxy for INP (Interaction to Next Paint); both are tracked where available.

MetricLabelPass (good)WarningFail (poor)
LCPLargest Contentful Paint2500ms25014000ms>4000ms
INPInteraction to Next Paint200ms201500ms>500ms
TBTTotal Blocking Time (lab INP proxy)200ms201600ms>600ms
CLSCumulative Layout Shift0.10.110.25>0.25

Alert thresholds used by the CWV alert rule (cwv_fail) are slightly tighter — a page is flagged when any of the following lab values are exceeded:

  • LCP > 2500ms
  • CLS > 0.1
  • TBT > 200ms

Source: src/lib/constants.ts → CWV_THRESHOLDS, ALERT_THRESHOLDS.cwv_fail_threshold

Alert types and severities

Alerts are evaluated once per day (05:45 UTC cron). Duplicate alerts of the same type for the same site within 24 hours are suppressed. Alerts follow a active → acknowledged → resolved lifecycle.

traffic_dropmin baseline: 50 sessions/day

Compares yesterday’s organic sessions to the 7-day rolling average. Sites below the minimum baseline are skipped.

  • Warning — drop ≥ 10% vs 7-day avg
  • Critical — drop ≥ 30% vs 7-day avg
cwv_fail

Counts pages where any PSI lab metric exceeds the alert threshold (LCP > 2500ms, CLS > 0.1, TBT > 200ms).

  • Warning — any page exceeds any threshold
index_regression

Detects sudden drops in indexed page count as reported by Google Search Console. Compares current indexed count to the previous measurement.

  • Critical — indexed pages dropped ≥ 50%
campaign_underperformance

Checks active campaigns that have been running for more than 7 days. Compares GA4 session data ingested via UTM attribution.

  • Info — 0 GA4 sessions after 7 days (check UTM parameters)
  • Warning — sessions dropped >50% vs first-3-day baseline (min 10 sessions baseline)

Source: src/lib/alerts.ts, src/lib/constants.ts → ALERT_THRESHOLDS

Maturity classification

Every site is classified as either early or established based on its 30-day organic session count from GA4. The threshold is 100 organic sessions per month.

TierConditionDashboard focus
Early100 organic sessions/moIndexing progress, GSC impressions, avg position, Lighthouse score, engagement rate
Established>100 organic sessions/moOrganic sessions, avg position, CWV score, conversions, health score

Maturity affects which health score components are used (see Health score) and which KPI strip is shown in the per-site dashboard. The classification is re-evaluated on every daily health-score compute.

The API surfaces maturity in GET /api/v1/sites/:siteId as a maturity field with values "early" or "established". The orb_portfolio_health MCP tool accepts a filter_maturity parameter to restrict results to one tier.

Source: src/lib/constants.ts → MATURITY_THRESHOLD, HEALTH_WEIGHTS, KPI_CONFIG

Time-range semantics

The API accepts a period query parameter (or MCP argument) in several endpoints. Supported values are 7d, 28d, and 90d.

ValueMeaningTypical use
7dLast 7 calendar days (today excluded — see note below)Alert checks, short-term traffic trends
28dLast 28 calendar daysDefault for site overview, keyword trends, MCP tools
90dLast 90 calendar daysContent decay detection, long-term ranking trends

Day boundaries are UTC. A “day” runs from 00:00:00 UTC to 23:59:59 UTC.

GSC data lag. Google Search Console data typically arrives 2–3 days late. The API reads the latest available snapshot, so results for the most recent 3 days may be incomplete or absent. This lag is baked into the ingestion schedule (daily 04:00 UTC).

GA4 data lag. GA4 session data is available the following day (ingested at 04:30 UTC). Today’s sessions are never included in any period range.