On-Domain Events API
Overview
The Events API lets you query pixel events from your website in real-time. When a visitor hits a page with your pixel installed, the platform captures the event and -- if the visitor can be identified -- enriches it with contact and company data.
flowchart LR
A[Create Pixel] --> B[Install on Site]
B --> C[Query Events]
C --> D[Filter and Deduplicate]
D --> E[Export or Integrate]
style A fill:#3b82f6,color:#fff
style B fill:#3b82f6,color:#fff
style C fill:#3b82f6,color:#fff
style D fill:#3b82f6,color:#fff
style E fill:#22c55e,color:#fff
Base URL: https://apiv3.delivr.ai
Prerequisites:
- JWT token and organization ID (Authentication)
- A project ID (Account Setup)
Authentication: Bearer token in the Authorization header, plus your organization ID.
Authorization: Bearer YOUR_API_TOKEN
Content-Type: application/json
x-organization-id: YOUR_ORGANIZATION_ID
The
x-organization-idheader is required for enriched (resolved) event queries. Without it, you still get raw events but without identity enrichment.
Step 1: Create a Pixel
Pixels track visitor activity on your website. Create one on your project:
curl -X POST https://api.delivr.ai/public/core/api/pixel/create \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-H "X-Delivr-Client-ID: YOUR_CLIENT_ID" \
-H "X-Delivr-Client-Secret: YOUR_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-H "project_id: YOUR_PROJECT_ID" \
-d '{
"title": "My Website Pixel"
}'Response
{
"response": {
"pixel_id": "8a755c42-b2b1-...",
"installation_url": "https://cdn.delivr.ai/pixels/8a755c42-b2b1-.../p.js"
}
}Install the Pixel
Add this script tag to your website's <head>:
<script id="delivr-ai" src="https://cdn.delivr.ai/pixels/YOUR_PIXEL_ID/p.js" async></script>Or install via Google Tag Manager.
Find Your Pixel ID
If you already have a pixel installed, find the ID from:
- Dashboard: Settings > Pixels > Copy the pixel ID
- Your pixel script: The ID is in the URL:
https://cdn.delivr.ai/pixels/YOUR_PIXEL_ID/p.js
Step 2: Query Events
Request
GET /api/v1/events?pixel_id=YOUR_PIXEL_ID&start_ms=START_MS&end_ms=END_MS&limit=20&offset=0
Required Parameters
| Parameter | Type | Description |
|---|---|---|
pixel_id or project_id | string (UUID) | Your pixel ID (from dashboard Settings > Pixels) or your project ID. Provide one or the other. |
start_ms | integer | Start of time window in milliseconds since epoch |
end_ms | integer | End of time window in milliseconds since epoch |
You can use either
pixel_idorproject_idto query events. If a project has multiple pixels, querying byproject_idreturns events from all of them.
Optional Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 100 | Max rows to return |
offset | integer | 0 | Skip this many rows (for pagination) |
filter | string | -- | Filter expression (see Filters section) |
select | string | -- | Comma-separated field names to return |
distinct | string | -- | Deduplicate by field: hem or company_domain |
has_valuable_data | boolean | -- | Only return rows with actionable contact or company data |
orderby | string | -- | Sort field and direction: field:asc or field:desc |
Time Window Limit
Each request can span a maximum of 25 hours. For longer ranges, split into multiple requests with consecutive time windows.
Generating Timestamps
import time
# Current time
end_ms = int(time.time() * 1000)
# 24 hours ago
start_ms = end_ms - (24 * 60 * 60 * 1000)
# Specific date (2026-01-28 00:00 UTC)
import datetime
dt = datetime.datetime(2026, 1, 28, tzinfo=datetime.timezone.utc)
start_ms = int(dt.timestamp() * 1000)Example Response (200 OK)
Resolved event (visitor was identified):
{
"pixel_id": "YOUR_PIXEL_ID",
"start_ms": 1769558400000,
"end_ms": 1769644800000,
"limit": 20,
"offset": 0,
"rows": [
{
"event_id": "8911fff2-c21e-42c3-85ae-cd31ae79de96",
"event_type": "page_view",
"timestamp": "2026-01-28T01:58:12.557202Z",
"ts_millis": 1769565492557,
"pixel_id": "YOUR_PIXEL_ID",
"project_id": "YOUR_PROJECT_ID",
"cookie_id": 3429795123691521,
"resolved": true,
"first_name": "jane",
"last_name": "smith",
"job_title": "product analyst",
"department": "information technology",
"seniority_level": "staff",
"company_name": "acme corp",
"company_domain": "acmecorp.com",
"company_industry": "government administration",
"company_employee_count_range": "10000+",
"current_business_email": "[email protected]",
"current_business_email_validation_status": "valid",
"linkedin_url": "https://linkedin.com/in/jane-smith-12345678",
"email": "[email protected]",
"referrer_url": "https://www.yoursite.com/",
"event_data": "{\"url\":\"https://www.yoursite.com/pricing\",\"title\":\"Pricing Page\"}",
"client_ip": "88.98.217.66",
"user_agent": "Mozilla/5.0 ...",
"...": "~97 total fields when resolved"
}
],
"meta": {
"files_scanned": 29,
"buckets_scanned": 1,
"took_ms": 151
},
"index_name": "project_id"
}Unresolved event (visitor was not identified):
{
"rows": [
{
"event_id": "1542601a-3eca-48d1-81af-9a770714ac26",
"event_type": "page_view",
"timestamp": "2026-01-28T00:37:48.768321Z",
"ts_millis": 1769560668768,
"pixel_id": "YOUR_PIXEL_ID",
"project_id": "YOUR_PROJECT_ID",
"cookie_id": 109879807197643124,
"client_ip": "92.50.23.199",
"ip": "34.8.133.12",
"referrer_url": "https://www.yoursite.com/",
"event_data": "{\"url\":\"https://www.yoursite.com/pricing\",\"title\":\"Pricing\"}",
"headers": "{...}",
"user_agent": "Mozilla/5.0 ..."
}
]
}Unresolved events contain ~13 fields (raw event data only). Resolved events contain ~97 fields (enriched with contact, company, and demographic data).
Step 3: Filter Events
Filters use the format field:operator:value, with multiple filters separated by commas (AND logic).
Filter Operators
| Operator | Example | Description |
|---|---|---|
eq | resolved:eq:true | Exact match |
like | company_name:like:acme | Case-insensitive contains |
not_null | first_name:not_null | Field has a value |
Common Filter Examples
# Only resolved (identified) visitors
filter=resolved:eq:true
# Only page views
filter=event_type:eq:page_view
# Resolved visitors with a company name
filter=resolved:eq:true,company_name:not_null
# Search by name
filter=first_name:like:john
# Specific event type + resolved
filter=resolved:eq:true,event_type:eq:page_view
Filter in a Request
GET /api/v1/events?pixel_id=YOUR_PIXEL_ID&start_ms=START_MS&end_ms=END_MS&limit=20&filter=resolved%3Aeq%3Atrue
URL-encode the filter value.
:becomes%3A, soresolved:eq:truebecomesresolved%3Aeq%3Atrue.
Step 4: Get Event Counts
Get a count of events without fetching the full rows.
Request
GET /api/v1/event_counts?pixel_id=YOUR_PIXEL_ID&start_ms=START_MS&end_ms=END_MS
Uses the same pixel_id or project_id parameter as the events endpoint.
Optional Parameters
| Parameter | Type | Description |
|---|---|---|
filter | string | Same filter syntax as events endpoint |
distinct | string | Count distinct values: hem or company_domain |
Example Response (200 OK)
{
"count": 287,
"meta": {
"files_scanned": 29,
"buckets_scanned": 1,
"took_ms": 51
}
}Count Examples
# Total events in time window
GET /api/v1/event_counts?pixel_id=...&start_ms=...&end_ms=...
# Only resolved events
GET /api/v1/event_counts?pixel_id=...&start_ms=...&end_ms=...&filter=resolved%3Aeq%3Atrue
# Unique identified visitors (distinct HEM)
GET /api/v1/event_counts?pixel_id=...&start_ms=...&end_ms=...&distinct=hem
# Unique companies
GET /api/v1/event_counts?pixel_id=...&start_ms=...&end_ms=...&distinct=company_domain
Step 5: Deduplicate Results
Use distinct and has_valuable_data together to get one row per person or company.
One Row Per Person
GET /api/v1/events?pixel_id=...&start_ms=...&end_ms=...&distinct=hem&has_valuable_data=true&limit=50
Returns one row per unique identity (HEM). Only includes rows where:
- The visitor has a displayable email, OR
- The visitor has a name AND company
One Row Per Company
GET /api/v1/events?pixel_id=...&start_ms=...&end_ms=...&distinct=company_domain&has_valuable_data=true&limit=50
Returns one row per unique company domain. Only includes rows where the company name is present.
Field Projection
Use the select parameter to return only specific fields. This reduces response size.
Fields that are null for a given row are omitted from the response. Your code should handle missing keys gracefully.
GET /api/v1/events?pixel_id=...&start_ms=...&end_ms=...&select=event_type,referrer_url,event_data&limit=5
Example Response
{
"rows": [
{
"event_type": "page_view",
"referrer_url": "https://www.yoursite.com/",
"event_data": "{\"url\":\"https://www.yoursite.com/pricing\"}"
}
]
}Available Fields (Schema)
GET /api/v1/events_schema
No parameters required. Returns the full field list for all event data.
Example Response (200 OK)
{
"fields": [
{ "name": "event_id", "data_type": "Utf8", "nullable": false },
{ "name": "pixel_id", "data_type": "Utf8", "nullable": false },
{ "name": "event_type", "data_type": "Utf8", "nullable": false },
{ "name": "timestamp", "data_type": "Utf8", "nullable": false },
{ "name": "ts_millis", "data_type": "Int64", "nullable": false },
{ "name": "cookie_id", "data_type": "Int64", "nullable": true },
{ "name": "resolved", "data_type": "Boolean", "nullable": true },
{ "name": "first_name", "data_type": "Utf8", "nullable": true },
{ "name": "last_name", "data_type": "Utf8", "nullable": true },
{ "name": "company_name", "data_type": "Utf8", "nullable": true },
{ "name": "company_domain", "data_type": "Utf8", "nullable": true },
{ "name": "job_title", "data_type": "Utf8", "nullable": true },
{ "name": "email", "data_type": "Utf8", "nullable": true }
]
}This is a partial list. The full schema contains ~97 fields. Call the endpoint to see all fields.
Key Field Categories
| Category | Example Fields |
|---|---|
| Event | event_id, event_type, timestamp, ts_millis, cookie_id |
| Page | referrer_url, event_data (JSON with URL, title, viewport) |
| Identity | resolved, hem, hems, email, domain_lc |
| Person | first_name, last_name, job_title, department, seniority_level |
| Company | company_name, company_domain, company_industry, company_employee_count_range |
| Location | personal_city, personal_state, personal_country, company_city |
| Demographics | age_range, gender, income_range_lc, net_worth, is_homeowner |
| Contact | current_business_email, business_emails, personal_emails, phones, linkedin_url |
| Hashes | emails_sha256_lc_hem, emails_md5_lc_hem, emails_sha1_lc_hem |
| Network | client_ip, ip, user_agent, headers |
Pagination
The events API uses offset-based pagination. There is no total count in the response -- use the event_counts endpoint for that.
How to Paginate
- Request with
offset=0&limit=100 - If you get back 100 rows (equal to your limit), there may be more -- request
offset=100&limit=100 - If you get fewer rows than your limit, you've reached the end
Example
import requests
all_rows = []
offset = 0
limit = 100
while True:
resp = requests.get(
"https://apiv3.delivr.ai/api/v1/events",
params={
"pixel_id": "YOUR_PIXEL_ID",
"start_ms": start_ms,
"end_ms": end_ms,
"limit": limit,
"offset": offset,
"filter": "resolved:eq:true",
},
headers={
"Authorization": "Bearer YOUR_API_TOKEN",
"x-organization-id": "YOUR_ORGANIZATION_ID",
},
)
data = resp.json()
rows = data.get("rows", [])
all_rows.extend(rows)
if len(rows) < limit:
break # No more rows
offset += limit
print(f"Total rows fetched: {len(all_rows)}")For ranges longer than 25 hours, split into 24-hour chunks and paginate within each chunk.
Complete Example
Python: Fetch Resolved Visitors from the Last 24 Hours
import time
import requests
API_TOKEN = "YOUR_API_TOKEN"
ORG_ID = "YOUR_ORGANIZATION_ID"
PIXEL_ID = "YOUR_PIXEL_ID"
end_ms = int(time.time() * 1000)
start_ms = end_ms - (24 * 60 * 60 * 1000)
resp = requests.get(
"https://apiv3.delivr.ai/api/v1/events",
params={
"pixel_id": PIXEL_ID,
"start_ms": start_ms,
"end_ms": end_ms,
"limit": 50,
"filter": "resolved:eq:true",
"distinct": "hem",
"has_valuable_data": "true",
},
headers={
"Authorization": f"Bearer {API_TOKEN}",
"Content-Type": "application/json",
"x-organization-id": ORG_ID,
},
)
data = resp.json()
rows = data.get("rows", [])
print(f"Found {len(rows)} unique identified visitors")
for row in rows:
print(f" {row.get('first_name')} {row.get('last_name')}")
print(f" {row.get('job_title')} at {row.get('company_name')}")
print(f" Email: {row.get('current_business_email')}")
print()curl: Quick Check
# Count total events in the last 24 hours
END_MS=$(python3 -c "import time; print(int(time.time()*1000))")
START_MS=$(python3 -c "import time; print(int((time.time()-86400)*1000))")
curl "https://apiv3.delivr.ai/api/v1/event_counts?pixel_id=YOUR_PIXEL_ID&start_ms=$START_MS&end_ms=$END_MS" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-organization-id: YOUR_ORGANIZATION_ID"
# Fetch 5 resolved events
curl "https://apiv3.delivr.ai/api/v1/events?pixel_id=YOUR_PIXEL_ID&start_ms=$START_MS&end_ms=$END_MS&limit=5&filter=resolved%3Aeq%3Atrue" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "x-organization-id: YOUR_ORGANIZATION_ID"Event Types
| Event Type | Description |
|---|---|
page_view | Page was loaded |
click | Element was clicked |
form_submission | Form was submitted |
file_download | File was downloaded |
copy | Text was copied |
exit_intent | Mouse moved toward browser close/back |
user_idle | User went idle on the page |
video_play | Video started playing |
video_pause | Video was paused |
video_complete | Video finished playing |
Filter by type: filter=event_type:eq:page_view
Event Data (Custom Passthrough Values)
The event_data field contains page-level context captured by the pixel. It is also where custom passthrough values appear.
What the Pixel Captures Automatically
For standard events, the pixel sends structured data including page URL, title, viewport dimensions, and event-specific metadata:
{
"url": "https://www.yoursite.com/pricing",
"title": "Pricing - YourSite",
"timestamp": "2026-02-16T01:02:13.536Z",
"screen": { "height": 900, "width": 1440 },
"viewport": { "height": 778, "width": 1440 },
"referrer": null
}For click events, it also includes the clicked element:
{
"url": "https://www.yoursite.com/",
"timestamp": "2026-02-16T03:55:34.851Z",
"coordinates": { "x": 959, "y": 139 },
"element": {
"tag": "A",
"text": "Meet the Team",
"href": "https://www.yoursite.com/meet-the-team/"
}
}Custom Passthrough Values
You can attach custom key-value pairs to every event the pixel sends. There are two approaches depending on whether you're using the JavaScript pixel or sending events server-to-server.
Option A: HTML Attribute (Simplest)
Add data-global-params to the pixel script tag. These values are sent with every event automatically:
<script id="delivr-ai"
src="https://cdn.delivr.ai/pixels/YOUR_PIXEL_ID/p.js"
data-global-params='{"campaign_id":"summer-2026","client_uid":"your-external-id"}'
async></script>Option B: JavaScript API (Dynamic)
After the pixel loads, call setGlobalParams or setEventParams on the SDK:
// Send with ALL events
PixelSDK.setGlobalParams({
campaign_id: "summer-2026",
account_tier: "premium"
});
// Send only with specific event types
PixelSDK.setEventParams("page_view", {
page_category: "pricing"
});setGlobalParams and setEventParams merge -- calling them multiple times adds to the existing params. Event-specific params override global params when the keys overlap.
The SDK object is available as both
PixelSDKandDelivrSDKon the window.
Where Custom Values Appear
Custom values are nested inside event_data.static_params. The payload the pixel sends looks like:
{
"event_type": "page_view",
"event_data": {
"url": "https://www.yoursite.com/pricing",
"title": "Pricing Page",
"timestamp": "2026-02-16T01:02:13.536Z",
"viewport": { "width": 1440, "height": 778 },
"screen": { "width": 1440, "height": 900 },
"static_params": {
"campaign_id": "summer-2026",
"client_uid": "your-external-id",
"page_category": "pricing"
}
},
"pixel_id": "YOUR_PIXEL_ID",
"organization_id": "YOUR_ORG_ID"
}When you query the Events API, event_data is returned as a JSON string. Parse it to access static_params:
import json
for row in rows:
event_data = json.loads(row.get("event_data", "{}"))
url = event_data.get("url")
static = event_data.get("static_params", {})
campaign = static.get("campaign_id")const eventData = JSON.parse(row.event_data || "{}");
const url = eventData.url;
const campaign = eventData.static_params?.campaign_id;Option C: Server-to-Server (Advanced)
If you need to send events from your backend (not the browser), POST directly to the pixel endpoint. The endpoint URL is in your pixel script -- look for the endpoint value:
curl -X POST https://YOUR_PIXEL_API_HOST/pixel/core/api/send-event \
-H "Content-Type: application/json" \
-d '{
"pixel_id": "YOUR_PIXEL_ID",
"organization_id": "YOUR_ORG_ID",
"event_type": "page_view",
"event_data": {
"url": "https://customer-site.com/pricing",
"title": "Pricing Page",
"campaign_id": "summer-2026",
"client_uid": "your-external-cookie-id"
}
}'With server-to-server, custom fields go directly in event_data (no static_params wrapper). The pixel API host varies by account -- find yours by inspecting the endpoint field in your pixel's JavaScript file.
Server-to-server events won't have browser context (viewport, cookies, user agent) unless you include them in the payload.
How event_data Appears in API Responses
event_data is returned as a JSON string, not a parsed object. You must parse it yourself:
{
"event_data": "{\"url\":\"https://customer-site.com/pricing\",\"title\":\"Pricing Page\",\"static_params\":{\"campaign_id\":\"summer-2026\",\"client_uid\":\"your-external-id\"}}"
}In CSV Exports
In CSV files, event_data appears as a quoted JSON string with escaped quotes:
event_data
"{""url"":""https://customer-site.com/pricing"",""title"":""Pricing Page"",""static_params"":{""campaign_id"":""summer-2026""}}"Errors
| HTTP Status | Error | Cause |
|---|---|---|
| 400 | "pixel_id is required" | Missing pixel_id parameter |
| 400 | "missing field start_ms" | Missing start_ms parameter |
| 401 | "invalid jwt token" | Invalid or expired Bearer token |
Response Reference
Events Response
{
"pixel_id": "...",
"start_ms": 1769558400000,
"end_ms": 1769644800000,
"limit": 20,
"offset": 0,
"rows": [ ... ],
"meta": {
"files_scanned": 29,
"buckets_scanned": 1,
"took_ms": 151
}
}Counts Response
{
"count": 287,
"meta": {
"files_scanned": 29,
"buckets_scanned": 1,
"took_ms": 51
}
}Schema Response
{
"fields": [
{ "name": "field_name", "data_type": "Utf8", "nullable": true }
]
}Next Steps
- Export Events to CSV -- Paginate and export visitor data to CSV
- Company Surge Report -- Identify companies with increased site activity
- High-Intent Visitors -- Daily lead list from pricing page visitors
- Intent Audiences API -- Create audiences from intent topics instead of pixel data
- Taxonomy API -- Browse the intent topic catalog
Updated 19 days ago