Получение статистики через Sendsay API
Универсальная статистика stat.uni — основной инструмент для получения статистических данных из CDP Sendsay через API: переходы, открытия, тиражи выпусков, результаты доставки, стоп-листы, формы и другое.
Вызов stat.uni предназначен для выборок статистики по многим объектам, а не для отслеживания отдельных писем в реальном времени.
Структура запроса
{
"apikey": "ваш-api-ключ",
"action": "stat.uni",
"select": ["поле1", "поле2", "функция(поле3)"],
"filter": [{ "a": "поле", "op": "оператор", "v": "значение" }],
"order": ["поле1", "-поле2"],
"skip": 0,
"first": 100
}
Основные параметры
| Параметр | Описание |
|---|---|
select | Обязательно. Список полей и/или функций агрегирования |
filter | Условия фильтрации. Все элементы объединяются через AND |
have | Дополнительный ф ильтр после агрегирования (синтаксис как у filter) |
order | Сортировка. Префикс - — по убыванию, + или без префикса — по возрастанию |
skip | Пропустить N строк от начала (по умолчанию 0) |
first | Количество строк после пропуска (по умолчанию все) |
caption | Массив заголовков столбцов (для CSV/XLSX) |
result | Способ возврата результата |
Поля и функции в select
Простые поля
"select": ["issue.id", "issue.subject", "issue.dt", "issue.members"]
Поля дат с точностью
Поля с датами поддерживают указание точности в формате dt:HL, где H — старший компонент, L — младший (из набора Y, M, D, h, m, s):
"select": ["issue.dt:YM", "issue.dt:YD"]
Примеры:
dt:YM— год и месяц,dt:Yh— от года до часа,dt:YY— только год.
Специальные производные от даты:
dt:DOW(день недели, 1–7),dt:DOY(день года),dt:WOY(неделя года, форматYYYY-Ww),dt:CW1D(первый день недели).
Функции агрегирования
"select": ["deliv.issue.id", "count(*)", "max(deliv.letter.dt)"]
Доступные функции:
count(*),count(unique поле),max(),min(),avg(),sum(),range(),stdev(),variance().
Перед именем поля в функциях можно использовать префикс unique.
Поле не может одновременно быть в списке выборки и использоваться в функциях агрегирования. Функция sum() для полей, хранящих уникальные счётчики (например, issue.u_readed), вернёт "NaN".
Карта преобразования значений
Поле можно задать как объект с map для преобразования значений:
"select": [
{
"field": "deliv.result",
"map": { "1": "Доставлено", "0": "В процессе", "-1": "Ошибка" },
"map.missing": "Неизвестно"
}
]
Константы
"select": ["const(123)", "const(\"текст\")", "const.ID", "const.current"]
const.current — текущее время, поддерживает указание точности и сдвига, например, const.current:YM - 1 day.
Фильтры — filter
Все элементы filter объединяются через оператор AND.
Операторы сравнения
| Оператор | Описание |
|---|---|
== | Равно |
!= | Не равно |
>, >=, <, <= | Сравнение |
in | Входит в список значений ("v": [...]) |
!in | Не входит в список |
is_null | Значение не определено (только для полей, помеченных null) |
!is_null | Значение определено |
Простой фильтр
"filter": [
{ "a": "issue.id", "op": "==", "v": 12345 },
{ "a": "issue.dt:YD", "op": ">=", "v": "2026-01-01" }
]
Логические группировки
"filter": [
{
"op": "OR",
"v": [
{ "a": "issue.id", "op": "==", "v": 111 },
{ "a": "issue.id", "op": "==", "v": 222 }
]
}
]
Доступне логические операторы:
OR,!OR,AND,!AND.
Фильтрация по идентификаторам адресов
При фильтрации по полям, хранящим идентификатор адреса (email.email, member.email и др.), значения v приводятся к стандартному значению автоматически. Указывайте addr_type в условии, если вы работаете не только с email и msisdn — это поможет исключить ложные совпадения между разными типами идентификаторов.
Сравнение с текущим временем — current
Значения полей с датами можно сравнивать с текущим временем:
"filter": [
{ "a": "issue.dt", "op": ">=", "v": "current - 7 days" },
{ "a": "read.dt", "op": ">=", "v": "current - 2 hours round" }
]
Формат:
current +/-N year +/-N month +/-N day +/-N hour round +/-N minute round +/-N second
Порядок — от года к секунде. Слово round при часах/минутах обнуляет младшие компоненты.
Примеры:
current - 1 month— начало прошлого месяца.current - 1 day— вчера (начало дня).current - 2 hours round— начало часа 2 часа назад.current - 0 month + 4 days— 5-е число текущего месяца.
Результат current неявно приводится к точности поля в ключе a.
Дополнительный фильтр — have
Дополнительный фильтр have фильтрует результат после агрегирования. Синтаксис как у filter, но можно использовать только поля из select и функции агрегирования.
Пример для фильтрации выпусков после 1 января с более чем 10 кликами:
{
"select": ["click.issue.id", "count(*)"],
"filter": [{ "a": "click.issue.dt:YD", "op": ">", "v": "2026-01-01" }],
"have": [{ "a": "count(*)", "op": ">", "v": 10 }]
}
Сортировка — order
"order": ["-issue.dt", "+count(*)"]
Префикс - — по убыванию, + или без префикса — по возрастанию.
Объединение запросов — join
В одном запросе stat.uni нельзя обращаться к разным областям данных (например, одновременно к доставкам и открытиям). Чтобы получить в одной таблице данные из нескольких областей, используйте конструкцию join.
Параметр join — это массив запросов, каждый к своей области. Параметр joinby задаёт размер уникального ключа — количество первых колонок, по которым результаты объединяются в строки. 0 — без ключа, объединяются все колонки.
Например, нужно выгрузить все события (доставка, открытия, клики, отписки) получателей конкретного выпуска:
{
"action": "stat.uni",
"result": "save",
"joinby": 2,
"join": [
{
"select": ["deliv.member.id", "deliv.issue.id", "deliv.member.email", "deliv.status"],
"filter": [{ "a": "deliv.issue.id", "op": "==", "v": "777" }]
},
{
"select": ["read.member.id", "read.issue.id", "count(*)"],
"filter": [{ "a": "read.issue.id", "op": "==", "v": "777" }]
},
{
"select": ["click.member.id", "click.issue.id", "count(*)"],
"filter": [{ "a": "click.issue.id", "op": "==", "v": "777" }]
},
{
"select": ["unsub.member.id", "unsub.issue.id", "min(unsub.dt)"],
"filter": [{ "a": "unsub.issue.id", "op": "==", "v": "777" }]
}
]
}
В результате получится таблица: member.id, issue.id, member.email, статус доставки, количество открытий, количество кликов, дата первой отписки.
Уникальный ключ здесь — первые 2 колонки (member.id + issue.id). Email берётся только из первого запроса (к доставкам), чтобы не запрашивать его в каждом подзапросе — это уменьшает объём данных и ускоряет выполнение.
В запросах для открытий, кликов и отписок результат намеренно агрегирован (count(*), min()). Без агрегации, если для одного ключа встречается несколько строк, используется только первая, а остальные отбрасываются.
Сортировка в join задаётся по номерам колонок: "-1n" — первая колонка, числовая, по убыванию; "+2s" — вторая, строковая, по возрастанию.
Ответ и способы получения результата
Синхронный (по умолчанию)
Если параметр result не указан, результат возвращается сразу в ответе:
{
"list": [
["12345", "Привет, весна!", "2026-01-15 10:00:00"],
["12346", "Специальное предложение", "2026-01-20 12:00:00"]
]
}
Данные возвращаются в массиве list, каждая строка — массив значений в порядке, заданном в select.
Синхронный режим предназначен для интерактивной работы. При автоматизации запросы могут быть прекращены системой (например, по длительности выполнения).
Для автоматизации используйте асинхронные способы.
Асинхронный
При указании любого result, кроме response, вызов возвращает track.id для отслеживания через track.get:
{
"track.id": 12345
}
Способы получения результата — result
Параметр result — массив из одного или нескольких способов. Можно комбинировать, чтобы, например, сохранить данные в отчёты и отправить уведомление на почту.
| Способ | Описание |
|---|---|
response | Вернуть в ответе (синхронно, по умолчанию) |
save | Сохранить в хранилище отчётов (хранится 90 дней) |
email_file | Отправить файл на email |
url_file | Загрузить на внешний ресурс по HTTP(S) или FTP(S) |
email_notify | Отправить уведомление на email (без самого результата) |
sms | Отправить SMS-уведомление (без самого результата) |
url_notify | Вызвать URL по GET (без самого результата) |
none | Не возвращать результат (для пересчёта кэша) |
Пример: сохранить отчёт в XLSX и отправить уведомление на почту:
"result": [
{ "type": "save", "filename": "report-{DT:Y-M}", "format": "xlsx" },
{ "type": "email_notify", "to": ["manager@example.com"] }
]
Для форматов csv, xlsx, html, json доступны дополнительные настройки:
compress(сжатие:zip,gzip,bzip2),separator(разделитель для CSV),utf8(кодировка для CSV).
Доступные области данных (поля)
Каждая область данных (issue.*, deliv.*, click.*, read.* и т. д.) содержит свои поля и может включать поля из связанных областей. Например, в области deliv доступны все поля выпуска через deliv.issue.*, все поля контакта через deliv.member.* и т. д. В одном запросе используйте поля из одной области, обращаясь к связанным данным через вложенные поля.
Запись xxx.yyy.* означает, что доступны все поля объекта yyy. Если поле само является набором полей дру гого объекта, то доступны и они. Например, click.issue.group.name — название группы из выпуска, в котором был клик.
Информация об адресе — email.*
Информация о конкретном адресе доступна вне зависимости от наличия контакта в базе, даже если он был удалён.
| Поле | Описание |
|---|---|
email.id | ID адреса |
email.email | Значение идентификатора |
email.addr_type | Строка типа идентификатора |
email.domain.* | Информация о домене |
email.error.error | Количество ошибок доставки (null) |
email.error.dt | Дата последней ошибки (Ys) (null) |
email.error.str | Текст последней ошибки (null) |
email.error.lock | Адрес заблокирован из-за ошибок (0/1/null) |
Информация о контакте — member.*
Информация о контакте доступна только если он есть в базе.
| Поле | Описание |
|---|---|
member.id | ID контакта |
member.email | Значение идентификатора |
member.addr_type | Строка типа идентификатора |
member.haslock | Блокировка: 0 — нет, 1 — отписался, 2 — не подтверждён, 4 — ошибки доставки |
member.create.time | Дата создания (Ys) (null) |
member.update.time | Дата изменения (Ys) (null) |
member.import.time | Дата последнего импорта (Ys) (null) |
member.confirm.time | Дата подтверждения (Ys) (null) |
member.origin | Номер источника (null) |
member.datakey.КЛЮЧ | Значение ключа данных (только в select) |
member.anketa.АНКЕТА.ВОПРОС | Значение из анкеты (только в select) |
member.error.* | Ошибки доставки (аналогично email.error.*) |
При использовании поля member.haslock в запросе возвращаются только контакты, которые всё ещё есть в базе. Без haslock — все адреса, включая удалённых.
Информация о выпуске — issue.*
| Поле | Описание |
|---|---|
issue.id | ID выпуска |
issue.dt | Дата выпуска (Ys) |
issue.name | Название выпуска |
issue.subject | Тема письма |
issue.members | Число получателей |
issue.format | Формат: e — email, s — sms, p — push, k — vk, g — tg, n — vknotify, m — pushapp, x — max |
issue.from_name | Имя отправителя |
issue.from_email | Адрес отправителя |
issue.label0…issue.label9 | Метки выпуска |
issue.source | Источник: api, smtp, tranz |
issue.draft.id | Номер черновика (null) |
issue.draft.name | Название черновика (null) |
issue.group.* | Группа выпуска |
issue.sequence.id | Номер последовательности (null) |
issue.delivery_rate | Delivery Rate (null) |
issue.open_rate | Open Rate (null) |
issue.click_rate | Click Rate (null) |
issue.click_open_rate | Click to Open Rate (null) |
issue.unsub_rate | Unsub Rate (null) |
issue.deliv_ok | Количество доставленных |
issue.deliv_bad | Количество с ошибкой |
issue.clicked / issue.u_clicked | Клики / уникальные клики |
issue.readed / issue.u_readed | Чтения / уникальные чтения |
issue.unsubed | Количество отписок |
issue.hardbounce | Отсеяно из-за ошибок доставки |
issue.stoplist | Отсеяно из-за стоп-листа |
issue.hourly.* | Почасовая статистика (кэш, обновление раз в 20 мин) |
issue.daily.* | Статистика по дням/доменам (кэш) |
issue.stat.geo.* | География клико в и чтений |
Информация о доставке — deliv.*
| Поле | Описание |
|---|---|
deliv.member.* | Получатель |
deliv.issue.* | Выпуск |
deliv.letter.id | Номер письма в выпуске |
deliv.letter.dt | Дата последнего изменения статуса (Ys) (null) |
deliv.status | Код статуса: > 0 — доставлено, 0 — в процессе, < 0 — ошибка |
deliv.result | Результат: 1 — доставлено, 0 — в процессе, -1 — ошибка |
deliv.member.geo.* | География |
deliv.member.gadget.* | Устройство |
Вместо префикса deliv можно использовать:
deliv_ok(только доставленные),deliv_bad(только ошибки),deliv_unk(ещё доставляется).
Информация о кликах - click.*
| Поле | Описание |
|---|---|
click.dt | Дата и время клика (Ys) |
click.ip | IP кликнувшего |
click.source | 4 — из AMP-версии |
click.member.* | Кликнувший контакт |
click.issue.* | Выпуск |
click.link.url | URL ссылки |
click.link.reltype | Классификация: -1 — ссылка из выпуска, -2 — целевая страница |
click.member.geo.* | География |
click.member.gadget.* | Устройство |
Уточняйте click.link.reltype в фильтре, чтобы не получить и клики из выпуска, и посещения целевых страниц.
Информация об открытиях — read.*
| Поле | Описание |
|---|---|
read.dt | Дата и время чтения (Ys) |
read.ip | IP |
read.duration | Длительность в секундах (null, макс. 30 сек) |
read.source | 4 — из AMP-версии |
read.member.* | Читатель |
read.issue.* | Выпуск |
read.member.geo.* | География |
read.member.gadget.* | Устройство |
Информация об отписках — unsub.*
Записи в unsub.* сохраняются навсегда (в отличие от stoplist.*, где запись удаляется при выходе из стоп-листа).
| Поле | Описание |
|---|---|
unsub.dt | Дата отписки (Ys) |
unsub.why | Способ отписки |
unsub.reason | Причина от получателя (null) |
unsub.label | Ме тка из ссылки отписки (null) |
unsub.sender.email | Адрес отправителя, null — глобальная отписка |
unsub.member.* | Отписавшийся |
unsub.issue.* | Выпуск (null) |
Стоп-лист — stoplist.*
| Поле | Описание |
|---|---|
stoplist.dt | Дата записи (Ys) |
stoplist.type | А — владельцем, М — получателем |
stoplist.email | Адрес |
stoplist.sender.email | Адрес отправителя, null — глобальная запись |
stoplist.why | Способ отписки: 1 — спам, 2 — ссылка, 3 — поддержка, 4 — кнопка отписки, 5 — тематическая |
stoplist.reason | Причина (null) |
stoplist.issue.* | Выпуск (null) |
Другие области данных
| Область | Описание |
|---|---|
group.* | Гру ппы: group.id, group.gid, group.name, group.type, group.stat.* |
domain.* | Домены: domain.id, domain.name |
link.* | Ссылки: link.id, link.url, link.reltype |
linkgroup.* | Группы ссылок |
campaign.* | Кампании: campaign.id, campaign.name, campaign.stat.* |
sequence.* / sequence_progress.* | Последовательности и прохождения |
draft.* | Черновики: draft.id, draft.name, draft.alias, draft.format |
form.* / formfilling.* | Формы и лог заполнений |
custid.* | Клиентские метки писем |
promocode.* | Выданные промо-коды |
datarow.* | Ряды данных |
origin.* | Источники |
stat.common.* | Общая информация об аккаунте по дням |
pase.doc.* | Бухгалтерские документы |
Примеры
Список выпусков за месяц
{
"action": "stat.uni",
"select": [
"issue.id",
"issue.subject",
"issue.dt:YD",
"issue.members",
"issue.deliv_ok",
"issue.u_readed",
"issue.open_rate",
"issue.click_rate"
],
"filter": [{ "a": "issue.dt:YM", "op": "==", "v": "2026-01" }],
"order": ["-issue.dt"]
}
Статистика доставки по выпуску
{
"action": "stat.uni",
"select": ["deliv.member.email", "deliv.status", "deliv.letter.dt"],
"filter": [{ "a": "deliv.issue.id", "op": "==", "v": 12345 }],
"first": 100
}
Выпуски за прошлый месяц (быстрый вариант)
{
"action": "stat.uni",
"select": ["issue.id", "issue.subject", "issue.members"],
"filter": [{ "a": "issue.dt:YM", "op": "==", "v": "current - 1 month" }]
}
Клики по ссылкам из выпуска
{
"action": "stat.uni",
"select": ["click.link.url", "count(*)", "count(unique click.member.email)"],
"filter": [
{ "a": "click.issue.id", "op": "==", "v": 12345 },
{ "a": "click.link.reltype", "op": "==", "v": -1 }
],
"order": ["-count(*)"]
}
Записи стоп-листа за период
{
"action": "stat.uni",
"select": ["stoplist.email", "stoplist.dt:YD", "stoplist.type", "stoplist.why"],
"filter": [{ "a": "stoplist.dt:YM", "op": ">=", "v": "2026-01" }],
"order": ["-stoplist.dt"]
}
Данные контакта через stat.uni
{
"action": "stat.uni",
"select": [
"member.email",
"member.haslock",
"member.create.time",
"member.datakey.base.firstName",
"member.datakey.base.city"
],
"filter": [{ "a": "member.email", "op": "==", "v": "anna@example.com" }]
}
Кэширование
При использовании параметра cache запоминаются сами данные. Способ возврата (result, caption) применяется позже. Режим fetch возвращает данные из кэша без пересчёта.