Skip to content

Data Model Overview

VisibilityIQ uses an observation model architecture. Raw data from external sources (GSC, GA4, CrUX, Bing, and the platform data index) is never stored directly in the database or exposed to the scoring and audit layers. Instead, it’s normalized into typed observation models that have stable schemas regardless of which connector provided the data.

Different data sources report the same concept differently:

  • “Clicks” in GSC vs “sessions” in GA4 vs “visits” in Bing Webmaster are all measures of traffic but they’re not the same thing
  • Google, Bing, and the platform data index all have some version of “index status” but they report it with different codes, at different granularities, and on different schedules

The observation model gives audit checks, the scoring engine, and the API a stable contract to code against, regardless of where the data came from or how the upstream connector changed its format.

ModelWhat it captures
PageObservationCrawl and page-level findings (status, metadata, links, headers)
CanonicalObservationCanonical status from external sources (GSC selected canonical vs declared)
PerformanceObservationCWV and speed metrics (LCP, INP, CLS, TTFB, FCP)
TrafficObservationClicks, impressions, sessions, revenue per URL
IndexationObservationIndex status, coverage signals, URL inspection data
AIVisibilityObservationAI Overview citations, AI bot access status

Observation type schemas are versioned and backward-compatible. When a connector changes its output format (e.g., GA4 changes a field name), the connector’s normalization layer absorbs the change. Observation consumers (audit checks, scoring, API) never see the raw connector format.

Raw API responses from connectors are stored in R2 (object storage), not D1. D1 holds only the normalized observations. This means:

  • D1 stays lean (normalized fields, no multi-kilobyte JSON blobs)
  • Raw data is available for debugging but doesn’t affect query performance
  • R2 retention is limited (configurable, default 30 days) — raw data is not meant for long-term archival

The public API exposes observation data through typed endpoints:

GET /v1/projects/:id/pages/:url/observations
GET /v1/projects/:id/performance
GET /v1/projects/:id/traffic
GET /v1/projects/:id/indexation

Each response follows the typed schema for that observation type. See the individual observation type docs for the full field listing.