Campaign analytics
Returns detailed campaign analytics including:
- Overall campaign stats (sent, opened, clicked, bounced, replied, unsubscribed)
- Step-by-step analytics with A/B variant breakdown
- Lead status breakdown (active, completed, bounced, etc.)
- Total replied count
Without from/to, returns all-time data. Supply one or both to filter by date range.
curl -X GET "https://api.sendkit.ai/v1/campaigns/example_string/analytics?from=2024-05-01&to=2024-05-31" \
-H "Content-Type: application/json" \
-H "X-Api-Key: YOUR_API_KEY"
import requests
import json
url = "https://api.sendkit.ai/v1/campaigns/example_string/analytics?from=2024-05-01&to=2024-05-31"
headers = {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY"
}
response = requests.get(url, headers=headers)
print(response.json())
const response = await fetch("https://api.sendkit.ai/v1/campaigns/example_string/analytics?from=2024-05-01&to=2024-05-31", {
method: "GET",
headers: {
"Content-Type": "application/json",
"X-Api-Key": "YOUR_API_KEY"
}
});
const data = await response.json();
console.log(data);
package main
import (
"fmt"
"net/http"
)
func main() {
req, err := http.NewRequest("GET", "https://api.sendkit.ai/v1/campaigns/example_string/analytics?from=2024-05-01&to=2024-05-31", nil)
if err != nil {
panic(err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", "YOUR_API_KEY")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("Response Status:", resp.Status)
}
require 'net/http'
require 'json'
uri = URI('https://api.sendkit.ai/v1/campaigns/example_string/analytics?from=2024-05-01&to=2024-05-31')
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Get.new(uri)
request['Content-Type'] = 'application/json'
request['X-Api-Key'] = 'YOUR_API_KEY'
response = http.request(request)
puts response.body
{
"success": true,
"data": {
"campaign": {
"id": "665a1b2c3d4e5f6a7b8c9d0e",
"name": "Q1 Outreach Campaign",
"status": "active",
"startedAt": "2025-06-15T09:00:00.000Z"
},
"stats": {
"sent": 150,
"delivered": 145,
"opened": 72,
"clicked": 18,
"replied": 12,
"bounced": 5,
"unsubscribed": 2
},
"bySequence": {
"main": {
"sent": 130,
"opened": 60,
"clicked": 14,
"bounced": 4
},
"subsequence": {
"sent": 20,
"opened": 12,
"clicked": 4,
"bounced": 1
}
},
"stepAnalytics": [
{
"_id": {
"sequenceStep": 0,
"variant": null
},
"sent": 90,
"opened": 45,
"clicked": 11,
"bounced": 3
},
{
"_id": {
"sequenceStep": 2,
"variant": null
},
"sent": 40,
"opened": 15,
"clicked": 3,
"bounced": 1
}
],
"subsequences": [
{
"id": "665c1b2c3d4e5f6a7b8c9d20",
"name": "Interested follow-up",
"trigger": {
"type": "ai_tag",
"value": "interested"
},
"enabled": true,
"archived": false,
"addedCount": 18,
"currentlyEnrolled": 9,
"stats": {
"sent": 20,
"opened": 12,
"clicked": 4,
"bounced": 1,
"replied": 5
},
"stepStats": [
{
"_id": 0,
"sent": 14,
"opened": 9,
"clicked": 3,
"bounced": 1
},
{
"_id": 1,
"sent": 6,
"opened": 3,
"clicked": 1,
"bounced": 0
}
]
}
],
"statusBreakdown": {
"active": 350,
"completed": 100,
"bounced": 5,
"unsubscribed": 2,
"replied": 12,
"removed": 31
},
"totalReplied": 12
}
}
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Campaign not found"
}
}
/v1/campaigns/{campaignId}/analyticsTarget server for requests. Edit to use your own host.
Platform API key (sk_user_...) or Workspace API key (sk_...)
Campaign ID
Start date (ISO 8601 or date string). Omit for no lower bound.
End date (ISO 8601 or date string). Omit for no upper bound.
Request Preview
Response
Response will appear here after sending the request
Authentication
API Key for authentication. Platform API key (sk_user_...) or Workspace API key (sk_...)
Path Parameters
Campaign ID
Query Parameters
Responses
Overall campaign statistics
Send/open/click/bounce totals split by main vs subsequence emails. The stats field above is still combined (main + subseq); this is purely additive.
Aggregate email stats for a single sequence bucket (main or subsequence) within the requested date range.
Aggregate email stats for a single sequence bucket (main or subsequence) within the requested date range.
Per-step analytics for the main sequence only, grouped by (sequenceStep, variant). Top-level stats remains the combined main + subseq total. Per-subsequence breakdown is in subsequences[].stepStats.
One entry per subsequence configured on the campaign. Each entry has its own stats and step breakdown scoped to that subsequence.
Subsequence ID (the _id of the subdocument)
What causes a lead to enter this subsequence. With type='ai_tag', the
subsequence fires when the lead's AI tag (set by reply classification or
manual tagging) matches value (case-insensitive); manual tag changes via
the campaign-lead PATCH endpoint and the MCP tag_conversation tool also
enroll into matching subsequences. With type='reply', the subsequence
fires the first time the lead replies and value is ignored.
ai_tagreplyRequired when type is ai_tag. The AI tag value to match.
Total leads that have ever entered this subsequence
Number of leads currently in this subsequence with subsequence.status === "active". Always reflects current state, ignoring the date filter.
Aggregate email stats for this subsequence within the date filter (replied counts use subsequence.repliedAt).
Per-step breakdown within this subsequence. Step indices are local to the subsequence — step 0 is the subsequence's first email.
Zero-based sequence step index
Lead counts grouped by status
Total number of leads that replied (across main + subseq)
Campaign not found