SERP Data API

Export your SERP ranking data programmatically. Pull positions, competitor URLs, and full result pages into your reporting tools or data warehouse.

Endpoint

GET https://app.wheremore.com/api/v1/serp

Authentication

Pass your API key in the X-API-Key  header:

curl -X GET "https://app.wheremore.com/api/v1/serp?workspace_id=ws_xxx&..." \
  -H "X-API-Key: sk_live_your_key_here"

How it works

The SERP endpoint uses a two-step flow with intelligent data checking:

  1. Register – Call with a pingback_url . We check if today's data exists:
    • Data exists → We POST to your pingback URL immediately and return status: "ready"
    • No data yet → We return status: "queued"  and POST when the next ETL run completes
  2. Retrieve – Call again without pingback_url  to fetch the data. Credits are charged here.

This flow ensures you're notified as soon as data is available — instantly if it already exists, or after collection completes.

Parameters

Parameter Required Description
workspace_id Yes Workspace ID (e.g. ws_d04ae5a1f833 )
keyword Yes The keyword text (case insensitive)
search_engine Yes google , bing , or yahoo
search_group Yes Your search group name (case insensitive)
pingback_url Step 1 only HTTPS URL where we'll POST when data is ready

Step 1: Register the request

curl -X GET "https://app.wheremore.com/api/v1/serp?workspace_id=ws_d04ae5a1f833&keyword=organic%20coffee%20beans&search_engine=google&search_group=my-client&pingback_url=https://your-server.com/webhook" \
  -H "X-API-Key: sk_live_your_key_here"

Response (Data exists)

If today's SERP data already exists, the pingback is sent immediately:

{
  "success": true,
  "status": "ready",
  "message": "Data is ready. We've notified your pingback URL.",
  "workspace_id": "ws_d04ae5a1f833",
  "request_id": "a1b2c3d4",
  "credits_used": 0,
  "meta": {
    "request_id": "a1b2c3d4",
    "duration_ms": 45.12
  }
}

Response (No data yet)

If data hasn't been collected yet today, the request is queued:

{
  "success": true,
  "status": "queued",
  "message": "No data available yet for today. We'll notify you when ready.",
  "workspace_id": "ws_d04ae5a1f833",
  "request_id": "a1b2c3d4",
  "credits_used": 0,
  "meta": {
    "request_id": "a1b2c3d4",
    "duration_ms": 45.12
  }
}

Pingback notification

When your data is ready (immediately if it exists, or after ETL completes), we POST to your pingback_url :

{
  "event": "serp_data_ready",
  "workspace_id": "ws_d04ae5a1f833",
  "keyword": "organic coffee beans",
  "search_engine": "google",
  "search_group": "my-client",
  "date": "2025-11-28",
  "request_id": "spb_123",
  "message": "Your SERP data is ready. Call the API with workspace_id=ws_d04ae5a1f833 to retrieve.",
  "timestamp": "2025-11-28T10:15:30+00:00"
}

Your endpoint should return a 2xx  status. We'll retry failed deliveries up to 3 times.

Step 2: Retrieve the data

Once you receive the pingback, call the same endpoint without pingback_url :

curl -X GET "https://app.wheremore.com/api/v1/serp?workspace_id=ws_d04ae5a1f833&keyword=organic%20coffee%20beans&search_engine=google&search_group=my-client" \
  -H "X-API-Key: sk_live_your_key_here"

Response

{
  "success": true,
  "data": {
    "workspace_id": "ws_d04ae5a1f833",
    "keyword": "organic coffee beans",
    "search_engine": "google",
    "search_group": "my-client",
    "date": "2025-11-29",
    "your_position": 4,
    "your_url": "https://example.com/coffee-beans",
    "serp_results": [
      {
        "position": 1,
        "url": "https://coffeeroasters.com/organic/",
        "title": "Premium Organic Coffee Beans | Fresh Roasted Daily",
        "description": "Discover our range of certified organic coffee beans...",
        "domain": "coffeeroasters.com"
      },
      {
        "position": 2,
        "url": "https://beanbox.com/organic-coffee",
        "title": "Organic Coffee Subscription | Bean Box",
        "description": "Get freshly roasted organic coffee delivered...",
        "domain": "beanbox.com"
      }
    ],
    "total_results": 100
  },
  "request_id": "b2c3d4e5",
  "credits_used": 0.5,
  "balance": 14999.5,
  "meta": {
    "request_id": "b2c3d4e5",
    "duration_ms": 120.34
  }
}

Response fields

Field Type Description
workspace_id string Workspace ID (ws_xxx format)
keyword string The keyword you queried
search_engine string The search engine (google, bing, yahoo)
search_group string Your search group name
date string Date of the SERP data (YYYY-MM-DD)
your_position integer|null Your tracked domain's position, or null if not ranking
your_url string|null Your ranking URL, or null if not ranking
serp_results array Up to 100 SERP results
serp_results[].position integer Position in SERP (1-100)
serp_results[].url string Full URL of the result
serp_results[].title string Page title
serp_results[].description string Meta description snippet
serp_results[].domain string Domain name
total_results integer Number of results returned

Error responses

Keyword not found

{
  "success": false,
  "error": {
    "code": 404,
    "message": "Keyword not found for this search group/engine combination in this workspace"
  },
  "meta": {
    "request_id": "c3d4e5f6"
  }
}

No data ready

{
  "success": false,
  "error": {
    "code": 400,
    "message": "No data ready. First request with pingback_url, then retrieve after notification."
  },
  "meta": {
    "request_id": "d4e5f6g7"
  }
}

SERP data not available

{
  "success": false,
  "error": {
    "code": 404,
    "message": "SERP data not yet available for today"
  },
  "meta": {
    "request_id": "e5f6g7h8"
  }
}

Workspace access denied

{
  "success": false,
  "error": {
    "code": 403,
    "message": "Access denied to workspace"
  },
  "meta": {
    "request_id": "f6g7h8i9"
  }
}

Pricing

0.5 credits per successful retrieval.

Credits are only charged when you retrieve data in step 2. Registering requests, receiving pingbacks, and error responses are free.

Example: Python integration

import requests

API_KEY = "sk_live_your_key_here"
WORKSPACE_ID = "ws_d04ae5a1f833"
BASE_URL = "https://app.wheremore.com/api/v1/serp"

def register_serp_request(keyword, search_engine, search_group, pingback_url):
    """Register a SERP data request and get notified when ready."""
    response = requests.get(
        BASE_URL,
        headers={"X-API-Key": API_KEY},
        params={
            "workspace_id": WORKSPACE_ID,
            "keyword": keyword,
            "search_engine": search_engine,
            "search_group": search_group,
            "pingback_url": pingback_url
        }
    )
    return response.json()

def retrieve_serp_data(keyword, search_engine, search_group):
    """Retrieve SERP data after pingback received."""
    response = requests.get(
        BASE_URL,
        headers={"X-API-Key": API_KEY},
        params={
            "workspace_id": WORKSPACE_ID,
            "keyword": keyword,
            "search_engine": search_engine,
            "search_group": search_group
        }
    )
    return response.json()

# Register the request
result = register_serp_request(
    keyword="organic coffee beans",
    search_engine="google",
    search_group="my-client",
    pingback_url="https://your-server.com/webhook"
)

# Check if data was ready immediately
if result['status'] == 'ready':
    print("Data exists - pingback sent immediately!")
else:
    print(f"Queued: {result['request_id']} - will notify when ready")

# After receiving pingback, retrieve data
data = retrieve_serp_data(
    keyword="organic coffee beans",
    search_engine="google",
    search_group="my-client"
)
print(f"Position: {data['data']['your_position']}")

Example: Webhook handler (Node.js/Express)

const express = require('express');
const app = express();

app.use(express.json());

app.post('/webhook', (req, res) => {
  const { event, workspace_id, keyword, search_engine, search_group, date } = req.body;
  
  if (event === 'serp_data_ready') {
    console.log(`SERP data ready for "${keyword}" on ${search_engine}`);
    console.log(`Workspace: ${workspace_id}`);
    console.log(`Date: ${date}`);
    
    // Trigger your data retrieval process
    // e.g., add to a job queue, call the API, update your database
  }
  
  res.status(200).send('OK');
});

app.listen(3000);

Tips

  • Match parameters exactly – The keyword, search engine, and search group must match what's configured in Wheremore.
  • Use HTTPS for pingbacks – We only POST to secure endpoints.
  • Handle both statuses – Check for "ready"  (instant) or "queued"  (wait for pingback) in the response.
  • Handle retries – Your webhook might receive duplicate notifications. Use the request_id  to deduplicate.
  • Check your balance – The balance  field in responses shows your remaining credits.

Wheremore | click to conversion. Developed by Tigerlily Group. Wheremore connects search performance, content engagement, user behaviour, and conversion data to show exactly where you lose prospects — and how to win them back.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.