# 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 | no | — | 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

```http
POST /v1/serp/volatility HTTP/1.1
Authorization: Bearer sof_live_your_key_here
Content-Type: application/json

{
  "niche": "health_wellness"
}
```

## Response

Wrapped in the standard envelope (id, request_id, object, created_at, elapsed_ms, credits) -- documented once on The Contract. The body below is an example response with this endpoint's `data` shape; values vary per request, and a `…` marks an array cut short for display.

```json
{
  "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. |
