curl --request GET \
--url https://api.inflection.io/v2/emails/stats \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.inflection.io/v2/emails/stats"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.inflection.io/v2/emails/stats', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.inflection.io/v2/emails/stats",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.inflection.io/v2/emails/stats"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.inflection.io/v2/emails/stats")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.inflection.io/v2/emails/stats")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"aggregate": {
"totalCount": 123,
"notSentCount": 123,
"processedCount": 123,
"deliveredCount": 123,
"droppedCount": 123,
"bounceCount": 123,
"spamreportCount": 123,
"totalOpenCount": 123,
"uniqueOpenCount": 123,
"totalClickCount": 123,
"uniqueClickCountByUrl": 123,
"uniqueClickCountByEmail": 123,
"unsubCount": 123
},
"records": [
{
"templateId": "<string>",
"totalCount": 123,
"notSentCount": 123,
"processedCount": 123,
"deliveredCount": 123,
"droppedCount": 123,
"bounceCount": 123,
"spamreportCount": 123,
"totalOpenCount": 123,
"uniqueOpenCount": 123,
"totalClickCount": 123,
"uniqueClickCountByUrl": 123,
"uniqueClickCountByEmail": 123,
"unsubCount": 123
}
]
},
"pagination": {
"pageNumber": 123,
"pageSize": 123,
"totalElements": 123,
"totalPages": 123
},
"meta": {
"status": "SUCCESS",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"errors": [
{
"errorCode": "NOT_FOUND",
"message": "<string>",
"detail": "<string>"
}
],
"meta": {
"status": "SUCCESS",
"timestamp": "2023-11-07T05:31:56Z"
}
}Email stats rollup
Counters for many emails in one call. An email reused across journeys is reported once here, on its own totals. Per-journey stats cannot answer “how is this email performing” when the caller does not know which journeys send it.
data.aggregate is a single ungrouped query over the whole matching
set, so it is not the sum of data.records. Those cover only the
current page.
It is also not the sum of every page. A send that carries two
different templates counts once in data.aggregate but once per template
in data.records, so summing all pages can exceed the aggregate by a
small margin (1 in 27,613 on our staging data). The aggregate is the
de-duplicated figure; prefer it over adding the rows up.
Emails are matched on send activity. An email with opens or clicks but no send record is not reported, and its engagement is not in the aggregate either.
At most one of ids, folderId may be given; both is a 400.
Explicit ids return a full row of zeros for an email with no activity,
whereas a folderId/unfiltered request returns only emails that have
activity. A filter matching more than 1000 emails is a 400
(RESULT_SET_TOO_LARGE). Nothing is silently trimmed. That guard cannot
trigger when ids is used, since ids is itself capped at 50.
An email whose template record no longer exists is still reported when it
has send activity. The sends happened, and the tracking is the record of
them. Those ids are therefore reported here but return 404 from
GET /v2/emails/{id}/stats. This is deliberately unlike the journey
rollup, which reports only live journeys.
curl --request GET \
--url https://api.inflection.io/v2/emails/stats \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.inflection.io/v2/emails/stats"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.inflection.io/v2/emails/stats', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.inflection.io/v2/emails/stats",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.inflection.io/v2/emails/stats"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.inflection.io/v2/emails/stats")
.header("Authorization", "Bearer <token>")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.inflection.io/v2/emails/stats")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body{
"data": {
"aggregate": {
"totalCount": 123,
"notSentCount": 123,
"processedCount": 123,
"deliveredCount": 123,
"droppedCount": 123,
"bounceCount": 123,
"spamreportCount": 123,
"totalOpenCount": 123,
"uniqueOpenCount": 123,
"totalClickCount": 123,
"uniqueClickCountByUrl": 123,
"uniqueClickCountByEmail": 123,
"unsubCount": 123
},
"records": [
{
"templateId": "<string>",
"totalCount": 123,
"notSentCount": 123,
"processedCount": 123,
"deliveredCount": 123,
"droppedCount": 123,
"bounceCount": 123,
"spamreportCount": 123,
"totalOpenCount": 123,
"uniqueOpenCount": 123,
"totalClickCount": 123,
"uniqueClickCountByUrl": 123,
"uniqueClickCountByEmail": 123,
"unsubCount": 123
}
]
},
"pagination": {
"pageNumber": 123,
"pageSize": 123,
"totalElements": 123,
"totalPages": 123
},
"meta": {
"status": "SUCCESS",
"timestamp": "2023-11-07T05:31:56Z"
}
}{
"errors": [
{
"errorCode": "NOT_FOUND",
"message": "<string>",
"detail": "<string>"
}
],
"meta": {
"status": "SUCCESS",
"timestamp": "2023-11-07T05:31:56Z"
}
}Authorizations
Personal Access Token or OAuth 2.1 access token, sent as a bearer
credential (Authorization: Bearer inf_pat_...). Read operations
require a token with the READ scope; write operations
(POST/PATCH/DELETE) require WRITE.
Query Parameters
Email ids to report on, at most 50 (a URL-length limit). Mutually
exclusive with folderId.
50Restrict to one folder. An id matching no folder yields an empty page rather than an error.
ISO-8601 lower bound on the send time, inclusive. A date
(2026-01-01), a Z offset and a ±HH:MM offset are all accepted and
normalised to UTC; a value carrying no offset is read in the server's
zone. Omit for all time.
ISO-8601 upper bound on the send time, inclusive. Same accepted forms as
since.
1-based page number.
1 <= x <= 2147483647Items per page.
1 <= x <= 200Was this page helpful?