Analytics

Get detailed geographic and device analytics for email events. Understand where your subscribers are located, what devices they use, and identify bot traffic to get accurate engagement metrics.

Geographic Analytics

Get a breakdown of email events by country. Available at both the organization level and per-campaign level.

Organization Level

GET /v1/analytics/geo
org-geo-analytics.ts
const geoData = await client.analytics.geo({
  days: 30,
  eventType: 'OPENED',
});

console.log(`Total events: ${geoData.totalCount}`);

for (const entry of geoData.data) {
  console.log(`${entry.countryName}: ${entry.count} (${entry.percentage}%)`);
}

// Example output:
// Total events: 12,450
// United States: 5,210 (41.8%)
// United Kingdom: 1,870 (15.0%)
// Germany: 1,120 (9.0%)
// ...

Campaign Level

GET /v1/campaigns/:id/analytics/geo
campaign-geo-analytics.ts
const geoData = await client.analytics.campaignGeo('campaign_xxxxx', {
  eventType: 'CLICKED',
});

for (const entry of geoData.data) {
  console.log(`${entry.countryCode} — ${entry.countryName}: ${entry.count} clicks`);
}

Query Parameters

ParameterTypeDescription
daysintegerLookback period in days (max 90, default 30). Only for org-level.
eventTypestringOPENED or CLICKED (default: OPENED)

Response Fields

FieldTypeDescription
totalCountintegerTotal number of events in the period
data[].countryCodestringISO 3166-1 alpha-2 country code
data[].countryNamestringFull country name
data[].countintegerNumber of events from this country
data[].percentagenumberPercentage of total events (0-100)

Device Analytics

Get a breakdown of email events by device type, operating system, and email client. Includes bot detection to help you separate genuine engagement from automated opens.

Organization Level

GET /v1/analytics/devices
org-device-analytics.ts
const deviceData = await client.analytics.devices({
  days: 30,
  eventType: 'OPENED',
});

// Bot detection metrics
console.log(`Total opens: ${deviceData.totalCount}`);
console.log(`Bot opens: ${deviceData.botCount}`);
console.log(`Machine opens: ${deviceData.machineOpenCount}`);

// Device breakdown
for (const device of deviceData.devices) {
  console.log(`${device.deviceType}: ${device.count} (${device.percentage}%)`);
}

// OS breakdown
for (const os of deviceData.operatingSystems) {
  console.log(`${os.name}: ${os.count} (${os.percentage}%)`);
}

// Email client breakdown
for (const client of deviceData.emailClients) {
  console.log(`${client.name}: ${client.count} (${client.percentage}%)`);
}

Campaign Level

GET /v1/campaigns/:id/analytics/devices
campaign-device-analytics.ts
const deviceData = await client.analytics.campaignDevices('campaign_xxxxx');

// Filter out bot traffic for accurate metrics
const humanOpens = deviceData.totalCount - deviceData.botCount;
console.log(`Human opens: ${humanOpens}`);

Response Fields

FieldTypeDescription
totalCountintegerTotal number of events
botCountintegerEvents identified as bot-generated
machineOpenCountintegerMachine-generated opens (e.g., Apple MPP)
devicesarrayBreakdown by device type (desktop, mobile, tablet)
operatingSystemsarrayBreakdown by OS (iOS, Android, Windows, macOS, etc.)
emailClientsarrayBreakdown by email client (Gmail, Apple Mail, Outlook, etc.)

Link Analytics

Track click-through performance for individual links in emails and campaigns. When open/click tracking is enabled on a domain, all links are automatically tracked and click data is available through these endpoints.

Email Link Analytics

GET /v1/emails/:id/links
email-link-analytics.ts
const { data } = await client.emails.links('email_xxxxx');

for (const link of data) {
  console.log(`${link.url}: ${link.totalClicks} clicks (${link.uniqueClicks} unique)`);
}

Campaign Link Analytics

GET /v1/campaigns/:id/links
campaign-link-analytics.ts
const { data } = await client.campaigns.links('campaign_xxxxx');

// Sort by most clicked
const sorted = data.sort((a, b) => b.totalClicks - a.totalClicks);

for (const link of sorted) {
  console.log(`${link.url}`);
  console.log(`  Total clicks: ${link.totalClicks}`);
  console.log(`  Unique clicks: ${link.uniqueClicks}`);
}

Engagement Scoring

Get aggregated engagement statistics for an audience and trigger recalculation of subscriber engagement scores based on recent activity.

Get Engagement Stats

GET /v1/audiences/:id/engagement-stats
engagement-stats.ts
const stats = await client.audiences.getEngagementStats('audience_xxxxx');

console.log(`Average score: ${stats.averageScore}`);
console.log(`Scored: ${stats.scoredCount}, Unscored: ${stats.unscoredCount}`);

// Tier distribution
console.log(`Highly engaged: ${stats.distribution.HIGHLY_ENGAGED}`);
console.log(`Engaged: ${stats.distribution.ENGAGED}`);
console.log(`Moderately engaged: ${stats.distribution.MODERATELY_ENGAGED}`);
console.log(`At risk: ${stats.distribution.AT_RISK}`);
console.log(`Inactive: ${stats.distribution.INACTIVE}`);

Recalculate Engagement

POST /v1/audiences/:id/recalculate-engagement
recalculate-engagement.ts
const result = await client.audiences.recalculateEngagement('audience_xxxxx');

console.log(`Recalculated scores for ${result.processed} subscribers`);

Bot Detection

Veil Mail automatically identifies bot-generated opens and clicks, including Apple Mail Privacy Protection (MPP) pre-fetches and corporate security scanners. This helps you measure genuine subscriber engagement.

Tip: Use the botCount and machineOpenCount fields to calculate true open rates. Subtract these from totalCount for human-only engagement metrics.

Organization-level analytics requires the email:read scope. Campaign-level analytics requires the campaign:read scope. Engagement scoring requires the audience:read and audience:write scopes.