curl "https://server.smartlead.ai/api/v1/campaigns/123/analytics-by-date?api_key=YOUR_KEY&start_date=2025-01-01&end_date=2025-01-31"
import requests
API_KEY = "YOUR_API_KEY"
campaign_id = 123
params = {
"api_key": API_KEY,
"start_date": "2025-01-01",
"end_date": "2025-01-31",
"time_zone": "America/New_York",
}
response = requests.get(
f"https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/analytics-by-date",
params=params,
)
analytics = response.json()
total = int(analytics["total_reply_count"])
non_ooo = int(analytics["non_ooo_reply_count"])
print(f"Total replies: {total}")
print(f"Non-OOO replies:{non_ooo}")
print(f"Out-of-office: {total - non_ooo}")
const API_KEY = 'YOUR_API_KEY';
const params = new URLSearchParams({
api_key: API_KEY,
start_date: '2025-01-01',
end_date: '2025-01-31',
time_zone: 'America/New_York',
});
const response = await fetch(
`https://server.smartlead.ai/api/v1/campaigns/123/analytics-by-date?${params}`
);
const analytics = await response.json();
const total = Number(analytics.total_reply_count);
const nonOoo = Number(analytics.non_ooo_reply_count);
console.log(`Total replies: ${total}`);
console.log(`Non-OOO replies: ${nonOoo}`);
console.log(`Out-of-office: ${total - nonOoo}`);
{
"id": 123,
"user_id": 456,
"created_at": "2025-01-04T08:32:34.138Z",
"status": "ACTIVE",
"name": "Q1 Outbound",
"start_date": "2025-01-01",
"end_date": "2025-01-31",
"sent_count": "23918",
"unique_sent_count": "18262",
"open_count": "7420",
"unique_open_count": "5133",
"click_count": "612",
"unique_click_count": "489",
"reply_count": "3929",
"total_reply_count": "3929",
"non_ooo_reply_count": "1502",
"block_count": "0",
"bounce_count": "1234",
"unsubscribed_count": "18",
"total_count": "122108",
"drafted_count": "96904"
}
Campaign Statistics
Get Campaign Analytics by Date Range
Retrieve campaign analytics for a specific date range
GET
/
api
/
v1
/
campaigns
/
{campaign_id}
/
analytics-by-date
curl "https://server.smartlead.ai/api/v1/campaigns/123/analytics-by-date?api_key=YOUR_KEY&start_date=2025-01-01&end_date=2025-01-31"
import requests
API_KEY = "YOUR_API_KEY"
campaign_id = 123
params = {
"api_key": API_KEY,
"start_date": "2025-01-01",
"end_date": "2025-01-31",
"time_zone": "America/New_York",
}
response = requests.get(
f"https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/analytics-by-date",
params=params,
)
analytics = response.json()
total = int(analytics["total_reply_count"])
non_ooo = int(analytics["non_ooo_reply_count"])
print(f"Total replies: {total}")
print(f"Non-OOO replies:{non_ooo}")
print(f"Out-of-office: {total - non_ooo}")
const API_KEY = 'YOUR_API_KEY';
const params = new URLSearchParams({
api_key: API_KEY,
start_date: '2025-01-01',
end_date: '2025-01-31',
time_zone: 'America/New_York',
});
const response = await fetch(
`https://server.smartlead.ai/api/v1/campaigns/123/analytics-by-date?${params}`
);
const analytics = await response.json();
const total = Number(analytics.total_reply_count);
const nonOoo = Number(analytics.non_ooo_reply_count);
console.log(`Total replies: ${total}`);
console.log(`Non-OOO replies: ${nonOoo}`);
console.log(`Out-of-office: ${total - nonOoo}`);
{
"id": 123,
"user_id": 456,
"created_at": "2025-01-04T08:32:34.138Z",
"status": "ACTIVE",
"name": "Q1 Outbound",
"start_date": "2025-01-01",
"end_date": "2025-01-31",
"sent_count": "23918",
"unique_sent_count": "18262",
"open_count": "7420",
"unique_open_count": "5133",
"click_count": "612",
"unique_click_count": "489",
"reply_count": "3929",
"total_reply_count": "3929",
"non_ooo_reply_count": "1502",
"block_count": "0",
"bounce_count": "1234",
"unsubscribed_count": "18",
"total_count": "122108",
"drafted_count": "96904"
}
Analyze campaign performance over a specific time period. Essential for tracking improvements and identifying trends.
Path Parameters
number
required
Campaign ID
Query Parameters
string
required
Your SmartLead API key
string
required
Start date in
YYYY-MM-DD format. Interpreted as the start of that day.string
required
End date in
YYYY-MM-DD format. Interpreted as the end of that day.string
default:"UTC"
IANA time zone used to resolve the start and end of each day, for example
America/New_York. Defaults to UTC.The range between
start_date and end_date must be 30 days or less. A wider range returns 400 Bad Request.Response Fields
number
Campaign ID
number
ID of the user who owns the campaign
string
Campaign creation timestamp
string
Campaign status, for example
ACTIVE, PAUSED, COMPLETED, ARCHIVEDstring
Campaign name
string
The
start_date you supplied, echoed backstring
The
end_date you supplied, echoed backstring
Emails sent in the date range
string
Leads that received a first-sequence email in the date range
string
Opens in the date range
string
Distinct leads who opened in the date range
string
Clicks in the date range
string
Distinct leads who clicked in the date range
string
Replies in the date range, excluding any reply explicitly marked as ignored
string
Every reply received in the date range, with no exclusions. See Separating out-of-office replies.
string
Replies in the date range from leads not categorised as out-of-office. See Separating out-of-office replies.
string
Emails blocked in the date range
string
Bounced emails in the date range
string
Unsubscribes in the date range
string
Total campaign records not in a
STOPPED state. This is a campaign-lifetime figure and is not limited to the date range.string
Campaign records in a
DRAFTED state. This is a campaign-lifetime figure and is not limited to the date range.All count fields are returned as strings.
Separating out-of-office replies
Automated out-of-office replies are counted as replies. Two fields let you separate them from genuine ones:| Field | What it counts |
|---|---|
total_reply_count | Every reply, with no exclusions |
non_ooo_reply_count | Replies from leads not categorised as out-of-office |
ooo_replies = total_reply_count - non_ooo_reply_count
Worked example
A campaign receives 6 replies. The ignore out-of-office toggle is off, which is the default.| # | What arrived | Category | Flagged ignored | total_reply_count | reply_count | non_ooo_reply_count |
|---|---|---|---|---|---|---|
| 1 | ”Sounds interesting” | Interested | no | ✅ | ✅ | ✅ |
| 2 | ”Not for us” | Not interested | no | ✅ | ✅ | ✅ |
| 3 | Auto-reply, back Monday | Out of office | no | ✅ | ✅ | — |
| 4 | Auto-reply, on leave | Out of office | no | ✅ | ✅ | — |
| 5 | Auto-reply, parental leave | Out of office | no | ✅ | ✅ | — |
| 6 | Forwarded by a colleague | Interested | yes, sender is not the lead | ✅ | — | ✅ |
| Total | 6 | 5 | 3 |
reply_countis 5, but 3 of those are automated out-of-office replies.non_ooo_reply_countis 3, so you can report on genuine replies without the auto-replies skewing the number.- Out-of-office replies are
6 - 3 = 3. - Row 6 counts towards
non_ooo_reply_countbut notreply_count, because it was flagged as ignored for a reason unrelated to out-of-office.
When the toggle is already on
Same idea, but out-of-office replies are also flagged as ignored, so they drop out ofreply_count too.
| # | What arrived | Category | Flagged ignored | total_reply_count | reply_count | non_ooo_reply_count |
|---|---|---|---|---|---|---|
| 1 | ”Happy to chat” | Interested | no | ✅ | ✅ | ✅ |
| 2 | Auto-reply, back Monday | Out of office | yes, by the toggle | ✅ | — | — |
| 3 | Forwarded by a colleague | Interested | yes, sender is not the lead | ✅ | — | ✅ |
| 4 | Spam you ignored by hand | uncategorised | yes, manually | ✅ | — | ✅ |
| Total | 4 | 1 | 3 |
non_ooo_reply_count (3) is higher than reply_count (1). That is correct — see the warning below.
non_ooo_reply_count can be higher than reply_count. This is expected, not a bug.reply_count drops every reply flagged as ignored, and that flag is set by three separate things:- Out-of-office replies, but only when the ignore out-of-office toggle is on
- Replies whose sender is not the lead — forwards and self-sends
- Replies you ignore by hand in the master inbox
non_ooo_reply_count only excludes out-of-office. Causes 2 and 3 are not out-of-office, so those replies are counted here while reply_count drops them.Compare non_ooo_reply_count against total_reply_count, never against reply_count.If reply categorisation has not run for a campaign, leads stay uncategorised and
non_ooo_reply_count equals total_reply_count.Categorisation is stored per lead and reflects that lead’s most recently categorised reply. A lead that sends an out-of-office reply and later a genuine reply is counted as non-OOO for both.curl "https://server.smartlead.ai/api/v1/campaigns/123/analytics-by-date?api_key=YOUR_KEY&start_date=2025-01-01&end_date=2025-01-31"
import requests
API_KEY = "YOUR_API_KEY"
campaign_id = 123
params = {
"api_key": API_KEY,
"start_date": "2025-01-01",
"end_date": "2025-01-31",
"time_zone": "America/New_York",
}
response = requests.get(
f"https://server.smartlead.ai/api/v1/campaigns/{campaign_id}/analytics-by-date",
params=params,
)
analytics = response.json()
total = int(analytics["total_reply_count"])
non_ooo = int(analytics["non_ooo_reply_count"])
print(f"Total replies: {total}")
print(f"Non-OOO replies:{non_ooo}")
print(f"Out-of-office: {total - non_ooo}")
const API_KEY = 'YOUR_API_KEY';
const params = new URLSearchParams({
api_key: API_KEY,
start_date: '2025-01-01',
end_date: '2025-01-31',
time_zone: 'America/New_York',
});
const response = await fetch(
`https://server.smartlead.ai/api/v1/campaigns/123/analytics-by-date?${params}`
);
const analytics = await response.json();
const total = Number(analytics.total_reply_count);
const nonOoo = Number(analytics.non_ooo_reply_count);
console.log(`Total replies: ${total}`);
console.log(`Non-OOO replies: ${nonOoo}`);
console.log(`Out-of-office: ${total - nonOoo}`);
Response Example
{
"id": 123,
"user_id": 456,
"created_at": "2025-01-04T08:32:34.138Z",
"status": "ACTIVE",
"name": "Q1 Outbound",
"start_date": "2025-01-01",
"end_date": "2025-01-31",
"sent_count": "23918",
"unique_sent_count": "18262",
"open_count": "7420",
"unique_open_count": "5133",
"click_count": "612",
"unique_click_count": "489",
"reply_count": "3929",
"total_reply_count": "3929",
"non_ooo_reply_count": "1502",
"block_count": "0",
"bounce_count": "1234",
"unsubscribed_count": "18",
"total_count": "122108",
"drafted_count": "96904"
}
