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

NameTypeRequiredDefaultDescription
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.

FieldDescription
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.