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.serp360.ai/api/v1/serp

Authentication

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

curl -X GET "https://app.serp360.ai/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.serp360.ai/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.serp360.ai/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.serp360.ai/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 SERP360.
  • 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.
Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.