Get Campaign Stats
Retrieve delivery, engagement and conversion stats for a push campaign.
Totals are lifetime. Unique counts can lag event counts by up to 15 minutes. Before the campaign is sent, counts are 0 and rates are null.
Delivered means accepted
delivered counts notifications accepted by APNs, FCM or Web Push, not arrival on the device. confirmed, displayed, suppressed, opens, clicks and dismissals come from the Client SDK and stay at 0 for an app that does not report them.
Endpoint
GET /push/campaigns/:id/stats
Returns: 200 OK
Headers
| Header | Value | Required |
|---|---|---|
Authorization | Bearer <your-api-key> | Yes |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | The campaign ID. |
Response
{
"object": "push_campaign_stats",
"campaign_id": "0192a1b2-c3d4-7e5f-9000-abcdef123456",
"recipients": 8400,
"devices": 9150,
"attempts": 9150,
"delivered": 8890,
"confirmed": 6120,
"displayed": 5980,
"suppressed": 140,
"opens": 1310,
"clicks": 420,
"dismissals": 260,
"failures": 75,
"token_invalidations": 185,
"bot_opens": 12,
"bot_clicks": 3,
"unique_delivered": 8200,
"unique_displayed": 5600,
"unique_opens": 1180,
"unique_clicks": 390,
"delivery_rate": 0.9716,
"open_rate": 0.1439,
"click_rate": 0.0476,
"smart_sending": { "enabled": true, "skipped": 25 },
"conversions": {
"enabled": true,
"tracking_since": "2026-09-01T00:00:00.000Z",
"total": 42,
"revenue": [{ "currency": "SAR", "amount": 18350 }],
"by_event": [
{
"event_id": "0192a1b2-eeee-7fff-8000-111111111111",
"event_name": "order_completed",
"tracks_revenue": true,
"conversions": 42,
"revenue": [{ "currency": "SAR", "amount": 18350 }]
}
]
}
}
Response fields
| Field | Type | Description |
|---|---|---|
recipients | number | Contacts sent to. Before sending, the contacts currently reachable. |
devices | number | Devices behind recipients |
attempts | number | delivered + failures + token_invalidations |
delivered | number | Accepted by APNs, FCM or Web Push |
confirmed | number | Device reported receiving it. A floor: not every device reports. |
displayed | number | Device reported showing it |
suppressed | number | Received but not shown, for example because permission was revoked |
opens | number | Total opens |
clicks | number | Total clicks |
dismissals | number | Dismissed without being opened |
failures | number | Refused by the push service |
token_invalidations | number | Refused because the device token was no longer valid |
bot_opens | number | Opens that failed signature verification. Not counted in opens. |
bot_clicks | number | Clicks that failed signature verification. Not counted in clicks. |
unique_delivered | number | Contacts with a delivered notification |
unique_displayed | number | Contacts with a displayed notification |
unique_opens | number | Contacts who opened |
unique_clicks | number | Contacts who clicked |
delivery_rate | number | null | delivered / attempts |
open_rate | number | null | unique_opens / unique_delivered |
click_rate | number | null | unique_clicks / unique_delivered |
smart_sending.enabled | boolean | Smart Sending was on when the campaign was sent |
smart_sending.skipped | number | Recipients skipped because they had reached the sending cap |
conversions.enabled | boolean | false when the organization has no conversion events |
conversions.tracking_since | string | null | When the first conversion event was enabled (ISO 8601) |
conversions.total | number | Conversions attributed to the campaign |
conversions.revenue | array | Attributed revenue as { currency, amount }, one entry per currency |
conversions.by_event | array | The same breakdown per conversion event |
Rates are fractions from 0 to 1, rounded to four decimals, and null when the denominator is 0.
Examples
- cURL
- JavaScript
- Python
- C#
- PHP
curl "https://api.arsel.sa/v1/push/campaigns/0192a1b2-c3d4-7e5f-9000-abcdef123456/stats" \
-H "Authorization: Bearer $ARSEL_API_KEY"
const response = await fetch("https://api.arsel.sa/v1/push/campaigns/0192a1b2-c3d4-7e5f-9000-abcdef123456/stats", {
headers: {
Authorization: `Bearer ${process.env.ARSEL_API_KEY}`,
},
});
const data = await response.json();
if (!response.ok) {
throw new Error(`${data.name}: ${data.message}`);
}
console.log(data);
import os
import requests
response = requests.get(
"https://api.arsel.sa/v1/push/campaigns/0192a1b2-c3d4-7e5f-9000-abcdef123456/stats",
headers={"Authorization": f"Bearer {os.environ['ARSEL_API_KEY']}"},
)
response.raise_for_status()
print(response.json())
using System.Net.Http.Headers;
var client = new HttpClient(); // share one instance across requests
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", Environment.GetEnvironmentVariable("ARSEL_API_KEY"));
var response = await client.GetAsync("https://api.arsel.sa/v1/push/campaigns/0192a1b2-c3d4-7e5f-9000-abcdef123456/stats");
var body = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException($"{(int)response.StatusCode}: {body}");
Console.WriteLine(body);
<?php
$ch = curl_init("https://api.arsel.sa/v1/push/campaigns/0192a1b2-c3d4-7e5f-9000-abcdef123456/stats");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("ARSEL_API_KEY"),
],
]);
$data = json_decode(curl_exec($ch), true);
if (curl_getinfo($ch, CURLINFO_HTTP_CODE) >= 400) {
throw new Exception("{$data['name']}: {$data['message']}");
}
print_r($data);
Error Responses
- 404 Not Found
- 401 Unauthorized
{
"status_code": 404,
"name": "not_found",
"message": "Campaign not found or access denied"
}
{
"status_code": 401,
"name": "unauthorized",
"message": "Invalid API Key"
}