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:
- 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
- Data exists → We POST to your pingback URL immediately and return
- Retrieve – Call again without
pingback_urlto 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_idto deduplicate. - Check your balance – The
balancefield in responses shows your remaining credits.