Conversions API
Export conversion attribution data with full customer journey tracking. Get first-touch, last-touch, and multi-touch attribution for every conversion.
Endpoint
GET https://app.serp360.ai/api/v1/conversions
Authentication
Pass your API key in the X-API-Key header:
curl -X GET "https://app.serp360.ai/api/v1/conversions?..." \ -H "X-API-Key: sk_live_your_key_here"
How it works
The Conversions endpoint uses an immediate pingback flow:
- Queue – Call with a
pingback_url. We send the pingback immediately and return statusready. - Retrieve – Call again without
pingback_urlto fetch the data. Credits are charged here.
Unlike the SERP and Visibility endpoints, conversion data is always ready—there's no ETL delay. We ping you instantly to confirm availability.
Parameters
| Parameter | Required | Description |
|---|---|---|
domain |
Yes | Your domain name (e.g. example.com ) |
path |
Yes | Conversion path name (case insensitive) |
include_journey |
No | Set to true to include full touchpoint sequence |
pingback_url |
Step 1 only | HTTPS URL where we'll POST immediately |
Step 1: Queue the request
curl -X GET "https://app.serp360.ai/api/v1/conversions?domain=example.com&path=Purchase%20Complete&include_journey=true&pingback_url=https://your-server.com/webhook" \ -H "X-API-Key: sk_live_your_key_here"
Response
{
"success": true,
"status": "ready",
"message": "Data is ready. Pingback sent.",
"request_id": "cpb_123",
"credits_used": 0,
"meta": {
"request_id": "cpb_123",
"duration_ms": 125.34
}
}
Note the status is ready rather than queued —the pingback has already been sent.
Pingback notification
We POST to your pingback_url immediately:
{
"event": "conversions_data_ready",
"domain": "example.com",
"path": "Purchase Complete",
"date": "2025-11-29",
"request_id": "cpb_123",
"message": "Your conversion data is ready. Call the API without pingback_url to retrieve."
}
Your endpoint should return a 2xx status. We'll retry failed deliveries up to 3 times.
Step 2: Retrieve the data
Call the same endpoint without pingback_url :
curl -X GET "https://app.serp360.ai/api/v1/conversions?domain=example.com&path=Purchase%20Complete&include_journey=true" \ -H "X-API-Key: sk_live_your_key_here"
Response
{
"success": true,
"data": {
"domain": "example.com",
"path": "Purchase Complete",
"currency": "GBP",
"date": "2025-11-29",
"sessions": [
{
"session_id": "abc123def456",
"conversion_timestamp": "2025-11-29T14:32:15Z",
"revenue": 149.99,
"is_new_customer": true,
"conversion_device": "desktop",
"sessions_to_conversion": 3,
"days_to_conversion": 12,
"touchpoint_count": 3,
"first_touch_timestamp": "2025-11-17T09:15:00Z",
"first_touch": {
"channel": "paid_search",
"source": "google",
"medium": "cpc",
"campaign": "black_friday",
"term": "winter boots sale",
"content": null,
"referrer_url": "https://www.google.com/",
"landing_page_url": "example.com/boots"
},
"last_touch": {
"channel": "email",
"source": "newsletter",
"medium": "email",
"campaign": "cart_reminder",
"term": null,
"content": null,
"referrer_url": null,
"landing_page_url": "example.com/checkout"
},
"journey": [
{
"position": 1,
"channel": "paid_search",
"source": "google",
"medium": "cpc",
"campaign": "black_friday",
"term": "winter boots sale",
"content": null,
"referrer_url": "https://www.google.com/",
"landing_page_url": "example.com/boots",
"device": "mobile",
"timestamp": "2025-11-17T09:15:00Z"
},
{
"position": 2,
"channel": "organic_search",
"source": "google",
"medium": "organic",
"campaign": null,
"term": null,
"content": null,
"referrer_url": "https://www.google.com/",
"landing_page_url": "example.com/boots/review",
"device": "desktop",
"timestamp": "2025-11-22T19:45:00Z"
},
{
"position": 3,
"channel": "email",
"source": "newsletter",
"medium": "email",
"campaign": "cart_reminder",
"term": null,
"content": null,
"referrer_url": null,
"landing_page_url": "example.com/checkout",
"device": "desktop",
"timestamp": "2025-11-29T14:30:00Z"
}
]
}
],
"total_sessions": 1
},
"credits_used": 0.5,
"balance": 63996.5,
"meta": {
"request_id": "cpb_124",
"duration_ms": 89.23
}
}
Response fields
Top level
| Field | Type | Description |
|---|---|---|
domain |
string | The domain name |
path |
string | Conversion path name |
currency |
string | Currency code (e.g. GBP, USD, EUR) |
date |
string | Date of conversions returned (YYYY-MM-DD) |
sessions |
array | Array of conversion sessions |
total_sessions |
integer | Number of sessions returned |
Session object
| Field | Type | Description |
|---|---|---|
session_id |
string | Unique session identifier |
conversion_timestamp |
string | ISO 8601 timestamp of conversion |
revenue |
number | Conversion value in the specified currency |
is_new_customer |
boolean | First conversion for this visitor |
conversion_device |
string | Device type at conversion (desktop, mobile, tablet) |
sessions_to_conversion |
integer | Number of sessions before converting |
days_to_conversion |
integer | Days from first touch to conversion |
touchpoint_count |
integer | Total touchpoints in the journey |
first_touch_timestamp |
string | ISO 8601 timestamp of first interaction |
first_touch |
object | First-touch attribution data |
last_touch |
object | Last-touch attribution data |
journey |
array | Full touchpoint sequence (if include_journey=true ) |
Attribution object (first_touch / last_touch)
| Field | Type | Description |
|---|---|---|
channel |
string | Classified channel (see channel list below) |
source |
string | UTM source or inferred source |
medium |
string | UTM medium or inferred medium |
campaign |
string|null | UTM campaign |
term |
string|null | UTM term (search keywords) |
content |
string|null | UTM content |
referrer_url |
string|null | Full referrer URL |
landing_page_url |
string|null | Landing page path |
Journey touchpoint object
| Field | Type | Description |
|---|---|---|
position |
integer | Position in journey (1 = first) |
channel |
string | Classified channel |
source |
string | UTM source or inferred source |
medium |
string | UTM medium or inferred medium |
campaign |
string|null | UTM campaign |
term |
string|null | UTM term |
content |
string|null | UTM content |
referrer_url |
string|null | Full referrer URL |
landing_page_url |
string | Landing page path |
device |
string | Device type for this touchpoint |
timestamp |
string | ISO 8601 timestamp |
Channel classification
Touchpoints are classified into these channels:
| Channel | Description |
|---|---|
organic_search |
Unpaid search engine traffic |
paid_search |
PPC / Google Ads / Bing Ads |
organic_social |
Unpaid social media |
paid_social |
Paid social campaigns |
email |
Email marketing |
referral |
Links from other websites |
direct |
Direct traffic / bookmarks |
display |
Display advertising |
affiliate |
Affiliate traffic |
video |
YouTube / video platforms |
sms |
SMS campaigns |
qr_code |
QR code scans |
other |
Unclassified traffic |
Data notes
- Latest date only: Returns conversions from the most recent date with data, not historical data.
- Cookie-based tracking required: Journey data requires cookie-based visitor tracking. Cookieless sessions return an empty
journeyarray. - Omit journey to save bandwidth: If you only need first/last touch attribution, omit
include_journeyor set it tofalse.
Error responses
Domain not found
{
"success": false,
"error": "Domain not found or not authorised.",
"code": "DOMAIN_NOT_FOUND",
"meta": {
"request_id": "cpb_125",
"duration_ms": 12.34
}
}
Path not found
{
"success": false,
"error": "Conversion path not found.",
"code": "PATH_NOT_FOUND",
"meta": {
"request_id": "cpb_126",
"duration_ms": 10.56
}
}
No conversions
{
"success": false,
"error": "No conversion data available for this path.",
"code": "NO_DATA",
"meta": {
"request_id": "cpb_127",
"duration_ms": 15.67
}
}
Pricing
0.5 credits per successful retrieval.
Credits are charged when you retrieve data in step 2, regardless of how many sessions are returned.
Example: Python integration
import requests
API_KEY = "sk_live_your_key_here"
BASE_URL = "https://app.serp360.ai/api/v1/conversions"
def queue_conversions_request(domain, path, pingback_url, include_journey=False):
"""Queue a conversions data request."""
params = {
"domain": domain,
"path": path,
"pingback_url": pingback_url,
"include_journey": str(include_journey).lower()
}
response = requests.get(
BASE_URL,
headers={"X-API-Key": API_KEY},
params=params
)
return response.json()
def retrieve_conversions(domain, path, include_journey=False):
"""Retrieve conversion data after pingback received."""
params = {
"domain": domain,
"path": path,
"include_journey": str(include_journey).lower()
}
response = requests.get(
BASE_URL,
headers={"X-API-Key": API_KEY},
params=params
)
return response.json()
# Queue the request
result = queue_conversions_request(
domain="example.com",
path="Purchase Complete",
pingback_url="https://your-server.com/webhook",
include_journey=True
)
print(f"Status: {result['status']}") # "ready"
# Retrieve immediately (or after pingback)
data = retrieve_conversions(
domain="example.com",
path="Purchase Complete",
include_journey=True
)
# Process conversions
for session in data['data']['sessions']:
print(f"\nConversion: {session['conversion_timestamp']}")
print(f"Revenue: {data['data']['currency']} {session['revenue']}")
print(f"First touch: {session['first_touch']['channel']}")
print(f"Last touch: {session['last_touch']['channel']}")
print(f"Journey length: {session['touchpoint_count']} touchpoints over {session['days_to_conversion']} days")
Example: Channel attribution analysis
def analyse_channel_attribution(data):
"""Analyse first-touch vs last-touch channel performance."""
first_touch_revenue = {}
last_touch_revenue = {}
currency = data['data']['currency']
for session in data['data']['sessions']:
revenue = session['revenue']
# First touch attribution
ft_channel = session['first_touch']['channel']
first_touch_revenue[ft_channel] = first_touch_revenue.get(ft_channel, 0) + revenue
# Last touch attribution
lt_channel = session['last_touch']['channel']
last_touch_revenue[lt_channel] = last_touch_revenue.get(lt_channel, 0) + revenue
return {
"currency": currency,
"first_touch": first_touch_revenue,
"last_touch": last_touch_revenue
}
# Usage
data = retrieve_conversions(domain="example.com", path="Purchase Complete")
attribution = analyse_channel_attribution(data)
print(f"First-touch attribution ({attribution['currency']}):")
for channel, revenue in sorted(attribution['first_touch'].items(), key=lambda x: -x[1]):
print(f" {channel}: {revenue:.2f}")
print(f"\nLast-touch attribution ({attribution['currency']}):")
for channel, revenue in sorted(attribution['last_touch'].items(), key=lambda x: -x[1]):
print(f" {channel}: {revenue:.2f}")
Example: Journey path analysis
def analyse_journey_paths(data, min_occurrences=2):
"""Find common conversion paths."""
from collections import Counter
paths = []
for session in data['data']['sessions']:
if session.get('journey'):
# Create path string from channels
path = " → ".join([tp['channel'] for tp in session['journey']])
paths.append(path)
# Count occurrences
path_counts = Counter(paths)
# Filter by minimum occurrences
common_paths = {
path: count
for path, count in path_counts.items()
if count >= min_occurrences
}
return common_paths
# Usage
data = retrieve_conversions(
domain="example.com",
path="Purchase Complete",
include_journey=True
)
paths = analyse_journey_paths(data, min_occurrences=2)
print("Common conversion paths:")
for path, count in sorted(paths.items(), key=lambda x: -x[1]):
print(f" {path}: {count} conversions")
Example: Webhook handler (Node.js/Express)
const express = require('express');
const app = express();
app.use(express.json());
app.post('/webhook', (req, res) => {
const { event, domain, path, date } = req.body;
if (event === 'conversions_data_ready') {
console.log(`Conversion data ready for ${domain} / ${path}`);
console.log(`Date: ${date}`);
// Trigger your data retrieval process
// Note: For conversions, this fires immediately after queuing
}
res.status(200).send('OK');
});
app.listen(3000);
Tips
- Use include_journey selectively – Full journey data increases response size. Omit it if you only need first/last touch.
- Daily exports – Call the API once per day to get yesterday's conversions. The endpoint returns the latest date's data.
- Cross-device journeys – The
devicefield in journey touchpoints shows when users switch devices during their path to conversion. - New vs returning – Use
is_new_customerto segment first-time buyers from repeat purchasers. - Calculate time to convert –
days_to_conversionandsessions_to_conversionhelp you understand your sales cycle length.