SERP Data API
Export published ranking history and search engine results page (SERP) evidence for one tracked keyword, search configuration and engine. Use this endpoint for reporting integrations that need the stored evidence behind Search Performance, including available page-conflict information.
Create with POST https://app.wheremore.com/api/v2/exports/serp . Retrieve with GET https://app.wheremore.com/api/v2/exports/{export_id} . This guide describes default data contract 2026-09-19 . See API Access and API Keys for credentials, shared response fields, callbacks and errors.
Identify the keyword and search configuration
Use the public workspace ID from API Access → Your Workspaces and Markets. Discover configurations for a website with:
curl --get "https://app.wheremore.com/api/v2/markets" \
-H "X-API-Key: ${WHEREMORE_API_KEY}" \
--data-urlencode "workspace_id=ws_your_workspace_id" \
--data-urlencode "domain=example.com"
The free response contains data.workspace_id , domain , domain_id , markets and total . Each markets entry contains queue_id , market_id , market_name and search_engines .
Use queue_id , not market_id , in the export. The queue identifies the exact search configuration; the market/group identifier is not interchangeable. Select an engine returned for that queue. You also need the numeric ID of a keyword tracked in that configuration. This API has no keyword-list endpoint; obtain the tracked keyword ID from your Wheremore configuration or workspace administrator.
Request fields
Send a JSON object. Unknown fields are rejected.
| Field | Required | Accepted value |
|---|---|---|
workspace_id |
Yes | Public ws_... ID. |
queue_id |
Yes | Positive search-configuration ID from market discovery. |
keyword_id |
Yes | Positive tracked keyword ID. |
search_engine |
Yes | google , bing or yahoo , available in this queue. |
date_from , date_to |
Yes | Inclusive UTC YYYY-MM-DD dates; 1–366 days, no future end date. |
category_id |
No | Positive category ID to narrow the keyword's reporting scope. Omit for the default scope. |
freshness |
No | latest_published (default) or wait_for_fresh . |
data_contract |
No | 2026-09-19 (default) or 2026-09-10 . |
callback_url |
No | Public HTTPS readiness callback; see the shared guide for constraints. |
domain , keyword and market_id are not valid fields for this export. They belong to the older request design or discovery metadata.
Create and retrieve a report
Replace the sample IDs and dates. Set WHEREMORE_API_KEY securely before running the command.
curl --fail-with-body -X POST \
"https://app.wheremore.com/api/v2/exports/serp" \
-H "X-API-Key: ${WHEREMORE_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: search-report-20260919-001" \
-d '{
"workspace_id": "ws_your_workspace_id",
"queue_id": 101,
"keyword_id": 456,
"search_engine": "google",
"date_from": "2026-09-01",
"date_to": "2026-09-18",
"freshness": "latest_published",
"data_contract": "2026-09-19"
}'
- Save
export.idfrom the creation response. HTTP 201 means ready and 202 means pending. Neither charges or returns report data. - Retrieve that ID with the same user's API credentials:
curl --fail-with-body \
"https://app.wheremore.com/api/v2/exports/exp_0123456789abcdef0123456789abcdef" \
-H "X-API-Key: ${WHEREMORE_API_KEY}"
- If GET returns 202, poll with backoff or await the optional callback. HTTP 200 returns the saved report in
data.
Retry an identical POST with the same idempotency key. Use a new key for different parameters or a later report. Repeated GETs never refresh the saved snapshot.
Choose freshness deliberately
latest_published accepts the available published data even if its selected observation predates export creation. Use it for historical reporting.
wait_for_fresh requires date_to to include the export's UTC creation date. It waits for a selected SERP observation whose observed_at is at or after creation. It does not start a search or force a monitoring run. The saved date window stays fixed; if no qualifying observation arrives in that window, the job can remain pending until expiry. Ranking history alone does not satisfy this policy.
Returned data
Field inside data |
Meaning |
|---|---|
data_contract , workspace_id |
Response format and public workspace scope. |
contract_version |
The published reader's own contract version, separate from the API date. |
scope |
Queue, keyword, engine, category, website and related scope metadata. |
periods |
Current/comparison date windows and their available as-of dates. |
snapshot |
Publication coverage, projection status, snapshot identity and error flags. |
ranking |
Published current/comparison summaries and ranking history. |
serp |
Selected observation, organic_top_10 , SERP features, AI Overview evidence, events and counts. |
page_conflict |
Page-conflict evidence supplied by the reader, or null when unavailable. |
data_quality |
Reader coverage and confidence limitations, or null when unavailable. |
evidence_limits |
Reader limits on evidence rows, or null when unavailable. |
freshness |
policy , requested_at and source_observed_at . |
empty |
Whether the export lacks both a selected observation and current/comparison ranking history. |
definitions |
Interpretation notes included with the saved report. |
For example, this is a response fragment, not a complete report:
{
"data_contract": "2026-09-19",
"workspace_id": "ws_your_workspace_id",
"freshness": {
"policy": "latest_published",
"requested_at": "2026-09-19 10:00:00 UTC",
"source_observed_at": "2026-09-18 09:00:00"
},
"empty": false
}
Honor each history point's observation state. Missing observations are different from observed unranked results. A null position is not proof that a page is absent from the whole search engine.
organic_top_10 is bounded stored evidence, not the complete SERP. Features and AI Overview evidence belong to the selected observation; missing evidence does not prove a feature was absent. Preserve data_quality alongside conclusions. A null page_conflict is not a verified “no conflict” result. Read non-null findings with their evidence and limitations; overlapping pages alone do not establish harmful cannibalisation.
The API excludes cross-engine summaries. It does not export the full Search dashboard, same-page context, competitor series or AI analysis reports. Under explicit contract 2026-09-10 , page_conflict , data_quality and evidence_limits are omitted.
Charges, retention and access
A report with a selected observation or ranking history in either period costs 0.5 credits on first successful GET. A zero-unit published report is free. An unavailable selection may instead return a reader error; do not convert an error into an empty report. When the reader returns no publication, the export remains pending.
Repeated GETs of a paid export return identical data at zero additional charge. A new export is separately billable. Exports expire seven days after creation and are limited to 8 MiB.
Retrieval rechecks workspace and market access and that the queue still identifies the saved domain. Revocation can prevent retrieval even after an earlier payment.
Troubleshooting
- 400 after following an old example: replace keyword text and market/group fields with
keyword_idandqueue_id, and supply both dates. - 403 or inaccessible market: rediscover configurations for the correct workspace/website, check permissions and choose an engine available in the selected queue.
- 503 or no evidence for the period: check that the selected configuration has published data in the requested current period. Persistent errors can require source-processing investigation. Retain
meta.request_id. - Pending with
wait_for_fresh: check the fixed date window and monitoring schedule. Creating an export does not schedule a search. - An old observation keeps returning: the export is frozen. Create a new export to read a later publication.
- New fields are missing: check
export.data_contract. The earlier contract omits them. A current-contract field with null means the reader did not supply a value. - 413: narrow the reporting window or scope and use a new idempotency key.
For shared authentication, credit, callback and retry errors, use API Access and API Keys.