Managing your API keys
Use the Wheremore API to export published Search Performance, AI Visibility and Conversion Performance reports, or accessible Action Board tasks, into your own application. Open API Access to manage keys and find your public workspace ID.
This developer guide describes API v2, with default data contract 2026-09-19 . The data contract identifies the response format. GET https://app.wheremore.com/api/v2 returns the server's default contract and advertised endpoints without requiring authentication. Check that contract before adopting these examples.
The base URL is https://app.wheremore.com/api/v2 . Export requests read stored information; they do not trigger searches, AI responses, analysis or conversion processing. They do not create or edit Action Board tasks.
Create and protect an API key
- Sign in and open API Access.
- In API Keys, select Generate new key.
- Enter Key name, then choose Expiry: No expiry, 30 days, 90 days or 1 year.
- Select Generate key and copy the complete secret immediately. It is shown once; the list subsequently shows identifying metadata rather than the complete secret.
- Store it in your server's secret manager or protected environment. Use separate named keys for independent integrations.
The default limit is five active, unexpired keys per user. Select Revoke on an active key to stop requests using it. Revocation cannot recover a lost secret; generate a replacement instead. Key access follows the owning user's resource permissions. Do not expose keys in browser code, URLs or logs.
Under Your Workspaces and Markets, copy the public ws_... workspace ID. This is different from a numeric internal workspace ID. Send it as workspace_id when creating an export or discovering markets. Export retrieval needs only the export ID and authentication.
Authentication
Send either header:
X-API-Key: sk_live_YOUR_COMPLETE_SECRET
Authorization: Bearer sk_live_YOUR_COMPLETE_SECRET
If both are supplied, X-API-Key takes precedence. Examples below assume WHEREMORE_API_KEY is already set securely. Replace every sample workspace and resource ID with your own.
Choose an endpoint
| Method and path, relative to the base URL | Purpose |
|---|---|
GET /markets?workspace_id=...&domain=... |
Discover search queues and enabled engine identifiers for a website. No credit charge. |
POST /exports/serp |
Save one keyword/queue/engine report for a date range. |
POST /exports/visibility |
Save project metrics and one page of monitored answers. |
POST /exports/conversions |
Save a website report, optionally narrowed to a conversion path. |
POST /exports/action-board |
Save one page of active tasks. |
GET /exports/{export_id} |
Poll or retrieve a saved export. |
Use the Search Performance API, AI Visibility API, Conversions API and Action Board API guides for request fields and data definitions. There are no public keyword, persona, project or conversion-path listing endpoints in this export API. Obtain those IDs from the corresponding Wheremore configuration or your workspace administrator; do not substitute display names unless the endpoint explicitly accepts them.
Create and retrieve an export
- Send a JSON POST to the chosen export endpoint. Include
Content-Type: application/json, authentication andIdempotency-Key. - Save the returned
export.id,export.status_urlandexport.expires_at. POST returns metadata, not exported data, and does not charge credits. HTTP 201 means ready; HTTP 202 means pending. - GET the returned export ID. A pending export returns HTTP 202 without a charge. Use a modest polling interval and back off on transient errors.
- A successful ready GET returns HTTP 200 with
data. The first retrieval of non-empty data charges once. Subsequent GETs return the same saved data without another charge.
curl --fail-with-body \
"https://app.wheremore.com/api/v2/exports/exp_0123456789abcdef0123456789abcdef" \
-H "X-API-Key: ${WHEREMORE_API_KEY}"
A saved export is immutable: later changes in Wheremore do not refresh its data. Create a new export with a new idempotency key for a later snapshot. Exports expire seven days after creation, including time spent pending. Retrieval after expiry returns HTTP 410 when access checks succeed. No endpoint extends retention or refreshes a saved export.
Retry POST safely
Idempotency-Key is an HTTP header, not a JSON field. It must contain 8–128 characters drawn from letters, digits, . , _ , : , / and - .
Reuse the same key, API credential and identical request when retrying a POST after a network failure. Idempotency is scoped to the API key. Changing the export type or request while reusing that key returns HTTP 409 idempotency_conflict . Retrying an existing pending job returns 202; an existing non-pending job can return 200, so inspect export.status .
Use a new key for each new report or page. Do not rotate API credentials in the middle of a POST retry and assume the original idempotency key will prevent another export. Expired export records do not reserve an idempotency key forever.
Dates and contracts
Search, Visibility and Conversions require date_from and date_to in YYYY-MM-DD format. Dates are inclusive UTC dates. The period must cover 1–366 days and cannot end in the future. Action Board uses task filters instead of reporting dates.
Omit data_contract for the default 2026-09-19 , or specify it explicitly. Search, Visibility and Conversions also accept 2026-09-10 ; Search then omits the newer page-conflict, data-quality and evidence-limit fields. Newly created Action Board exports accept only 2026-09-19 .
Saved ready exports keep their original contract and data. Check export.data_contract on retrieval. Unknown request fields are rejected. Treat omitted fields and explicit values as different requests for idempotency purposes.
Credits and billing states
| Export | Charge on first successful non-empty GET |
|---|---|
| Search Performance | 0.5 credits per report. Ranking history can make a report billable even without a SERP observation. |
| AI Visibility | 0.5 credits per distinct conversation/persona pair on the returned answer page. |
| Conversions | 0.5 credits per report with starting sessions in either returned period, even if conversions are zero. |
| Action Board | 0.5 credits per non-empty task page, containing up to 100 tasks. |
POSTs, pending polls, market discovery, readiness callbacks and empty exports do not consume export credits. Charges use the workspace account's shared balance.
billing_status |
Meaning |
|---|---|
not_charged |
No retrieval charge has completed. Common in creation/pending responses. |
deducted |
This retrieval completed the charge. |
already_deducted |
The saved export was already charged; this response uses zero additional credits. |
not_charged_empty_export |
The export has zero billable units. |
credits_used describes this response's charge. balance can contain the post-charge balance on first paid retrieval. Repeated paid retrievals return balance: null ; do not treat a historical balance as the current account balance. If credits are insufficient, resolve the balance issue and retry the same export GET before expiry.
Response envelope
This illustrative ready response uses an intentionally abbreviated data object. Each endpoint guide describes its contents.
{
"success": true,
"export": {
"id": "exp_0123456789abcdef0123456789abcdef",
"type": "serp",
"data_contract": "2026-09-19",
"status": "ready",
"created_at": "2026-09-19T10:00:00Z",
"ready_at": "2026-09-19T10:00:00Z",
"expires_at": "2026-09-26T10:00:00Z",
"status_url": "/api/v2/exports/exp_0123456789abcdef0123456789abcdef"
},
"credits_used": 0.5,
"balance": 99.5,
"billing_status": "deducted",
"data": {},
"meta": {
"request_id": "0123456789abcdef",
"api_version": "v2",
"data_contract": "2026-09-19",
"duration_ms": 42
}
}
An export can be ready while its underlying report says it is updating: readiness means a usable snapshot was saved, not that all source processing has finished. Keep source publication/coverage fields when displaying data. Nested metric structures come from the published reporting readers; honor their null values, units and availability flags, and tolerate additional response properties.
Optional readiness callbacks
Supply callback_url in the creation JSON to receive a JSON POST when the export is ready. It must be an absolute public HTTPS URL, at most 500 characters, using port 443 with no embedded credentials or fragment. Private/local addresses and redirects are not supported.
{
"event": "export.ready",
"export": {
"id": "exp_0123456789abcdef0123456789abcdef",
"type": "serp",
"status": "ready",
"status_url": "/api/v2/exports/exp_0123456789abcdef0123456789abcdef",
"ready_at": "2026-09-19T10:00:00Z",
"expires_at": "2026-09-26T10:00:00Z"
}
}
Callbacks contain metadata only: no exported data, workspace field or payment confirmation. They have no API-provided signature header. Treat them as a notification to check an export your application already created, then retrieve it using your stored ID, the known API host and authenticated GET. Do not trust a callback-supplied URL for arbitrary outbound requests.
Return 2xx promptly; delivery has a ten-second timeout and up to three attempts. Handle duplicates safely. Scheduling delays and delivery failures are possible, so retain polling as a fallback. There is no separate failure-notification event.
Access checks and limits
Retrieval is restricted to the user who created the export; a replacement key for that same user can retrieve it while authorized. Every GET rechecks workspace access. Visibility also checks the project and any selected persona; Conversions checks the domain; Search checks the current market and saved domain; Action Board checks projects and tags represented in the payload. An earlier successful charge does not override later access revocation.
The default rate limit is 60 requests per minute per key; a configured key limit can differ. Authenticated discovery and polling requests count too. On HTTP 429, wait for Retry-After seconds before retrying.
Saved payloads are limited to 8 MiB. On HTTP 413, reduce the answer/task page size or narrow the applicable filters and create a new export. A reporting reader can impose its own capacity limit before the saved-payload limit is reached.
Errors and recovery
Errors contain success: false , an error object with numeric code , string type and message , plus meta.request_id , api_version and data_contract . Log the HTTP status and request ID; do not log the secret key.
| HTTP status | What to do |
|---|---|
| 400 | Correct JSON, required fields, filter values, dates or unsupported contract. |
| 401 | Check the complete key and header; replace expired or revoked keys. |
| 402 | Resolve insufficient credits, then retry the same GET. |
| 403 | Check resource permissions. export_access_revoked means saved-export access no longer passes. |
| 404 | Check the route, export ID and resource/workspace identifiers. |
| 405 | Use the method shown for the endpoint. |
| 409 | Resolve idempotency_conflict ; retry export_retrieval_in_progress shortly. For export_scope_unverifiable , create a new export because the old record lacks verifiable resource scope. |
| 410 | The saved export expired. Create a new one. |
| 413 | Narrow the export and use a new idempotency key. |
| 422 | The job failed to prepare. Check source processing; retain the request ID for support. |
| 429 | Respect Retry-After . |
| 500 or 503 | Back off and retry transient failures. Persistent errors can indicate missing reporting services or capacity limits; narrow scope where possible and contact support with the request ID. |
A ready export does not change when polled repeatedly. A pending export cannot make progress until its source reporting is available; creation itself does not schedule new monitoring. When seeking support, provide the endpoint, export ID, workspace ID, UTC time, HTTP status and request ID, never the API secret.