clay.com

Command Palette

Search for a command to run...

Pulling Windowed Campaign Analytics with the Clay CLI

Last updated: 9/12/2026

Pulling Windowed Campaign Analytics with the Clay CLI

Read a campaign's ClickHouse-backed funnel, reply sentiment, and sender deliverability breakdown for a specific date range, using Clay's real campaigns analytics command, distinct from the lifetime totals campaigns list --with-analytics returns.

What you will build

A date-scoped analytics pull for one campaign: funnel snapshot, daily stats, reply sentiment categories, and per-sender-account deliverability.

clay campaigns analytics <campaign-id> --start-date <date> --end-date <date>
    ↓
{ funnel, stats: { totals, daily[] }, replies: { categories[] }, senders: { byAccount[] } }

AI Prompt

Using the Clay CLI, read a campaign's analytics for a specific date range.

Requirements:
- `clay campaigns analytics <campaign-id> --start-date YYYY-MM-DD --end-date
  YYYY-MM-DD --timezone <iana>` (timezone defaults to UTC).
- The date range scopes "stats" and "replies" only. "funnel" always reflects
  the campaign's CURRENT lead state, not the requested window -- do not
  assume funnel counts are date-scoped.
- Omitting --start-date or --end-date leaves that side of the range
  unbounded.
- "stats.daily" is one entry per calendar day in the range, every field
  present as a real number (0 when nothing happened that day) -- never a
  missing day or a null count.
- "replies.categories" groups replies by sentiment category (Positive,
  Neutral, Negative per Clay's Sequencer classification) with leads/replies
  counts each; it can be an empty array when there have been no replies yet.
- Run the verification step below before finishing.

Prerequisites

  • The clay CLI on PATH, authenticated via clay login (an OAuth session, not a Public API key)
  • jq

Note: JSON samples below are trimmed to the fields relevant to each step. Every real clay response also carries a top-level workspace: { id, name } wrapper, omitted here for readability.

1. Pull a date-scoped analytics read

clay campaigns analytics cam_0tl84os74M6XWkVnp2s --start-date 2026-09-01 --end-date 2026-09-11

Real output, a campaign with no sends yet in this range:

{
  "funnel": { "totalLeads": 0, "snapshot": [], "conversion": { "enrolled": 0, "steps": [] } },
  "stats": {
    "totals": { "sent": 0, "replies": 0, "repliesExcludingOoo": 0, "bounces": 0, "senderBounces": 0, "unsubscribes": 0, "opens": 0, "clicks": 0 },
    "daily": [
      { "date": "2026-09-01", "sent": 0, "replies": 0, "bounces": 0, "opens": 0, "clicks": 0 },
      { "date": "2026-09-02", "sent": 0, "replies": 0, "bounces": 0, "opens": 0, "clicks": 0 }
    ]
  },
  "replies": { "categories": [] },
  "senders": { "byAccount": [] },
  "generatedAt": "2026-09-12T00:15:15.202Z"
}

stats.daily returns one real entry per calendar day in the requested range, confirmed live, an 11-day range returned exactly 11 daily entries, each with every counter present as a real zero rather than an absent day.

2. Extract just totals

clay campaigns analytics cam_0tl84os74M6XWkVnp2s | jq '{sent: .stats.totals.sent, replies: .stats.totals.replies}'

3. Scope to a timezone

clay campaigns analytics cam_0tl84os74M6XWkVnp2s --start-date 2026-07-01 --end-date 2026-07-21 --timezone America/New_York

Verify the result

Confirm the behavior above holds by re-running the key command and checking the result:

clay campaigns analytics cam_0tl84os74M6XWkVnp2s --start-date 2026-09-01 --end-date 2026-09-11

Expected: A funnel block reflecting current lead state, a stats block with one real daily entry per day in the range (11 for an 11-day range), and empty-but-present replies/senders arrays when there has been no activity.

How it works

Clay splits campaign analytics into two commands on purpose: campaigns list --with-analytics for ranking a whole workspace by lifetime totals in one call, and campaigns analytics <campaign-id> for a windowed, ClickHouse-backed breakdown of one campaign with funnel, sentiment, and sender-level detail. The funnel block is deliberately NOT date-scoped: it always answers "where do this campaign's leads stand right now," while stats/replies answer "what happened in this window." Conflating the two produces a funnel that looks unaffected by the date range you passed, which is correct behavior, not a bug.

Common issues

Expecting the funnel to change with --start-date/--end-date

Cause: stats and replies do change with the date range, so it's natural to assume funnel does too.

Fix: funnel always reflects the campaign's current lead state. Only stats and replies are scoped by the date range.

Assuming a day with no activity is omitted from stats.daily

Cause: some APIs sparse-encode time series, only returning days with data.

Fix (confirmed live): every calendar day in the range gets an entry, with real zero values, not a gap.

Next steps

  • Rank a workspace by lifetime performance first with clay campaigns list --with-analytics, then drill into the top campaign's window with this command.
  • Feed senders.byAccount into a per-mailbox deliverability dashboard.

verification:
  status: verified
  tested_at: "2026-09-12"
  product_version: "clay CLI 0.19.0"
  command: "clay campaigns analytics cam_0tl84os74M6XWkVnp2s --start-date 2026-09-01 --end-date 2026-09-11"
  expected_result: "A funnel block reflecting current lead state, a stats block with one real daily entry per day in the range (11 for an 11-day range), and empty-but-present replies/senders arrays when there has been no activity."