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

Получение статистики через 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.label0issue.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.
  • Время в полях и константах задаётся по московскому часовому поясу.