docs / endpoint
POST /v1/serp/volatility
Day-by-day movement in US Google results over the last 30 days, scored against our own recent history for that market.
Credits: 1 credit per call.
Timeout: 60s
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
niche |
string | optional | — | One of: commerce_retail, education_public, finance_legal, health_wellness, home_industry, media_entertainment, tech_software, travel_hospitality, unclassified. Optional. Narrows the response's days[].niches breakdown to this one niche. It does not re-score the day: score and change_pct are always whole-market, with or without niche set. Omit for the whole market. |
Request
→ requestPOST /v1/serp/volatility
POST /v1/serp/volatility HTTP/1.1
Authorization: Bearer sof_live_your_key_here
Content-Type: application/json
{
"niche": "health_wellness"
}
Response
The usual envelope (id, request_id, object, created_at, elapsed_ms, credits) is documented once on The Contract. Below is an example response with this endpoint's data. Values vary per request; … marks an array cut short.
← response200
{
"id": "serp_srzdoi2uajltrgbn6rclc247",
"request_id": "req_vyep4vhzrrjxrdpjsdbumfam5i",
"object": "serp_volatility",
"created_at": "2026-07-29T12:00:00Z",
"elapsed_ms": 244,
"credits": {
"charged": 1,
"balance": 9857
},
"data": {
"location": 2840,
"language": "en",
"window": "14d",
"basis_version": 1,
"days": [
{
"date": "2026-09-03",
"score": 74,
"change_pct": -4.2,
"status": "ok",
"niches": [
{
"niche": "health_wellness",
"vs_market": 1.52
}
],
"features": [
{
"feature": "ai_overview",
"presence": 0.49,
"delta": 0.02
}
]
},
{
"date": "2026-09-04",
"score": 83,
"change_pct": 12.5,
"status": "ok",
"niches": [
{
"niche": "health_wellness",
"vs_market": 1.44
}
],
"features": [
{
"feature": "ai_overview",
"presence": 0.51,
"delta": 0.04
}
]
},
"…"
],
"coverage": {
"days": 31,
"days_with_data": 28,
"days_insufficient": 2,
"days_not_computed": 1,
"data_through": "2026-09-04",
"updated_at": "2026-09-05T02:14:00Z"
}
}
}
Response fields
What each field in data (above) means.
| Field | Description |
|---|---|
data.location |
Google Ads geotarget ID this reading covers. Always 2840 (United States) -- not a request parameter. |
data.language |
ISO language code this reading covers. Always "en" -- not a request parameter. |
data.window |
What each day is compared against -- "14d" means every day is measured against the same market two weeks earlier. |
data.basis_version |
Version of the reference keyword basket these scores are measured against. If it changes between two calls, days either side of the change are not comparable. |
data.days |
One entry per calendar day in the fixed window, ascending. The window is a 30-day span ending today, so it covers 31 calendar dates -- both endpoints included. |
data.days[].date |
The day, YYYY-MM-DD. |
data.days[].score |
0-100: how much results moved that day, ranked against the trailing 60 days in this market. 83 means more movement than 83% of recent days. Null until there are 20 days of history to rank against, and on any day that is not "ok". |
data.days[].change_pct |
Percent change in movement against the previous day. Null when either day is not "ok". |
data.days[].status |
"ok", "insufficient_data" (too few observations that day: score, change_pct and niches[].vs_market are null; features may still carry real readings), or "not_computed" (no reading for that day; chart it as a gap, never as zero). |
data.days[].niches |
Per-niche movement for that day, always for the United States -- the one market this reading covers. |
data.days[].niches[].niche |
Niche key. |
data.days[].niches[].vs_market |
This niche's movement divided by the whole market's that day. 1.52 means the niche moved 52% more than the market. |
data.days[].features |
SERP features observed that day. A feature absent from this list was either not observed or seen too few times to report -- the two are not distinguishable here, and neither is the same as 0%. |
data.days[].features[].feature |
Feature key, e.g. "ai_overview". |
data.days[].features[].presence |
Share of observed SERPs carrying this feature that day, 0-1. |
data.days[].features[].delta |
Change in presence against its 28-day baseline. |
data.coverage.days |
Days returned. |
data.coverage.days_with_data |
Of those, how many have a reading. |
data.coverage.days_insufficient |
How many had too few observations. |
data.coverage.days_not_computed |
How many have no reading at all. |
data.coverage.data_through |
Most recent day we computed anything for, or null. A day that was computed but suppressed for thinness still advances it, so this can point at a day whose score, change_pct and niches[].vs_market are null -- not a promise of a fully-scored day. |
data.coverage.updated_at |
When these readings were last computed, or null. |