SERP Data API
The Search Performance API exports the saved search-results page for one tracked keyword, website, search engine and market.
Endpoints
POST https://app.wheremore.com/api/v2/exports/serp
GET https://app.wheremore.com/api/v2/exports/{export_id}
POST is free. The first successful GET of a non-empty ready export costs 0.5 credits. Repeat GETs of the same immutable export are free. Exports expire after seven days.
Authentication and idempotency
Send X-API-Key: sk_live_... or Authorization: Bearer sk_live_... .
Every POST requires Idempotency-Key , containing 8 to 128 permitted characters. Retry the identical POST with the same key to receive the same export ID. Use a new key for a different request. Unknown JSON fields are rejected.
JSON fields
| Field | Required | Description |
|---|---|---|
workspace_id |
Yes | Public workspace ID in ws_... format. |
domain |
Yes | Tracked website hostname. |
keyword |
Yes | Exact tracked keyword text; matching is case-insensitive. |
search_engine |
Yes | google , bing or yahoo . |
market_id |
Yes | Numeric market ID from API Access or the free GET /api/v2/markets endpoint. |
callback_url |
No | Public HTTPS URL notified when the export becomes ready. |
Find the market ID
curl "https://app.wheremore.com/api/v2/markets?workspace_id=ws_your_workspace_id&domain=example.com" \ -H "X-API-Key: sk_live_your_key_here"
Create the export
curl -X POST "https://app.wheremore.com/api/v2/exports/serp" \
-H "X-API-Key: sk_live_your_key_here" \
-H "Idempotency-Key: serp-export-2026-08-25-001" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "ws_your_workspace_id",
"domain": "example.com",
"keyword": "organic coffee beans",
"search_engine": "google",
"market_id": 12
}'
HTTP 201 means the snapshot is ready. HTTP 202 means it is pending. Both are free and return an export.id and status_url .
Retrieve the export
curl "https://app.wheremore.com/api/v2/exports/exp_0123456789abcdef0123456789abcdef" \ -H "X-API-Key: sk_live_your_key_here"
A pending GET returns HTTP 202 and is free. A ready GET returns HTTP 200 with the saved data . The first paid retrieval reports billing_status: "deducted" ; later GETs report already_deducted , use zero credits and return identical data.
If callback_url was supplied, its export.ready message contains only job metadata. Always use authenticated GET for the export data.
Response
{
"success": true,
"export": {
"id": "exp_0123456789abcdef0123456789abcdef",
"type": "serp",
"status": "ready",
"created_at": "2026-08-25T18:00:00Z",
"ready_at": "2026-08-25T18:00:00Z",
"expires_at": "2026-09-01T18:00:00Z",
"status_url": "/api/v2/exports/exp_0123456789abcdef0123456789abcdef"
},
"credits_used": 0.5,
"balance": 999.5,
"billing_status": "deducted",
"data": {
"workspace_id": "ws_your_workspace_id",
"domain": "example.com",
"domain_id": 30,
"queue_id": 101,
"market_id": 12,
"keyword": "organic coffee beans",
"search_engine": "google",
"market_name": "UK",
"date": "2026-08-25",
"your_position": 8,
"your_url": "https://example.com/coffee",
"serp_results": [
{
"position": 1,
"url": "https://another.example/result",
"title": "Example result"
}
],
"total_results": 20
},
"meta": {
"request_id": "req_example",
"api_version": "v2",
"data_contract": "2026-08-25",
"duration_ms": 18.42
}
}
The fields inside each serp_results item reflect the stored SERP record. Consumers should tolerate additional fields being introduced under future data-contract versions.
Interpreting the data
dateis the date of the returned stored SERP.your_positionis the selected website’s matching position and can benullwhen it was not found in the stored depth.your_urlis the matching ranking URL and can also benull.queue_idis Wheremore’s internal website/market monitoring configuration.market_idis the public market identifier supplied in the request.market_nameis its corresponding display name.serp_resultscontains the stored result set for that keyword, engine and market.total_resultsis the number of result rows returned in this export, not the search engine’s estimated number of pages on the web.
Important limitations
- The API returns Wheremore’s stored monitoring data, not a new unrestricted search.
- The keyword must belong to the selected website and canonical market configuration.
- Market scope matters. The same keyword in another country, language, device or search group is a different monitored context.
- Results reflect the most recent successfully stored data available for the request.
- Workspace access is checked again whenever the saved export is retrieved.
- Search-engine layouts and feature data can vary by engine and monitoring configuration.
- A missing position is not proof that the page is absent from the whole search engine; it means it was not present in the stored result depth.
Errors
| Status | Typical cause |
|---|---|
| 400 | A parameter is missing or search_engine is unsupported. |
| 401 | The key is invalid, expired or revoked. |
| 402 | The shared account has insufficient credits for retrieval. |
| 403 | The key cannot access the supplied workspace. |
| 404 | The workspace, website, market, keyword or stored result was not found. |
| 409 | The request conflicts with a concurrent retrieval. |
| 413 | The saved export exceeds the maximum payload size; narrow the request. |
| 429 | The key exceeded 60 requests per minute. |
Error responses include a request_id . Store it with integration logs.
Troubleshooting
The keyword is not found
- Confirm it is already tracked in Wheremore.
- Match the website, search engine and market used by that monitoring configuration.
- Use the public workspace ID containing that website.
- Check spelling; text matching is case-insensitive but otherwise uses the tracked keyword.
The market is not found
- Call
GET /api/v2/marketsfor the workspace and website. - Use the returned numeric
market_id. - Do not use the internal queue ID or market name in its place.
- Confirm the market is still enabled.
Data has not updated
- Confirm the market’s search processing is enabled.
- Allow the scheduled rank-processing cycle to finish.
- Poll the saved export ID until it is ready, or create a new export for a later processing cycle.
The callback is not delivered
- Use a public HTTPS URL on port 443.
- Accept JSON
POSTrequests and respond with2xx. - Check callback logs. Wheremore retries failed deliveries.
A repeat retrieval
Repeat GET requests are expected to return HTTP 200 with the same saved data, zero credits used, billing_status: "already_deducted" and balance: null .
Security
- Keep API keys server-side.
- Store them in a secret manager or protected environment variable.
- Never put a key in browser code or a URL.
- Revoke an exposed key immediately.
- Validate the
eventand expected workspace in callback payloads before starting retrieval. - Make callback processing idempotent because readiness notifications can be retried.