Events API v3

The Events API v3 splits the events query into four route-fixed tiers. The URL you call fixes both the billing tier and the identity fields you can get back, so the price of a call is legible from the route. Pick the tier that matches the enrichment you need.

Pick the route, and you pick the price

RouteReturns
GET /api/v3/events/web_eventBase event columns, no identity resolution (lowest cost)
GET /api/v3/events/resolution_hemAdds the hashed email (HEM)
GET /api/v3/events/resolution_hem_pteAdds HEM plus PTE identifiers
GET /api/v3/events/resolution_full_profileAdds full contact, company, and demographic fields (highest cost)

There is no bare /api/v3/events query route: you always call a tier. Start with web_event to confirm data is flowing, then move up only to the tier whose identity or profile fields you actually need.

Rate limit and billing

  • The Events API allows 720 requests per minute per organization, with a burst of 25. Over the limit returns 429 with a Retry-After header. Higher limits are available per contract.
  • You are billed per record returned on a 2xx response, on the route's meter. Schema routes, the counts route, empty results, and non-2xx responses bill nothing.

See Rate Limits and Metering for the full rules.

Discover the fields before you query

  • GET /api/v3/events_schema returns the full field metadata (non-billable).
  • GET /api/v3/events/{tier}/schema returns only the fields allowed at that tier, for example GET /api/v3/events/web_event/schema.

Selecting a field above your route's tier is rejected, so the schema endpoint tells you exactly what each tier can return.

Authenticate and scope

Send your API key on every request, and scope the call to a project:

X-Api-Key: dlvr_xxxxxxxx
X-Api-Secret: <your secret>

Pass the project as ?project_id=<project-uuid> (or the X-Project-Id header). Create your key in the dashboard under Organization settings, API Keys. All calls are served from https://api.delivr.ai. The apiv2.delivr.ai and apiv3.delivr.ai hostnames are legacy aliases of the same backend, so the path selects the version, not the host.

A project-scoped key reads only its own project: a request scoped to the organization returns that project's events, and another project's project_id, pixel or campaign returns 404.

Query

curl -s "https://api.delivr.ai/api/v3/events/web_event?project_id=<project-uuid>&start_ms=1792022400000&end_ms=1792108800000&limit=100" \
  -H "X-Api-Key: dlvr_xxxxxxxx" \
  -H "X-Api-Secret: <your secret>"

The response is { "rows": [ ... ], "meta": { ... } }; the columns present depend on the tier route and the fields you select.

start_ms and end_ms are required on every tier route; a request without them returns 400. The window is at most 25 hours, or 92 days with include_cold=true. limit defaults to 300 and is capped at 2000: a larger value returns 400. distinct takes a single field, not a comma list.

To see what a query would bill before you run it, add dry_run=true. The response is { "dry_run": true, "meter": "...", "records_to_charge": N }, and nothing is billed. records_to_charge is an upper bound.

Move up a tier to get resolved identity:

curl -s "https://api.delivr.ai/api/v3/events/resolution_hem?project_id=<project-uuid>&start_ms=1792022400000&end_ms=1792108800000&limit=100" \
  -H "X-Api-Key: dlvr_xxxxxxxx" \
  -H "X-Api-Secret: <your secret>"

List the visitors behind the events

The tier routes return one row per event. When you want who visited rather than what they did, call a tier's /visitors route. It returns one row per person, or per company, over a window, deduplicated across the project's pixels.

curl -s "https://api.delivr.ai/api/v3/events/resolution_full_profile/visitors?project_id=<project-uuid>&kind=buyers&start_ms=1790121600000&end_ms=1790726400000&limit=500" \
  -H "X-Api-Key: dlvr_xxxxxxxx" \
  -H "X-Api-Secret: <your secret>"

The response is { "visitors": [ ... ], "count": 500, "has_partial_data": false }.

  • Buyers or accounts. kind=buyers returns one row per person: rows that share a profile id or any hashed email in hem or hems are merged. kind=accounts returns one row per company, deduplicated on company_domain. Only resolution_full_profile carries the company domain, so accounts are available on that tier alone.
  • The tier is the price. Visitors are available on resolution_hem, resolution_hem_pte and resolution_full_profile. Each visitor returned counts once at that tier's price, whatever columns you select. Selecting fewer columns makes the answer smaller, not cheaper. web_event has no visitors route, because it carries no identity to deduplicate on.
  • Scope. Name project_id to read every pixel in the project, or pixel_id for one. Add include_dsp=true to include visitors from the project's DelivrDSP campaigns as well. Campaigns are read only for a project_id: with pixel_id alone, include_dsp adds nothing, so name the project too.
  • Window and limit. start_ms, end_ms and limit are required, and limit goes up to 50,000. Visitors come most recent first, and limit keeps the most recent. There is no paging: when more visitors match than you asked for, narrow the window to reach earlier ones.
  • Partial answers. has_partial_data: true means part of the window could not be read, so visitors may be missing. Only the visitors returned count. Ask again, or narrow the window.

Count and break down one day of events

When you want to know how much traffic a day carried and where it came from, rather than the events themselves, call GET /api/v3/events/web_event/counts. It counts a project's or one pixel's events over a window of up to 24 hours, and breaks the counts down by up to two fields. It returns counts only, never identifiers, and bills nothing.

curl -s "https://api.delivr.ai/api/v3/events/web_event/counts?project_id=<project-uuid>&start_ms=1792022400000&end_ms=1792108800000&group_by=ip_country&limit=3" \
  -H "X-Api-Key: dlvr_xxxxxxxx" \
  -H "X-Api-Secret: <your secret>"

The response carries the window and scope you asked for, plus { "totals": { "events": 2438, "visitors": 120, "resolved": 30 }, "groups": [ { "key": { "ip_country": "US" }, "events": 1290, "visitors": 81, "resolved": 27 }, ... ] }. Without group_by, groups is empty.

  • Measures. events counts events, visitors counts distinct browsers, and resolved counts events resolved to a person. totals covers every matching event. groups holds the top limit groups (10 by default, up to 50), largest first by order_by.
  • Breakdowns. group_by takes up to two of pixel_id, event_type, ip_country, is_bot, bot_type, resolved, gpc and browser. filter takes the usual grammar, limited to fields the web_event tier carries.
  • Browser ranking. group_by=browser ranks individual browsers by activity. Each comes back as a rank (1, 2, and so on), never as a cookie ID. Events without a browser cookie form one unranked group, with browser set to null.
  • Window. At most 24 hours, starting no more than 30 days ago. A window with more stored events than one request can read returns 422 with code set to scan_too_large, and a message giving both numbers. Narrow to one pixel, or shorten the window.
  • Freshness. Counts cover events the event store has already written, which it does within an hour of their arrival. A window that includes the most recent hour can come back short for that hour, so ask again later for a complete count.
  • Errors. Every error carries a message in error and a reason in code. Branch on code:
codeStatusMeaning
invalid_request400A parameter is missing or invalid. The message names it.
upstream_rejectedthe event store's 4xx, usually 400The event store refused the query, for example a filter with an unsupported operator. With 408, the query ran too long: narrow the window, the pixel or the filter.
not_found404The project or pixel was not found in your organization, or the pixel is not in the named project.
method_not_allowed405The route takes GET only.
pixel_inactive422The pixel is inactive.
scan_too_large422The window holds more stored events than one request can read.
internal_error500Access could not be checked. Retry.
upstream_error502, or the event store's 5xxThe event store failed or is overloaded. Retry.
unavailable503Counts are temporarily unavailable. Retry shortly.
upstream_timeout504The event store did not answer in time. Retry, or narrow the window.

A missing or invalid API key (401) and rate limiting (429) are answered before the route runs, so those carry error only.

Filters, field selection, and time windows

The tier routes share the same query parameters as the rest of the events API: field selection, filters, ordering, paging, and the time window. See Pixel and DSP Event Fields for the full field catalog and query-parameter reference.

Which endpoint should I use?

The per-tier routes make cost predictable and bound the returned fields to the tier you called. Start at web_event to confirm data is flowing, then move up only to the tier whose identity or profile fields you actually need.


Did this page help you?