docs / endpoint

POST /v1/serp/history

Position history for a (domain, keyword) pair from our SERP archive.

Credits: 5 credits per call. Timeout: 60s

Parameters

NameTypeRequiredDefaultDescription
domain string required Domain to check ranking history for (bare hostname, lowercased). Required, max 255 chars, no spaces.
keyword string required Keyword to check ranking history for. Required, max 700 chars.
engine string optional "google" Optional, default "google". Same enum as /v1/search.
location any optional 2840 Google Ads geotarget ID. Optional, default 2840 (United States). Look one up with /v1/locations.
language string optional "en" ISO language code. Optional, default "en".
device string optional "desktop" One of: desktop, mobile. Optional, default "desktop".
date_from string optional ISO YYYY-MM-DD, inclusive. Optional, default 730 days ago -- the rankings archive's TTL floor. An empty result is still a chargeable answer: coverage.observations counts the distinct days we archived this exact SERP in the window, so it can mean either "we observed you on N days and you never ranked" (observations > 0) or "we never looked" (observations: 0).
date_to string optional ISO YYYY-MM-DD, inclusive. Optional, default today.

Request

→ requestPOST /v1/serp/history
POST /v1/serp/history HTTP/1.1
Authorization: Bearer sof_live_your_key_here
Content-Type: application/json

{
  "domain": "example.com",
  "keyword": "best running shoes",
  "engine": "google",
  "location": 2840,
  "language": "en",
  "device": "desktop",
  "date_from": "2026-05-20",
  "date_to": "2026-07-28"
}

Response

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

← response200
{
  "id": "serp_d5ol4mx5bnnin24qqnksxcz5",
  "request_id": "req_dhxllbqudbfwhaay4p2eo4ajza",
  "object": "serp_history",
  "created_at": "2026-08-09T07:23:32Z",
  "elapsed_ms": 180,
  "cache": "miss",
  "credits": {
    "charged": 5,
    "balance": 9857
  },
  "data": {
    "domain": "example.com",
    "keyword": "best running shoes",
    "engine": "google",
    "location": 2840,
    "language": "en",
    "device": "desktop",
    "points": [
      {
        "date": "2026-06-03",
        "rank": 9,
        "url": "https://example.com/best-running-shoes",
        "type": "organic"
      },
      {
        "date": "2026-06-17",
        "rank": 6,
        "url": "https://example.com/best-running-shoes",
        "type": "organic"
      }
    ],
    "first_seen": "2026-06-03",
    "best_rank": 6,
    "coverage": {
      "from": "2026-05-20",
      "observations": 14
    }
  }
}

Response fields

What each field in data (above) means.

FieldDescription
data.domain The domain you requested.
data.keyword The keyword you requested.
data.engine Search engine this history is drawn from.
data.location Google Ads geotarget ID this history is drawn from.
data.language ISO language code this history is drawn from.
data.device Device this history is drawn from: desktop or mobile.
data.points One entry per day with an archived observation for this (domain, keyword) pair, in the requested date window.
data.points[].date Date of this observation, YYYY-MM-DD.
data.points[].rank Absolute rank on that day's results page, or null if the domain didn't appear.
data.points[].url URL that ranked, or null if the domain didn't appear.
data.points[].type Result type this rank came from, e.g. "organic".
data.first_seen Date of the earliest point in this response, or null if points is empty.
data.best_rank Best (lowest-numbered) rank across all points, or null if the domain never appeared.
data.coverage How much of the rankings archive this response drew from.
data.coverage.from Earliest date the archive could have observed, floor-clamped to the archive's retention window.
data.coverage.observations Number of distinct days we archived this exact result page in the window -- can be > 0 even when every point shows no ranking.