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 | string (UUID) | Your pixel ID (from dashboard Settings > Pixels) |
start_ms | integer | Start of time window in milliseconds since epoch |
end_ms | integer | End of time window in milliseconds since epoch |
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 ...",
"...": "110 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 ~110 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
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?pixel_id=YOUR_PIXEL_ID
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 110+ 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
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 1 day ago