Discover signals
Public, no-auth endpoints for Google Discover volatility — the 0–10 index that flags unannounced Discover updates, plus category, topic and format signals.
The Discover signals are open, no-auth aggregates over the same Google Discover data that powers the app. They report how much the Discover feed is reshuffling — overall, by editorial format, by topic, and by content category — plus a live category heatmap. No API key is needed: these endpoints sit at the same trust level as /health, expose only non-sensitive aggregates, and take no auth header. Every response is Redis-cached server-side, so poll on a sensible interval.
All five endpoints share one region parameter, country, which takes an ISO 3166-1 alpha-2 code (us, ru, de) or all for the global feed. Codes are case-insensitive and trimmed. Only a handful of markets have enough history for a full-confidence score; young regions fall back to a global default scale with lower confidence.
#Discover volatility index
/public/discover/volatilityPublicThe flagship early-warning signal: a 0..10 Google Discover volatility index for the last 30 days, scoring how much the feed reshuffles period-over-period. This is the primary unannounced-update detector.
countrystringoptionaldefaultall- Region to score. An ISO 3166-1 alpha-2 code (e.g.
us,ru), orallfor the global feed. Case-insensitive and trimmed. granularitystringoptionaldefaultdaydaycompares each day to the trailing 30 days.3hbuckets the last 30 days into 3-hour windows, each compared to the same 3h-of-day slot on prior days (diurnally normalised) to locate an update's onset within a day. Must match^(day|3h)$.
curl "https://d2tr.com/api/public/discover/volatility?country=us&granularity=day"{
"country": "us",
"granularity": "day",
"window_days": 30,
"baseline_days": 30,
"generated_at": "2026-07-01T09:12:44.512300+00:00",
"scale": "0..10 (multi-window 3/7/30d): ~3 = typical period, >=7 = anomalous (likely Discover update)…",
"calibration": {
"method": "multiwindow_level_transient_fixed_scale",
"windows_days": [3, 7, 30],
"center": 3.0,
"gain_per_sigma": 2.0,
"spike_index": 7.0,
"sustain_index": 5.0
},
"weights": { "new_domains": 0.5, "domain_shift": 0.3, "format_shift": 0.2 },
"summary": {
"current_index": 4.12,
"mean_index": 3.87,
"max_index": 8.35,
"trend": "rising",
"current_state": "active",
"episodes": [
{
"start": "2026-06-18",
"peak_index": 8.35,
"peak_period": "2026-06-19",
"end": "2026-06-22",
"status": "ended",
"periods": 5
}
],
"likely_update_days": ["2026-06-18", "2026-06-19"]
},
"series": [
{
"date": "2026-06-02",
"volatility_index": 3.44,
"is_spike": false,
"state": "calm",
"confidence": "high",
"baseline_days": 30,
"components": { "new_domains": 0.0821, "domain_shift": 0.1934, "format_shift": 0.0412 },
"signals": { "activity_3d": 0.08123, "mid_7d": 0.09541, "displacement_30d": 0.11203 },
"metrics": {
"total_impressions": 48213400,
"total_articles": 10233,
"unique_domains": 512,
"new_domains_count": 14,
"new_domain_impression_share": 0.0731,
"domain_distribution_jsd": 0.1841,
"unique_domain_change": 0.0312,
"format_distribution_jsd": 0.0412
},
"top_new_domains": [
{ "domain": "example.com", "impressions": 40213 }
]
}
]
}There is no pagination — the series is capped to the last 30 days (or 30 days × 8 buckets for 3h) and ordered ascending by period; summary.current_index is the last scored period. trend compares the last value to the mean of the prior ~7 periods (rising / falling / stable, needs ≥4 scored periods). Responses are Redis-cached for at least 15 minutes. A backend RPC failure returns 500 "Failed to compute volatility".
#Category (format-group) volatility
/public/discover/category-volatilityPublicPer-editorial-format-group volatility (0..10) for the last 30 days. Same model as /volatility, but computed within each enrichment_format_group (News / Analysis / Service / Ritual / Atmosphere), where the factors read as publisher churn plus sub-format churn.
countrystringoptionaldefaultall- Region — alpha-2 code or
all. granularitystringoptionaldefaultday- Time grouping.
dayor3h; must match^(day|3h)$. categorystringoptionaldefaultnull- Filter to one format group — one of
News,Analysis,Service,Ritual,Atmosphere. When set, returns that group only with full per-period detail; otherwise a compact series per category.
curl "https://d2tr.com/api/public/discover/category-volatility?country=all&granularity=day&category=News"{
"country": "all",
"granularity": "day",
"category_dimension": "enrichment_format_group",
"category_filter": "News",
"window_days": 30,
"generated_at": "2026-07-01T09:12:44.512300+00:00",
"scale": "0..10 (multi-window 3/7/30d): ~3 = typical period, >=7 = anomalous …",
"calibration": { "method": "multiwindow_level_transient_fixed_scale", "windows_days": [3, 7, 30], "center": 3.0, "gain_per_sigma": 2.0, "spike_index": 7.0, "sustain_index": 5.0 },
"weights": { "new_domains": 0.5, "domain_shift": 0.3, "format_shift": 0.2 },
"factors": {
"new_domains": "new publishers in the format group",
"domain_shift": "publisher impression-mix shift",
"format_shift": "sub-format (format type) mix shift within the group"
},
"categories": [
{
"category": "News",
"summary": {
"current_index": 5.02, "mean_index": 4.11, "max_index": 7.8,
"trend": "stable", "current_state": "active",
"episodes": [ { "start": "2026-06-19", "peak_index": 7.8, "peak_period": "2026-06-20", "end": null, "status": "ongoing", "periods": 3 } ],
"likely_update_days": ["2026-06-20"]
},
"series": [
{
"date": "2026-06-02",
"volatility_index": 4.02,
"is_spike": false,
"state": "calm",
"confidence": "high",
"baseline_days": 30,
"components": { "new_domains": 0.07, "domain_shift": 0.18, "format_shift": 0.03 },
"signals": { "activity_3d": 0.081, "mid_7d": 0.095, "displacement_30d": 0.112 },
"metrics": { "total_impressions": 20031400, "total_articles": 4021, "unique_domains": 210, "new_domains_count": 6, "new_domain_impression_share": 0.041, "domain_distribution_jsd": 0.171, "unique_domain_change": 0.02, "format_distribution_jsd": 0.03 },
"top_new_domains": [ { "domain": "example-news.com", "impressions": 12044 } ]
}
]
}
]
}Without a category filter, each category's series is compacted to { date|bucket, volatility_index, is_spike, state } to bound the payload, and categories is ranked by current_index descending (categories with no scored value sink to the bottom). With a filter, you get full per-period detail for that one group.
#Topic volatility
/public/discover/topic-volatilityPublicPer-topic volatility (0..10), daily only. Same model as /volatility, computed within each top-level Google-NL topic taxonomy category (Sports, News, Law & Government, Business & Industrial, Arts & Entertainment, Finance, Health, …).
countrystringoptionaldefaultall- Region — alpha-2 code, or
allfor a global view across regions. categorystringoptionaldefaultnull- Filter to one topic — a top-level Google-NL topic name, e.g.
Sports,News,Finance. When set, returns that topic only with full per-day detail; otherwise a compact daily series per topic.
curl "https://d2tr.com/api/public/discover/topic-volatility?country=us&category=Sports"{
"country": "us",
"granularity": "day",
"category_dimension": "gnl_topic_category",
"category_filter": "Sports",
"window_days": 30,
"generated_at": "2026-07-01T09:12:44.512300+00:00",
"scale": "0..10 (multi-window 3/7/30d): ~3 = typical period …",
"calibration": { "method": "multiwindow_level_transient_fixed_scale", "windows_days": [3, 7, 30], "center": 3.0, "gain_per_sigma": 2.0, "spike_index": 7.0, "sustain_index": 5.0 },
"weights": { "new_domains": 0.5, "domain_shift": 0.3, "format_shift": 0.2 },
"factors": {
"new_domains": "publishers new to the topic category",
"domain_shift": "publisher impression-mix shift within the topic",
"format_shift": "editorial format mix shift within the topic"
},
"categories": [
{
"category": "Sports",
"summary": { "current_index": 6.41, "mean_index": 4.02, "max_index": 8.9, "trend": "rising", "current_state": "active", "episodes": [], "likely_update_days": ["2026-06-28"] },
"series": [
{
"date": "2026-06-02",
"volatility_index": 3.9,
"is_spike": false,
"state": "calm",
"confidence": "high",
"baseline_days": 30,
"components": { "new_domains": 0.06, "domain_shift": 0.2, "format_shift": 0.04 },
"signals": { "activity_3d": 0.08, "mid_7d": 0.09, "displacement_30d": 0.11 },
"metrics": { "total_impressions": 8231400, "total_articles": 1904, "unique_domains": 142, "new_domains_count": 4, "new_domain_impression_share": 0.03, "domain_distribution_jsd": 0.19, "unique_domain_change": 0.02, "format_distribution_jsd": 0.04 },
"top_new_domains": [ { "domain": "example-sports.com", "impressions": 9044 } ]
}
]
}
]
}Unfiltered, categories is ranked by current_index descending and each series is compacted to { date, volatility_index, is_spike, state } — the same shape as the category-volatility list view. Backed by api_discover_topic_volatility_daily (min 1000 impressions per period inside the RPC), Redis-cached for 1 hour; an RPC failure returns 500.
#Format interest index
/public/discover/format-interestPublicA Google Discover format-preference index for the last 30 days. For each editorial format type (enrichment_format_type — the ~19 T-codes, e.g. T01_news_brief, T03_listicle, T16_ritual), it reports a daily index of how much Discover favours that format, computed as 100 × (0.5 × impression_share + 0.5 × article_share) — impression share and article share weighted equally. Values run roughly 0..100 and sum to ~100 per day across formats.
countrystringoptionaldefaultall- Region — alpha-2 code or
all. formatstringoptionaldefaultnull- Filter to one format type — an
enrichment_format_typevalue, e.g.T03_listicle. Shares are always computed against all-format day totals, then filtered.
curl "https://d2tr.com/api/public/discover/format-interest?country=us&format=T03_listicle"{
"country": "us",
"granularity": "day",
"window_days": 30,
"format_dimension": "enrichment_format_type",
"format_filter": "T03_listicle",
"generated_at": "2026-07-01T09:12:44.512300+00:00",
"index": "0..100 balanced share of Discover format attention: 50% impression-share + 50% article-share that day",
"holidays": {
"2026-07-04": [ { "country": "us", "name": "Independence Day", "kind": "public" } ]
},
"holidays_note": "date -> [{country, name, kind}] for the selected country (all served countries when country=all); join with series by date",
"formats": [
{
"format_type": "T03_listicle",
"summary": {
"current_index": 12.44,
"mean_index": 11.02,
"peak_index": 15.9,
"shift_vs_mean": 1.42,
"trend": "rising"
},
"series": [
{
"date": "2026-06-02",
"interest_index": 11.203,
"impressions": 5402100,
"articles": 1204,
"impression_share": 0.1121,
"article_share": 0.1119
}
]
}
]
}Formats are ranked by current_index descending. summary.trend compares the current value to the prior-week mean, with relative (15%) and absolute (0.5 pt) guards and needs ≥8 days (rising / falling / stable). The holidays map is joinable with each series by date and is non-essential — it becomes an empty object on lookup failure. Responses are Redis-cached for 1 hour; an RPC failure returns 500 "Failed to compute format interest".
#Top Discover categories
/public/discover/categoriesPublicThe top Google Discover content categories over the last 24 hours (Google-NL taxonomy, e.g. /News/Politics, /Sports/Team Sports/Soccer). This mirrors the web category heatmap.
countrystringoptionaldefaultallallsums every country; an alpha-2 code narrows to one market (across all languages).limitintegeroptionaldefault100- Top-N categories to return — an integer from
1to500inclusive.
curl "https://d2tr.com/api/public/discover/categories?country=us&limit=50"{
"country": "us",
"window_hours": 24,
"generated_at": "2026-07-01T09:12:44.512300+00:00",
"metrics": {
"impressions": "Discover reach estimate (est_url_reach) over 24h",
"delta_pct": "change vs the preceding 24h, %",
"publications": "distinct articles",
"publishers": "distinct domains",
"trends": "distinct trends touching the category",
"lifespan_hours": "avg hours an article stayed visible",
"share_of_impressions": "category reach / total window reach, %"
},
"categories_count": 50,
"categories": [
{
"category": "/News/Politics",
"impressions": 18402100,
"delta_pct": 12.4,
"publications": 842,
"publishers": 121,
"trends": 37,
"lifespan_hours": 9.3,
"share_of_impressions": 14.2
}
]
}The window is a hour-aligned 24 hours; delta_pct compares against the preceding 24h and may be null when there is no prior data, while lifespan_hours and share_of_impressions default to 0.0 when absent. There is no offset pagination — use limit only (max 500); ordering is by the RPC (reach/impressions). Responses are Redis-cached for 1 hour, and the hour-aligned window makes that cache exact. An RPC failure returns 500 "Failed to compute categories".