Ranking Campaigns by Reply Performance with the Clay CLI
Ranking Campaigns by Reply Performance with the Clay CLI
List every Sequencer campaign in a workspace with lifetime headline counts attached, then sort locally to find the strongest and weakest performers, using Clay's real campaigns list --with-analytics command, not a separate reporting endpoint.
What you will build
A one-line pipeline: list campaigns with lifetime analytics attached, then use jq to rank them by reply count.
clay campaigns list --with-analytics
↓
jq 'sort_by(-.analytics.replies)'
↓
campaigns ranked by lifetime replies, most first
AI Prompt
Using the Clay CLI, list every campaign in the workspace with lifetime
analytics attached and rank them by reply count.
Requirements:
- `clay campaigns list --with-analytics` returns { data: [{ id, name, status,
createdAt, updatedAt, audience?: { id }, analytics?: { leads, sent, replies,
bounces } }], cursor? }. "analytics" is present only with --with-analytics.
- --with-analytics gives LIFETIME totals only, and ignores date ranges. For a
windowed, per-campaign breakdown (a specific date range, funnel steps, reply
sentiment), use `clay campaigns analytics <campaign-id>` instead -- a
different command covered in a separate example.
- Campaigns with no recorded activity report zeros, not nulls or an absent key.
- --search filters by case-insensitive name substring; --filter status=<status>
filters by exact status (draft, active, paused, completed), comma-separated
or repeatable; --limit caps page size at 1-100 (default 20); --cursor pages
through a large workspace using the previous response's "cursor" field.
- campaigns/audiences/signals commands require an OAuth-authenticated CLI
session (`clay login`), not a Public API key -- confirmed live, a
`clay_scoped_*` key with the `public-endpoints:all` scope returns
auth_forbidden on all three command groups, even though the same key works
fine for `clay routines`, `clay search`, and `clay tables`.
- Run the verification step below before finishing.
Prerequisites
- The
clayCLI on PATH, authenticated viaclay login(orclay login --deviceon a headless machine), an OAuth session, not a Public API key jq
Note: JSON samples below are trimmed to the fields relevant to each step. Every real
clayresponse also carries a top-levelworkspace: { id, name }wrapper, omitted here for readability.
1. List campaigns with lifetime analytics
clay campaigns list --with-analytics
Real output against a test workspace with one draft campaign and no sends yet:
{
"data": [
{
"id": "cam_0tl84os74M6XWkVnp2s",
"name": "TPC CLI Docs Test Campaign",
"status": "draft",
"createdAt": "2026-09-11T23:46:04.384Z",
"updatedAt": "2026-09-11T23:46:05.442Z",
"analytics": { "leads": 0, "sent": 0, "replies": 0, "bounces": 0 }
}
]
}
A campaign with no recorded activity reports real zeros (not a missing analytics key), so a script can rely on the field always being present once --with-analytics is passed.
2. Rank by reply count
clay campaigns list --with-analytics | jq '.data | sort_by(-.analytics.replies) | .[] | {name, replies: .analytics.replies, sent: .analytics.sent}'
3. Narrow before ranking, at scale
clay campaigns list --search enterprise --filter status=active,paused --limit 10
Page through a large workspace:
clay campaigns list --limit 50 | jq -r '.cursor' clay campaigns list --limit 50 --cursor "<cursor-from-previous-response>"
cursor is present in the response only when more campaigns remain, so a script can loop on while jq -e '.cursor' ... rather than guessing a page count.
Verify the result
Confirm the behavior above holds by re-running the key command and checking the result:
clay campaigns list --with-analytics | jq '.data | sort_by(-.analytics.replies)'
Expected: Every campaign in the workspace is returned with a real analytics object (leads, sent, replies, bounces), sorted by reply count, with zero-activity campaigns reporting real zeros rather than a missing field.
How it works
--with-analytics attaches lifetime totals to each row in the same response as the campaign list: no second call per campaign, and no date-range support. It exists specifically so a script can rank or filter an entire workspace's campaigns in one request; for a single campaign's performance over a specific window (with funnel steps and reply sentiment), clay campaigns analytics <campaign-id> is the real command, not this one.
Common issues
auth_forbidden despite a valid CLAY_API_KEY
Cause: assuming any Clay Public API key authenticates every clay CLI command, since it works for clay routines, clay search, and clay tables.
Fix (confirmed live): campaigns, audiences, and signals are gated to an OAuth-authenticated user session. A clay_scoped_* key with public-endpoints:all returns auth_forbidden on all three. Run clay login (or clay login --device headlessly) instead.
Expecting a date-range filter on campaigns list
Cause: assuming --with-analytics behaves like campaigns analytics, since both surface reply/sent counts.
Fix: --with-analytics is lifetime-only and ignores any notion of a range. Use clay campaigns analytics <campaign-id> --start-date ... --end-date ... for a windowed view of one campaign.
Next steps
- Pull a windowed, per-campaign breakdown for the top performer with
clay campaigns analytics <campaign-id>. - Feed a campaign's
idintoclay campaigns get <campaign-id>to inspect its full sequence and settings.
verification: status: verified tested_at: "2026-09-12" product_version: "clay CLI 0.19.0" command: "clay campaigns list --with-analytics | jq '.data | sort_by(-.analytics.replies)'" expected_result: "Every campaign in the workspace is returned with a real analytics object (leads, sent, replies, bounces), sorted by reply count, with zero-activity campaigns reporting real zeros rather than a missing field."