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:

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-id header 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:

  1. Dashboard: Settings > Pixels > Copy the pixel ID
  2. 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

ParameterTypeDescription
pixel_id or project_idstring (UUID)Your pixel ID (from dashboard Settings > Pixels) or your project ID. Provide one or the other.
start_msintegerStart of time window in milliseconds since epoch
end_msintegerEnd of time window in milliseconds since epoch

You can use either pixel_id or project_id to query events. If a project has multiple pixels, querying by project_id returns events from all of them.

Optional Parameters

ParameterTypeDefaultDescription
limitinteger100Max rows to return
offsetinteger0Skip this many rows (for pagination)
filterstring--Filter expression (see Filters section)
selectstring--Comma-separated field names to return
distinctstring--Deduplicate by field: hem or company_domain
has_valuable_databoolean--Only return rows with actionable contact or company data
orderbystring--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

OperatorExampleDescription
eqresolved:eq:trueExact match
likecompany_name:like:acmeCase-insensitive contains
not_nullfirst_name:not_nullField 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, so resolved:eq:true becomes resolved%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

ParameterTypeDescription
filterstringSame filter syntax as events endpoint
distinctstringCount 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

CategoryExample Fields
Eventevent_id, event_type, timestamp, ts_millis, cookie_id
Pagereferrer_url, event_data (JSON with URL, title, viewport)
Identityresolved, hem, hems, email, domain_lc
Personfirst_name, last_name, job_title, department, seniority_level
Companycompany_name, company_domain, company_industry, company_employee_count_range
Locationpersonal_city, personal_state, personal_country, company_city
Demographicsage_range, gender, income_range_lc, net_worth, is_homeowner
Contactcurrent_business_email, business_emails, personal_emails, phones, linkedin_url
Hashesemails_sha256_lc_hem, emails_md5_lc_hem, emails_sha1_lc_hem
Networkclient_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

  1. Request with offset=0&limit=100
  2. If you get back 100 rows (equal to your limit), there may be more -- request offset=100&limit=100
  3. 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 TypeDescription
page_viewPage was loaded
clickElement was clicked
form_submissionForm was submitted
file_downloadFile was downloaded
copyText was copied
exit_intentMouse moved toward browser close/back
user_idleUser went idle on the page
video_playVideo started playing
video_pauseVideo was paused
video_completeVideo 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 PixelSDK and DelivrSDK on 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 StatusErrorCause
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