Analytics: Summary & Timeseries

Get email delivery and engagement metrics

Use analytics endpoints to understand email performance across your campaigns and flows.

Summary Metrics

Get aggregate metrics for a time period.

cURL

curl "https://api.tratto.email/v1/analytics/summary?period=30d" \
  -H "Authorization: Bearer tratto_live_..."

Query Parameters:

  • period: Time range — 7d, 30d, 90d (default 30d), 180d, or 1y

Response:

{
  "data": {
    "period": "30d",
    "totalSent": 50000,
    "delivered": 49500,
    "opened": 24750,
    "clicked": 7425,
    "bounced": 400,
    "complained": 100,
    "deliveryRate": 99,
    "openRate": 50,
    "clickRate": 15,
    "bounceRate": 0.8,
    "avgDeliveryLatencySeconds": 4.2
  }
}

Metric Definitions

MetricDefinition
totalSentEmails sent to mail server
deliveredMail servers confirmed delivery (no bounce)
openedUnique recipients who opened email
clickedUnique recipients who clicked a link
bouncedMail servers rejected delivery (hard or soft bounce)
complainedRecipients marked as spam
avgDeliveryLatencySecondsAverage time from send to delivery confirmation. null when no email in the period has been delivered yet

unsubscribed isn't part of this endpoint — it's tracked on the contact, not on individual emails. Check a contact's status instead. A campaign has no unsubscribed count either — the nearest thing is stats.skipped, the sends cancelled because the contact unsubscribed between dispatch and delivery (see Campaigns).

Rates

All rates are percentages (98.5 means 98.5%, not 0.985).

RateCalculation
deliveryRatedelivered / totalSent
openRateopened / delivered (falls back to totalSent if nothing delivered yet)
clickRateclicked / opened (falls back to totalSent)
bounceRatebounced / totalSent

These are workspace-wide rates. For one campaign, including the per-subject results of an A/B subject test, use GET /v1/campaigns/:id/stats: its open and click rates leave out recipients who opted out of tracking.

Timeseries Data

Get daily breakdown of metrics.

cURL

curl "https://api.tratto.email/v1/analytics/timeseries?period=7d" \
  -H "Authorization: Bearer tratto_live_..."

Response:

{
  "data": [
    {
      "date": "2026-07-30",
      "sent": 5000,
      "delivered": 4950,
      "opened": 2475,
      "bounced": 40
    },
    {
      "date": "2026-07-29",
      "sent": 4800,
      "delivered": 4752,
      "opened": 2376,
      "bounced": 38
    }
  ]
}

Note the narrower shape than the summary endpoint: sent/delivered/opened/bounced only, no clicked or complained per day.

Get the most-clicked links for a specific campaign.

cURL

curl "https://api.tratto.email/v1/campaigns/camp_abc123/links?limit=20" \
  -H "Authorization: Bearer tratto_live_..."

Response:

{
  "data": [
    { "linkUrl": "https://example.com/pricing", "clicks": 142, "uniqueClicks": 98 },
    { "linkUrl": "https://example.com/docs", "clicks": 37, "uniqueClicks": 30 }
  ]
}

limit is optional, 1-50, defaults to 20.

Live and Test Data

Analytics follow the environment of the key that asks. A tratto_live_... key reads live sends only, a tratto_test_... key reads test sends only.

Both endpoints also accept a livemode query parameter (true or false). It is the view switch of the dashboard and is ignored for API keys: the key's own environment always wins, so a live key cannot read test numbers and a test key cannot read live ones, whatever you pass.

The test view is limited to 7d, 30d and 90d. The longer periods read an aggregate that contains live data only, so a test key asking for 180d or 1y gets 422 VALIDATION_ERROR instead of zeros:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Analytics for the test environment are not available for the '180d' period.",
    "docs": "https://docs.tratto.email/en/docs/error-codes",
    "suggestion": "Periods longer than 90 days read the long-term aggregate, which contains live data only. Use period=90d or shorter for the test view."
  }
}

Data Windows: Firestore vs BigQuery

  • 7d/30d/90d are served from Firestore in real time — no aggregation delay, but Firestore only retains email documents for 90 days.
  • 180d/1y are served from a nightly BigQuery aggregate instead. That aggregation runs once per day (around 02:30 UTC) — very recent data (today, and sometimes yesterday depending on when you check) won't be reflected yet for these two longer windows. Use 90d or shorter if you need same-day numbers.

Best Practices

1. Monitor Delivery Rate

Target 99%+. Lower rates indicate domain or list hygiene issues.

if (data.deliveryRate < 95) {
  alert('Low delivery rate detected');
}

2. Track Open & Click Rates

Typical benchmarks:

  • Open rate: 20-50% (varies by industry)
  • Click rate: 5-20% (varies by industry)

Low rates suggest content or timing issues.

3. Monitor Bounce & Complaint Rates

  • Bounce rate: < 2% acceptable (< 5% is OK)
  • Complaint rate: complained / totalSent — keep it under 0.1%

High rates damage sender reputation and deliverability.


Next Steps


Edit this page on GitHub

Last updated on