Перейти к основному содержимому
Опубликовано: 

Получение статистики через 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.idID адреса
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.idID контакта
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.idID выпуска
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_rateDelivery Rate (null)
issue.open_rateOpen Rate (null)
issue.click_rateClick Rate (null)
issue.click_open_rateClick to Open Rate (null)
issue.unsub_rateUnsub 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.ipIP кликнувшего
click.source4 — из AMP-версии
click.member.*Кликнувший контакт
click.issue.*Выпуск
click.link.urlURL ссылки
click.link.reltypeКлассификация: -1 — ссылка из выпуска, -2 — целевая страница
click.member.geo.*География
click.member.gadget.*Устройство
Важно

Уточняйте click.link.reltype в фильтре, чтобы не получить и клики из выпуска, и посещения целевых страниц.

Информация об открытиях — read.*​

ПолеОписание
read.dtДата и время чтения (Ys)
read.ipIP
read.durationДлительность в секундах (null, макс. 30 сек)
read.source4 — из 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 возвращает данные из кэша без пересчёта.

Использование stat.uni в объединённых запросах​

Чтобы получить данные из нескольких областей в одном запросе stat.uni (например, данные о доставке, открытиях и кликах) используйте параметр join.

Параметр join содержит массив подзапросов. Каждый из них получает данные из своей области статистики. Дополнительный параметр joinby задаёт количество полей, которые образуют ключ объединения результатов.

Пример:

{
"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"
}
]
}
]
}

В этом примере joinby: 2 означает, что строки объединяются по двум полям — member.id и issue.id. В результате выполнения запроса формируется таблица с полями:

"member.id",
"issue.id",
"member.email",
"статус доставки"
"количество открытий"
"количество кликов"
"первая дата отписки"

Особенности получения статистики через Sendsay API​

  • stat.uni — мощный инструмент с очень большим набором полей. Полный список доступен в документации API.
  • Для больших выборок используйте параметр result для асинхронного получения.
  • Суммарная статистика по выпуску (issue.deliv_ok, issue.u_readed и др.) — это кэш, обновляемый раз в 15 минут (первые 5 дней) или раз в 3 часа (далее).
  • Не все поля можно комбинировать в одном запросе. Используйте join для объединения результатов из разных областей.
  • Обработку асинхронного запроса можно прекратить вызовом track.set.
  • Статистика по выпускам Stream API также доступна через stat.uni.
  • Время в полях и константах задаётся по московскому часовому поясу.