API
API v1 ДАРА АНАЛИТИКИ — ключи и общие правила, конверсии и заказы с сервера, отчёты, атрибуты и данные посетителей. Примеры на curl, PHP и Python.
API нужен, чтобы передавать данные с сервера сайта и забирать отчёты в свои системы. Он работает только сервер-сервер: ключ API хранится на вашем сервере и в браузер не попадает.
Здесь — общие правила, методы и примеры. Полное описание методов и полей запросов — машинное: OpenAPI по адресу GET /api/v1/openapi.json, без авторизации. Его понимают генераторы клиентов и просмотрщики API, и оно всегда совпадает с тем, что сервис принимает на самом деле. Ключ API описан в нём схемой безопасности bearer, поэтому клиент, собранный генератором, авторизуется сам. Схемы ответов и ошибок в формате {"error": {"code", "message", "details"}} описаны у каждого метода — от POST /visitors/attributes до POST /reports; сами коды ошибок и пределы — в разделе Ошибки и лимиты.
Ключ API
Ключей два вида, и возможности у них одинаковые — отличаются они только тем, на что выписаны:
| Вид ключа | Строка | На что выписан | Какой сайт отвечает |
|---|---|---|---|
| ключ сайта | ak_ и 40 букв и цифр |
один сайт | тот, которому выдан ключ |
| ключ аккаунта | aa_ и 40 букв и цифр |
все сайты вашего аккаунта | тот, который назван заголовком X-Dara-Site |
- Ключ сайта создаётся в настройках сайта, раздел «Ключи API»; ключ аккаунта — в кабинете, раздел «Ключи аккаунта».
- Полный ключ показывается один раз — сохраните его в секретах сервера. В списке ключей потом видны только имя, префикс, дата создания и дата последнего использования.
- Потерянный или раскрытый ключ отзовите и создайте новый. Отзыв ключа аккаунта закрывает доступ ко всем сайтам сразу.
В примерах вместо ключа заглушка ak_XXXXXXXX…. Приставки разные нарочно: по одной строке видно, один сайт она открывает или все сайты аккаунта. Прав и сроков жизни у ключей нет: любой ключ делает всё, что умеет API, и работает, пока его не отзовут. Кнопка «Выйти на всех устройствах» в кабинете ключи API не отзывает.
Агентству и тому, у кого много сайтов, удобнее один ключ аккаунта: заводить и менять сотню ключей на сотню сайтов не нужно, а какой сайт имеется в виду — говорит заголовок каждого запроса.
Ключ API — не то же самое, что ключ сайта as_… в коде тега: ключ сайта публичный и для API не подходит.
Этим же ключом сайт аналитики подключается к проекту рекомендаций ДАРА: ключ выпускаете вы и вводите его в настройках своего проекта core — Подключение к ДАРА. Для этого подключения держите отдельный ключ сайта ak_…: core принимает только такой ключ и ключ аккаунта отклоняет своей же проверкой формы, а лимиты частоты считаются на ключ — частый опрос API вашими же скриптами не должен мешать рекомендациям.
Общие правила
| Что | Как |
|---|---|
| Адрес | https://analytics.daratech.ru/api/v1, только HTTPS |
| Ключ | заголовок Authorization: Bearer ak_XXXXXXXX…; в адресе и параметрах ключ не принимается |
| Сайт запроса | ключу аккаунта — заголовок X-Dara-Site с идентификатором сайта; ключу сайта заголовок не нужен |
| Формат | JSON в UTF-8, заголовок Content-Type: application/json |
| Время | ISO-8601 с Z: 2026-09-14T12:00:00Z |
| Размер тела | до 1 МБ |
| Ответы | с заголовками Cache-Control: no-store и X-Request-ID |
| Браузер | CORS-заголовков нет: запросы из браузера не пройдут |
| Персональные данные | API не отдаёт их ни в одном ответе |
- Отозванный ключ — ответ
401. Заблокированный или удалённый сайт, заблокированный владелец —403. X-Request-IDпишите в свои логи: по нему можно найти запрос при разборе.- Коды ошибок и лимиты запросов — в разделе Ошибки и лимиты.
Сайт запроса
Ключ аккаунта сам по себе не говорит, про какой сайт вы спрашиваете, поэтому сайт называется заголовком:
X-Dara-Site: 7b3c1e42-9f58-4d0a-8b61-2c4e5f7a9d13
Идентификатор сайта — тот же, что в адресе его страницы в кабинете (/cabinet/sites/7b3c1e42-…). Из кода его отдают GET /sites (все сайты аккаунта) и GET /site (сайт запроса), поэтому заводить его копию в своих настройках не нужно. Номер сайта, его название и ключ сайта as_… в этом заголовке не принимаются.
| Что вы прислали | Ответ |
|---|---|
| ключ аккаунта без заголовка, а методу нужен сайт | 422, в details — поле X-Dara-Site. Сервис не угадывает сайт и не берёт первый из ваших |
| ключ аккаунта с заголовком на ваш сайт | обычный ответ метода — про этот сайт |
| ключ сайта с заголовком на его же сайт | обычный ответ: один и тот же код работает с ключом любого вида |
| ключ сайта с заголовком на другой ваш сайт | 403: ключ сайта распоряжается только своим сайтом. Заголовок не игнорируется молча |
| заголовок на сайт, которого нет или который не ваш | 404 — одинаковый ответ в обоих случаях |
| заголовок дважды в одном запросе | 422: сайт запроса должен быть один |
Заголовок понимают все методы. GET /meta, GET /network и GET /categories отвечают одинаково для всех сайтов, поэтому им сайт не нужен: ключ аккаунта работает с ними и без заголовка.
curl https://analytics.daratech.ru/api/v1/goals \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "X-Dara-Site: 7b3c1e42-9f58-4d0a-8b61-2c4e5f7a9d13"
Лимиты частоты считаются на ключ, а не на сайт, — держите это в виду, когда обходите свои сайты в несколько потоков: числа пределов для каждого вида ключа собраны в разделе Ошибки и лимиты.
Страницы списков
Списки объектов отдаются страницами и обходятся курсором, а не смещением. Правила одни у всех таких методов:
curl https://analytics.daratech.ru/api/v1/sites?limit=100 \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
В ответе рядом со списком приходят next_cursor и total. Чтобы получить следующую страницу, передайте next_cursor параметром cursor; next_cursor: null значит, что строк больше нет и ходить ещё раз незачем.
| Параметр | Что делает |
|---|---|
limit |
сколько строк отдать за раз: от 1 до 200, по умолчанию 100 |
cursor |
значение next_cursor предыдущего ответа того же метода с теми же параметрами отбора |
- Курсор непрозрачен: это строка, которую разбирать не нужно и не получится. Её формат может измениться, поэтому сохранять курсор надолго не стоит — он нужен только на время обхода.
- Обход устойчив к изменениям: если во время обхода что-то добавили, оно встретится в конце; если удалили — просто не встретится. Ни одна оставшаяся строка не повторится и не потеряется. Поэтому
total— число строк на момент запроса, а не обещание, что столько и придёт. - Курсор принадлежит одному списку и одному отбору. Курсор от другого метода, от другого сайта или от другого значения
status—422с полемcursor: начните обход заново.
Идентификаторы
| Идентификатор | Что это | Формат |
|---|---|---|
visitor_id |
посетитель сайта — значение cookie _dara_a; в браузере его отдаёт команда dara('ready') |
UUID |
user_id |
id пользователя на вашем сайте, переданный в аналитику командой dara('user', { user_id: '…' }) после входа или методом POST /users |
строка до 191 символа Unicode |
fingerprint_uuid |
id устройства DARA Fingerprint — только если он подключён к сайту, см. DARA Fingerprint | UUID |
На сервере visitor_id удобно брать из cookie _dara_a запроса, в котором посетитель оформил заказ или заявку, — только если значение похоже на UUID. Сохраните его вместе с заказом: отправка в аналитику обычно идёт позже, из очереди.
Чтобы связать посетителя с пользователем сайта, после входа вызовите на странице:
dara('user', { user_id: 'u-501' });
Методы
| Метод | Назначение |
|---|---|
POST /conversions |
конверсии JS-целей с сервера |
GET /goals |
цели сайта: коды, названия, типы, условия |
POST /goals |
завести цель |
PATCH /goals/{code} |
изменить цель: название, ценность, условия, включение |
POST /orders |
заказы и возвраты с сервера |
POST /reports |
отчёт по измерениям и метрикам |
POST /reports/async |
тот же отчёт заданием очереди, без ожидания ответа |
POST /audiences/export |
выгрузка аудитории сегмента в ваших идентификаторах — заданием очереди |
GET /jobs/{job_id} |
состояние задания очереди, а у готового — и его результат |
GET /segments |
сегменты сайта: названия и условия |
POST /segments |
завести сегмент |
PATCH /segments/{segment_id} |
изменить сегмент: название, условия |
DELETE /segments/{segment_id} |
удалить сегмент |
GET /funnels |
воронки сайта: названия, окно, порядок и шаги |
POST /funnels |
завести воронку |
PATCH /funnels/{funnel_id} |
изменить воронку |
DELETE /funnels/{funnel_id} |
удалить воронку |
POST /funnels/{funnel_id}/report |
посчитать воронку за период: посетители на каждом шаге, конверсия, отток |
GET /categories |
справочник категорий сети: номер, путь и название |
GET /category-mappings |
категории товаров сайта и их категории в справочнике |
POST /category-mappings |
сопоставить категорию товара с категорией справочника |
PATCH /category-mappings/{category_hash} |
сменить категорию справочника у известной строки |
DELETE /category-mappings/{category_hash} |
снять ручное сопоставление |
GET /ad-costs |
расходы на рекламу за период: день, UTM-метки, сумма |
POST /ad-costs |
записать расходы на рекламу пачкой до 1000 строк |
DELETE /ad-costs |
удалить расходы за период |
GET /sites |
ваши сайты и те, где вы администратор по приглашению: идентификаторы, ключи тега, домены, состояние — только ключом аккаунта |
POST /sites |
завести сайт — только ключом аккаунта |
PATCH /sites/{site_uuid} |
изменить название и домены сайта — только ключом аккаунта |
DELETE /sites/{site_uuid} |
удалить сайт (обратимо 30 суток) — только ключом аккаунта |
POST /sites/{site_uuid}/restore |
восстановить удалённый сайт — только ключом аккаунта |
GET /meta |
каталог измерений и метрик: идентификаторы, названия, группы, подписи значений, пределы запроса |
GET /site |
сайт запроса: пояс, валюта, суточная квота и её расход, срок хранения, участие в сети |
POST /visitors/attributes |
атрибуты до 100 посетителей: пол, возрастная группа, география, дети, компания, интересы, категории покупок, параметры — API: посетители |
GET /visitors/{visitor_id} |
атрибуты одного посетителя, его первый и последний визит и число визитов — API: посетители |
GET /network |
настройки сети: передаются ли чувствительные темы — API: посетители |
POST /users |
данные сайта о посетителях: user_id, email, телефон, пол, возраст, город, дети, интересы, параметры — Данные о посетителях |
POST /mcp |
MCP-сервер для ИИ-ассистентов: отчёты, воронки и описание сайта в разговоре — только чтение (раздел «MCP» ниже) |
GET /openapi.json |
описание API в формате OpenAPI, без авторизации |
GET /health |
состояние сервиса, отставание очереди приёма и поток, который ближе всех к своему порогу, — без авторизации |
GET /health всегда отвечает 200, а состояние — в теле: {"status": "ok", "checks": {"mariadb": "ok", "clickhouse": "ok", "spool_lag_s": 2.1, "spool_lag_stream": "hits"}}. status — ok или degraded (degraded — сервис принимает данные, но что-то работает не так). spool_lag_s — наибольшее отставание очереди приёма по всем её потокам в секундах или null, если оно сейчас неизвестно; spool_lag_stream — поток, ближе всех подошедший к своему порогу (при degraded — виновник состояния). У разных потоков нормальное отставание разное, поэтому эти два поля могут относиться к разным потокам: число — у самого отставшего потока, имя — у самого тревожного, и судить о сервисе по одному числу нельзя. Ключ методу не нужен, поля в ответе со временем могут добавляться.
Конверсии
POST /conversions записывает достижение цели, которое подтвердилось на сервере: оплату, звонок из колл-центра, сделку в CRM.
export DARA_ANALYTICS_KEY='ak_XXXXXXXX…'
curl -X POST https://analytics.daratech.ru/api/v1/conversions \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{
"conversions": [
{"id": "deal-7731", "goal": "crm_deal_won", "user_id": "u-501", "occurred_at": "2026-09-14T15:20:00Z", "value": 12000},
{"id": "deal-7732", "goal": "crm_deal_won", "visitor_id": "0b7e6f8a-3c2d-4e5f-9a1b-2c3d4e5f6a7b"}
]
}'
Ответ 202 Accepted:
{"accepted": 1, "duplicates": 0, "rejected": [{"index": 1, "code": "unknown_visitor"}]}
- В одном запросе — до 1000 конверсий.
id— ваш id конверсии, до 64 символов ASCII, уникален в пределах сайта. Повторно присланная конверсия не учитывается и попадает вduplicates, поэтому пачку можно безопасно отправить ещё раз.goal— код цели типа «JS-цель».visitor_idилиuser_id— кто достиг цели,occurred_at— когда,value— ценность достижения: она важнее фиксированной суммы из настроек цели. Безoccurred_atберётся время приёма запроса; время из будущего заменяется временем приёма.- Конверсия записывается в последний визит посетителя до
occurred_at, не старше 90 дней. Если посетитель известен только поuser_id, берётся последний визит связанного с ним посетителя. - Коды отказа:
unknown_goal— у сайта нет включённой цели типа «JS-цель» с таким кодом,unknown_visitor— посетитель не найден,too_old— подходящий визит старше 90 дней.
Цели
Конверсия передаётся по коду цели, и этот код можно узнать и завести тем же ключом, не заходя в кабинет: GET /goals показывает цели сайта, POST /goals создаёт цель, PATCH /goals/{code} меняет её. Типы целей, их условия и правила подсчёта — в разделе Цели.
curl https://analytics.daratech.ru/api/v1/goals \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
Ответ:
{"goals": [{"code": "crm_deal_won", "public_id": "3f2a1b7c-4d5e-4f60-8a1b-2c3d4e5f6a7b", "name": "Сделка в CRM",
"type": "js", "checked_in": "tag", "is_active": true, "accepts_conversions": true, "value": 12000.0,
"conditions": {"v": 1}, "created_at": "2026-09-14T12:00:00Z", "updated_at": "2026-09-14T12:00:00Z"}],
"total": 1, "next_cursor": null}
code— код цели: его принимаетPOST /conversions, им же цель называется в метриках отчётов.public_id— постоянный идентификатор цели: код в кабинете сменить можно,public_id— нет.accepts_conversions— примет лиPOST /conversionsкод этой цели. С сервера засчитываются достижения только включённых целей типа «JS-цель»: остальные типы считает сам тег или отчёт по сохранённым хитам, и присланное достижение такой цели ничего бы не изменило.checked_in— где проверяется цель:tag— тег присылает достижение сам,report— цель считается по хитам в момент запроса отчёта.conditions— условия по типу цели в той же форме, которую принимаетPOST /goals: цель можно прочитать у одного сайта и такой же создать у другого. У составной цели шаги названы кодами целей-шагов.- Отключённые цели тоже в ответе, с
is_active: false. Целей у сайта не больше 200, поэтому без параметраlimitприходит весь каталог, аnext_cursorв ответе —null. Меньшийlimitвключает страницы — правила в разделе «Страницы списков» выше. - Код цели нужен не только
POST /conversions: им называются метрики отчётовgoal_reaches:{код}иconversion:{код}— их описание в разделе «Каталог измерений и метрик».
Новая цель — код, название, тип и условия по типу:
curl -X POST https://analytics.daratech.ru/api/v1/goals \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"code": "crm_deal_won", "name": "Сделка в CRM", "type": "js", "value": 12000}'
curl -X POST https://analytics.daratech.ru/api/v1/goals \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"code": "price_page", "name": "Страница цен", "type": "url",
"conditions": {"match": [{"op": "contains", "value": "/price"}]}}'
Ответ 201 Created — созданная цель в поле goal:
{"goal": {"code": "crm_deal_won", "public_id": "3f2a1b7c-4d5e-4f60-8a1b-2c3d4e5f6a7b", "name": "Сделка в CRM",
"type": "js", "checked_in": "tag", "is_active": true, "accepts_conversions": true, "value": 12000.0,
"conditions": {"v": 1}, "created_at": "2026-09-14T12:00:00Z", "updated_at": "2026-09-14T12:00:00Z"}}
- Обязательные поля —
code,nameиtype.conditionsзависят от типа цели (у «JS-цели» и «Покупки» условий нет),value— фиксированная ценность достижения,is_active— по умолчаниюtrue. Условия и текст ошибок — те же, что в форме кабинета. - Повтор того же запроса безопасен: если такая цель уже есть и совпадает во всём, ответ —
200и та же цель, второй цели не появляется. Если цель с этим кодом другая, ответ —422с полемcode: посмотритеGET /goalsи меняйте существующую цель методомPATCH. - Предел 200 целей на сайт —
422с кодомtoo_manyу поляgoals. Неверный код, неизвестный тип или условия не по типу —422, а какие именно поля неверны, написано вdetails. - Лишнее поле в теле — тоже
422с кодомunknown_field: в методе управления опечатка в имени поля опаснее отказа.
Правка цели меняет только переданные поля:
curl -X PATCH https://analytics.daratech.ru/api/v1/goals/crm_deal_won \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"value": 15000, "is_active": false}'
- Меняются
name,value,conditionsиis_active;value: nullубирает фиксированную ценность. После правки отчёты за прошлые дни пересчитываются. - Код и тип цели через API не меняются: смена кода ломает интеграцию, которая этим кодом шлёт конверсии, а смена типа меняет способ подсчёта уже собранных достижений. И то и другое делается в кабинете.
- Цели не удаляются ни в кабинете, ни через API: ненужную цель отключают —
{"is_active": false}. - Нет цели с таким кодом —
404. Код, который сам выглядит ключом API (ak_и 40 знаков), в адресе не принимается: такой адрес отклоняет защита «ключ API передаётся только заголовком» — правьте такую цель в кабинете. Коды, в которыхak_просто встречается (kayak_booking), правятся как любые другие.
Сегменты
Сегмент — набор условий, по которым отбираются посетители; отчёт с сегментом показывает только их визиты. Что такое сегмент и какие бывают условия — в разделе Конструктор отчётов. Через API сегмент заводится и правится тем же ключом, а его segment_id сразу годится для POST /reports.
curl -X POST https://analytics.daratech.ru/api/v1/segments \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Смотрели цены и не купили",
"definition": {"op": "and", "groups": [
{"conditions": [{"kind": "page", "match": [{"op": "contains", "value": "/price"}], "days": 30}]},
{"conditions": [{"kind": "goal", "goal": "crm_deal_won", "days": 30}]}]}}'
Ответ 201 Created — созданный сегмент в поле segment:
{"segment": {"segment_id": "9d4c1a2b-7e35-4f18-9a60-1b2c3d4e5f60", "name": "Смотрели цены и не купили",
"definition": {"v": 1, "op": "and", "groups": [{"op": "and", "conditions": [{"kind": "page", "match": [{"op": "contains", "value": "/price"}], "days": 30}]}]},
"created_at": "2026-09-28T12:00:00Z", "updated_at": "2026-09-28T12:00:00Z"}}
- Условия проверяются теми же правилами, что и форма в кабинете. Неверное условие —
422, а вdetailsназвано точное поле внутри определения:definition.groups[0].conditions[1].values[0]. Свою схему условий описывать не нужно: что принимает кабинет, то принимает и API. - Цель в условии называется кодом:
{"kind": "goal", "goal": "crm_deal_won", "days": 30}. Числовойgoal_idне принимается —422с кодомunknown_field. Коды целей отдаётGET /goals. - Сегменты API — общие для всех, у кого есть доступ к сайту. Личные сегменты сотрудников через API не видны и не правятся: в списке их нет, а
PATCHиDELETEпо такомуsegment_idотвечают404— ровно так же, как кабинет. PATCHменяет только переданные поля (name,definition); правка теми же значениями ничего не пишет и не сбрасывает кэш отчётов.- Повтор того же запроса безопасен: если общий сегмент с таким названием и такими же условиями уже есть, ответ —
200и он же, второго не появляется. - Удаление необратимо — восстановления у сегментов нет ни в кабинете, ни в API. Поэтому
DELETEотдаёт удалённый сегмент целиком, вместе с условиями: если он ещё понадобится, создайте его заново тем же телом. Отчёты, сохранённые с этим сегментом, после удаления перестанут строиться. DELETEработает любым вашим ключом — и ключом сайтаak_…, и ключом аккаунтаaa_…с заголовкомX-Dara-Site. Это данные одного сайта, и ключ этого сайта ими распоряжается целиком: заводит, правит и убирает. Удаление здесь необратимо, поэтому ответ отдаёт удалённую строку целиком — по ней её возвращает повторныйPOSTс тем же телом.- Предел через API — 200 сегментов на сайт (считаются и личные сегменты сотрудников):
422с кодомtoo_manyу поляsegments. - Список отдаётся страницами и обходится курсором — правила в разделе «Страницы списков» выше.
Воронки
Воронка — последовательность шагов, по которой видно, где посетители уходят. Что такое воронка и как считаются её шаги — в разделе Конструктор отчётов. Методы устроены как у сегментов.
curl -X POST https://analytics.daratech.ru/api/v1/funnels \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Корзина → покупка",
"definition": {"window": {"days": 7}, "order": "strict", "steps": [
{"kind": "page", "match": [{"op": "contains", "value": "/cart"}]},
{"kind": "goal", "goal": "crm_deal_won"},
{"kind": "ecommerce", "action": "purchase"}]}}'
- Шагов от 2 до 10;
window—"visit"(в пределах визита) или{"days": N}от 1 до 90;order—strict(шаги подряд) илиany(все шаги в окне без требования порядка). Умолчания —"visit"иstrict. - Шаг по цели называет цель кодом:
{"kind": "goal", "goal": "crm_deal_won"}. Числовойgoal_id—422. - Воронки API — общие; личные воронки сотрудников через API не видны и не правятся.
DELETEотдаёт удалённую воронку целиком: восстановления у воронок нет.DELETEработает любым вашим ключом — и ключом сайтаak_…, и ключом аккаунтаaa_…с заголовкомX-Dara-Site. Это данные одного сайта, и ключ этого сайта ими распоряжается целиком: заводит, правит и убирает. Удаление здесь необратимо, поэтому ответ отдаёт удалённую строку целиком — по ней её возвращает повторныйPOSTс тем же телом.- Предел через API — 200 воронок на сайт.
Посчитать воронку за период — POST /funnels/{funnel_id}/report:
curl -X POST https://analytics.daratech.ru/api/v1/funnels/3c9e2a71-5b04-4f6d-9e18-0a7b2c4d6e85/report \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"date_from": "2026-09-01", "date_to": "2026-09-14"}'
{"funnel_id": "3c9e2a71-5b04-4f6d-9e18-0a7b2c4d6e85", "name": "Корзина → покупка",
"window": {"days": 7}, "order": "strict", "visitors": 1930, "completed": 260, "conversion": 13.47,
"steps": [{"index": 1, "step": {"kind": "page", "match": [{"op": "contains", "value": "/cart"}]},
"visitors": 1930, "conversion": 100.0, "step_conversion": 100.0, "dropped": 0, "drop_rate": null,
"avg_seconds_to_next": 88.25},
{"index": 2, "step": {"kind": "goal", "goal": "crm_deal_won"},
"visitors": 610, "conversion": 31.61, "step_conversion": 31.61, "dropped": 1320, "drop_rate": 68.39,
"avg_seconds_to_next": 35.0},
{"index": 3, "step": {"kind": "ecommerce", "action": "purchase"},
"visitors": 260, "conversion": 13.47, "step_conversion": 42.62, "dropped": 350, "drop_rate": 57.38,
"avg_seconds_to_next": null}],
"timezone": "Europe/Moscow", "date_from": "2026-09-01", "date_to": "2026-09-14"}
- Считается так же, как в кабинете: шаги — по посетителям, цепочка целиком лежит внутри периода; проценты — от первого шага (
conversion) и от предыдущего (step_conversion); деление на ноль —null. robots—exclude(по умолчанию),includeилиonly. Период — те же правила, что у отчёта: не позже сегодняшнего дня сайта и не длиннее 25 месяцев.- Бюджет — как у отчёта: тот же лимит
POST /reportsи те же одновременные отчёты ключа или аккаунта.
Категории товаров
Категории товаров у каждого магазина свои, а покупки, интересы и ценовые сегменты считаются по общему справочнику категорий ДАРА. Строку категории сайта сервис заводит сам, когда встречает её в заказах, и подбирает ей категорию справочника; выбор можно задать самим — из кабинета по одной или отсюда выгрузкой из товарного учёта.
Справочник читается методом GET /categories — он один на всю сеть, ключ подойдёт любой:
curl https://analytics.daratech.ru/api/v1/categories?limit=200 \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
{"categories": [{"id": 1604, "parent_id": 166, "level": 2, "path": "Одежда и аксессуары > Одежда", "title": "Одежда"}],
"next_cursor": "…", "total": 5443}
Свои категории и их сопоставление — GET /category-mappings:
{"category_mappings": [{"category_hash": "623621428df845ca971fc6dfe1b2e59f", "category": "Одежда/Женская/Платья",
"category_id": 1604, "category_path": "Одежда и аксессуары > Одежда",
"is_manual": false, "updated_at": "2026-09-28T03:10:00Z"}],
"next_cursor": null, "total": 1}
category_hash— ключ строки: md5 приведённой строки категории (обрезанные пробелы, схлопнутые пробелы внутри, нижний регистр). Один и тот же ключ получается и у сервиса, и у вас, поэтому разное написание одной категории не заводит второй строки.is_manual: false— категорию подобрал сервис;true— её задали руками, и сервис такую строку не трогает.- Отбор:
only_manual=true— только заданные руками,only_unmapped=true— только без категории справочника (с них и стоит начинать).
Задать сопоставление:
curl -X POST https://analytics.daratech.ru/api/v1/category-mappings \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"category": "Одежда/Женская/Платья", "category_id": 1604}'
- Строка заводится, если её ещё нет, и получает
is_manual: true. Ответ —201, а если строка уже была —200. category_id: null— «не сопоставлять»: строка остаётся заданной руками и без категории, и сервис больше не будет подбирать ей категорию сам.- Номера, которого нет в справочнике, —
422с полемcategory_id. PATCH /category-mappings/{category_hash}меняет только категорию справочника, саму строку категории пересылать не нужно.DELETE /category-mappings/{category_hash}снимает ручное сопоставление и возвращает строку автоматическому подбору; в ответе — снятая строка целиком. Строку, которую подобрал сервис, снимать нечего —404.DELETEработает любым вашим ключом — и ключом сайтаak_…, и ключом аккаунтаaa_…с заголовкомX-Dara-Site. Сопоставление — данные одного сайта, и ключ этого сайта ими распоряжается: заводит, правит и снимает. Снятое сопоставление возвращается новымPOST, поэтому ответ отдаёт снятую строку целиком.- После изменения сопоставлений отчёты электронной коммерции за прошлые дни пересчитываются.
Расходы на рекламу
Окупаемость рекламы — ad_cost, cost_per_visit, cost_per_order, cpa:{код цели}, roi, crr в отчётах — считается по расходу, которого у сервиса нет: его знает ваш рекламный кабинет. Расход вносят в кабинете («Настройки → Расходы на рекламу», строкой или файлом CSV) или этим методом — например, раз в сутки скриптом из выгрузки Директа. Как считаются метрики — Отчёты и метрики.
Строка расхода — день, три UTM-метки и сумма:
curl -X POST https://analytics.daratech.ru/api/v1/ad-costs \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"costs": [
{"date": "2026-10-01", "utm_source": "yandex", "utm_medium": "cpc", "utm_campaign": "autumn_sale", "amount": 12500.50},
{"date": "2026-10-01", "utm_source": "vk", "utm_medium": "cpm", "utm_campaign": "", "amount": 99.90, "currency": "USD"}
]}'
{"created": 2, "updated": 0, "unchanged": 0, "deleted": 0}
- Сопоставление — только по
utm_source,utm_mediumиutm_campaign, без учёта регистра. «Yandex» и «yandex» — одна строка.utm_contentиutm_termв расчёт не идут. Визиты поyclidиgclidбез UTM-меток расходу не сопоставляются: размечайте объявления UTM-метками. utm_sourceобязателен. Пустыеutm_mediumиutm_campaignзначат буквально «метка не задана», а не «весь источник»: расход на кампанию записывайте с еёutm_campaign.date— день расходаГГГГ-ММ-ДДв поясе сайта: не в будущем и не старше 25 месяцев. Расход за месяц одной суммой разложите по дням или запишите на первое число.amount— от 0, не больше двух знаков после запятой.currency— код валюты из списка валют сайта; без него — валюта сайта. Сумма в другой валюте пересчитывается в валюту сайта по курсу ЦБ на дату расхода в момент записи и задним числом не меняется. Курса на эту дату нет —422с кодомno_rateу поляcosts[N].currency.- Повтор строки с теми же датой и метками заменяет сумму. Повтор той же пачки ничего не меняет:
"unchanged": 2. - Пачка — всё или ничего: до 1000 строк, ошибки всех строк приходят сразу (
costs[3].amount), и не пишется ни одна. Одна и та же строка дважды в пачке — кодduplicate. "replace_dates": trueв теле — сначала удалить все расходы сайта за даты пачки. Так повторная выгрузка из рекламного кабинета не оставляет кампаний, которых в ней уже нет; число удалённых строк — в полеdeleted.- На сайт — до 200 000 строк расходов.
Прочитать записанное — GET /ad-costs?date_from=2026-10-01&date_to=2026-10-31, с отбором utm_source, utm_medium, utm_campaign (без учёта регистра) и обходом страницами:
{"ad_costs": [{"date": "2026-10-01", "utm_source": "yandex", "utm_medium": "cpc", "utm_campaign": "autumn_sale",
"amount": 12500.5, "currency": "RUB", "amount_site": 12500.5, "origin": "api",
"updated_at": "2026-10-02T03:00:00Z"}],
"next_cursor": null, "total": 1, "total_amount_site": 12500.5, "currency": "RUB"}
Метки в ответе — в том виде, в каком хранятся: в нижнем регистре, с одним пробелом между словами. origin — откуда строка: cabinet или api.
Удалить расходы за период — DELETE /ad-costs?date_from=…&date_to=…, можно с теми же метками; в ответе — {"deleted": N}. Работает любым вашим ключом, как и запись. После любого изменения расходов отчёты за прошлые дни пересчитываются.
Заказы
POST /orders передаёт заказы и возвраты. Правила учёта — в разделе Электронная коммерция.
curl -X POST https://analytics.daratech.ru/api/v1/orders \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{
"orders": [{
"transaction_id": "T-20931",
"visitor_id": "0b7e6f8a-3c2d-4e5f-9a1b-2c3d4e5f6a7b",
"occurred_at": "2026-09-14T12:30:00Z",
"status": "purchased",
"currency": "RUB",
"revenue": 5480,
"products": [
{"id": "SKU-1017", "name": "Платье миди", "category": "Одежда/Женская/Платья", "brand": "Пример", "variant": "синее", "price": 2490, "qty": 2},
{"id": "SKU-2044", "name": "Ремень", "category": "Одежда/Аксессуары", "price": 500, "qty": 1}
]
}]
}'
Ответ 202 Accepted:
{"accepted": 1, "duplicates": 0, "rejected": []}
- В одном запросе — до 500 заказов.
status—purchased(по умолчанию) илиrefunded. Заказ со статусомrefunded— возврат: он уменьшает выручку и заказы того периода, в который пришёл, а прошлые отчёты задним числом не меняет. Правила учёта — в разделе Электронная коммерция.- Заказ узнаётся по
transaction_id: покупка из тега и из API учитывается один раз. - Заказ приписывается визиту по тем же правилам, что конверсия.
- Коды отказа:
unknown_visitor,too_old,invalid_products— в заказе нет товаров, их больше 100 или у товара неверное поле.
Отчёты
POST /reports строит отчёт тем же движком, что конструктор в кабинете. Права и пределы — как у пользователя с ролью «наблюдатель».
curl -X POST https://analytics.daratech.ru/api/v1/reports \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{
"dimensions": ["source_type", "source_name"],
"metrics": ["visits", "visitors", "pageviews"],
"date_from": "2026-09-01",
"date_to": "2026-09-14",
"filters": [{"dimension": "device_type", "op": "eq", "values": ["phone"]}],
"attribution": "last_significant",
"robots": "exclude",
"sort": [{"field": "visits", "dir": "desc"}],
"limit": 100
}'
Ответ:
{"data": [
{"dimensions": ["search", "Яндекс"], "metrics": [1840, 1522, 5210]},
{"dimensions": ["search", "Google"], "metrics": [620, 541, 1733]}
],
"totals": [3920, 3311, 11042],
"sampled": false,
"timezone": "Europe/Moscow",
"labels": {"source_type": {"search": "Поисковые системы"}},
"total_rows": 57,
"sample_share": 1.0,
"quota_sampled": [],
"date_from": "2026-09-01",
"date_to": "2026-09-14",
"cached": false,
"generated_at": "2026-09-14T21:15:30.120Z"}
В примере показаны две строки из 57 (total_rows) — остальные приходят так же, страницами по limit.
| Поле запроса | Значение |
|---|---|
dimensions |
до 5 измерений |
metrics |
от 1 до 10 метрик |
date_from, date_to |
даты ГГГГ-ММ-ДД в часовом поясе сайта, включительно; период — до 25 месяцев |
filters |
до 20 условий, объединяются через «и»: {"dimension", "op", "values"}. op — eq (равно), neq (не равно), contains (содержит), regex (регулярное выражение RE2), in (входит в список). У in в values до 100 значений, у остальных — ровно одно |
segment_id |
UUID сохранённого сегмента сайта — он же в адресе страницы сегмента в кабинете (…/segments/{UUID}). Числовой номер не принимается |
segment |
условия сегмента прямо в запросе, вместо segment_id; оба поля сразу — ошибка |
attribution |
модель атрибуции: last_significant — последний значимый источник (по умолчанию), first — первый, last — последний |
robots |
exclude — без роботов (по умолчанию), include — все, only — только роботы |
sort |
до 3 условий {"field", "dir"}: field — метрика или измерение из запроса, dir — asc или desc. По умолчанию — первая метрика по убыванию, а если в запросе есть измерение времени — по нему по возрастанию |
limit, offset |
limit — от 1 до 10 000, по умолчанию 100; offset — сколько строк пропустить |
compare |
период сравнения {"date_from", "date_to"}; тогда в ответе появляется поле compare |
totals |
false — без итогов: totals в ответе будет null |
accuracy |
точность: full — по всем данным (по умолчанию), low — по выборке 10 %. Грубый отчёт считается примерно в десять раз дешевле и годится для панелей, которые обновляются часто; что получилось, видно в sampled и sample_share |
- Измерения и метрики — те же, что в разделе Отчёты и метрики. Полный список идентификаторов с названиями, группами, допустимыми операциями фильтров и пределами одного запроса отдаёт
GET /meta— ниже, раздел «Каталог измерений и метрик». В описании OpenAPI перечня идентификаторов нет: тело отчёта там описано формой, а состав измерений и метрик меняется без выпуска новой схемы. - Измерения аудитории —
gender,age_group,interest,has_children,children_age,company_type,industry,company_region,purchase_category,price_segment,geo_person_country,geo_person_region,geo_person_city,visitor_param:ключ,value_source:атрибут— и метрикиpeople,people_share,undefined_share,network_share,affinity_indexсчитаются по правилам раздела Сеть ДАРА. Значения — коды, как вPOST /visitors/attributes; у интересов, категорий и мест — id справочника, а название — вlabelsответа. totals— итоги по метрикам в том же порядке, чтоmetrics.labels— подписи значений:{измерение: {значение: подпись}}. Значения в строках отчёта не меняются, подписи лежат отдельно и не повторяются в каждой строке. Чего вlabelsнет, то само себе подпись: у адресов страниц и поисковых фраз подписывать нечего, и такие измерения в словаре не появляются вовсе.total_rows— сколько строк у отчёта всего, безlimitиoffset: по нему видно, когда страницы кончились. У отчёта по выборке это строки выборки — ровно те, которые обойдут следующие страницы с той же точностью.sampled: true— отчёт посчитан по выборке: либо запрос не уложился в 30 секунд, либо клиент попросилaccuracy: low.sample_share— доля данных в ответе (1.0или0.1); счётные метрики выборки умножены на 10, то есть это оценка. Дни изquota_sampledв эту долю не входят: они собирались по выборке ещё при приёме, и их числа — оценка сборщика при любомsample_share.quota_sampled— дни периода, в которые сайт вышел за суточную квоту хитов и данные собирались по выборке (см. Отчёты и метрики). Пустой список — таких дней в периоде не было. Если отчёт пришёл по выборке, а список пуст, дело во времени запроса: повтор или более узкий период помогут.date_from,date_to— разобранный период отчёта,cachedиgenerated_at— взят ли ответ из кэша и когда он посчитан. Поэтому одинаковые числа в двух ответах — это кэш, а не остановка сбора данных.compareв запросе —compareв ответе: у того же отчёта считается второй период, и строки сравниваются строка к строке. Остальные поля ответа те же, поэтому в примере ниже показаны не все.
{"data": [{"dimensions": ["search", "Яндекс"], "metrics": [1840, 1522, 5210]},
{"dimensions": ["search", "Google"], "metrics": [620, 541, 1733]}],
"totals": [3920, 3311, 11042],
"sampled": false,
"timezone": "Europe/Moscow",
"compare": {"date_from": "2026-08-18", "date_to": "2026-08-31",
"data": [{"metrics": [1710, 1402, 4980]}, null],
"totals": [3640, 3080, 10310]}}
compare.data идёт строка к строке с data: та же длина и тот же порядок, а null значит, что в периоде
сравнения такой строки не было. compare.totals ведёт себя как totals. Без compare в запросе поля
compare в ответе нет.
- Одновременно — до 2 запросов отчётов на ключ и до 60 в минуту; ключу аккаунта — 4 одновременных запроса на аккаунт, общих для всех его ключей, и 240 в минуту. Запрос сверх этого сразу получает
429с кодомrate_limitedи заголовкомRetry-After. - Неизвестное измерение или метрику, неверные даты и фильтры отчёт отклоняет ответом
422с кодомvalidation_failed— подробности вdetails, коды — в разделе Ошибки и лимиты.
Задания: отчёт без ожидания и выгрузка аудитории
Тяжёлый запрос не должен держать соединение. Вы ставите задание, получаете job_id, спрашиваете состояние — и по тому же адресу забираете результат, когда он готов.
| Метод | Что делает |
|---|---|
POST /reports/async |
ставит в очередь отчёт: тело — то же определение, что у POST /reports |
POST /audiences/export |
ставит в очередь выгрузку аудитории сегмента |
GET /jobs/{job_id} |
состояние задания, а у готового — и его результат |
curl -X POST https://analytics.daratech.ru/api/v1/reports/async \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{
"dimensions": ["source_type", "source_name"],
"metrics": ["visits", "visitors"],
"date_from": "2026-01-01",
"date_to": "2026-09-14"
}'
Ответ:
{"job": {"job_id": "0f2d6b18-3c47-4e9a-8b52-7d1e0a4c6f83",
"kind": "report",
"status": "queued",
"created_at": "2026-09-14T12:00:00Z",
"started_at": null,
"finished_at": null,
"rows": null,
"expires_at": null,
"error": null},
"report": null,
"audience": null,
"next_cursor": null}
Дальше спрашивайте состояние — по тому же адресу придёт и результат:
curl https://analytics.daratech.ru/api/v1/jobs/0f2d6b18-3c47-4e9a-8b52-7d1e0a4c6f83 \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
{"job": {"job_id": "0f2d6b18-3c47-4e9a-8b52-7d1e0a4c6f83",
"kind": "report",
"status": "done",
"created_at": "2026-09-14T12:00:00Z",
"started_at": "2026-09-14T12:00:05Z",
"finished_at": "2026-09-14T12:00:20Z",
"rows": 1,
"expires_at": "2026-09-14T13:00:20Z",
"error": null},
"report": {"data": [{"dimensions": ["search", "Яндекс"], "metrics": [1840, 1522]}],
"totals": [1840, 1522],
"sampled": false,
"timezone": "Europe/Moscow",
"labels": {"source_type": {"search": "Поисковые системы"}},
"total_rows": 1,
"sample_share": 1.0,
"quota_sampled": [],
"date_from": "2026-01-01",
"date_to": "2026-09-14",
"cached": false,
"generated_at": "2026-09-14T12:00:20Z"},
"audience": null,
"next_cursor": null}
| Поле задания | Значение |
|---|---|
job_id |
идентификатор задания; он же в адресе GET /jobs/{job_id} |
kind |
report — отчёт, audience — выгрузка аудитории |
status |
queued — ждёт очереди, running — считается, done — результат в этом же ответе, failed — причина в error |
created_at, started_at, finished_at |
когда задание поставлено, взято в работу и кончилось |
rows |
строк в готовом результате; null — результата ещё нет |
expires_at |
до какого времени результат доступен |
error |
у failed — объект с полями code и message. Значения code: validation_failed (условие задания перестало быть верным), too_slow, unavailable, not_found, interrupted, internal |
- Результат приходит там же, где состояние. У готового отчёта поле
report— это ровно тот же ответ, что отдаётPOST /reports, поле в поле: переписывать разбор ответа не нужно. У готовой выгрузки заполненоaudience. - Очередь просыпается раз в 15 секунд, и считается в ней одно задание за раз. Спрашивать состояние чаще, чем раз в несколько секунд, смысла не имеет.
- Определение проверяется сразу. Неизвестное измерение, неверная дата, неизвестный
segment_id—422в ответ на постановку, а не готовое задание с отказом через минуту. - Повтор того же запроса не ставит второе задание: если такое задание уже в очереди, придёт оно же, а код ответа будет
200вместо202. - Ключом аккаунта спрашивайте с тем же
X-Dara-Site, что при постановке: без заголовка —422, с другим сайтом —404. - Срок результата: у отчёта — час, у выгрузки аудитории — сутки. После него задание отвечает
404— как неизвестное и как чужое: по ответу нельзя узнать, существовало ли оно.
Пределы. Очередь не даёт обойти пределы синхронных отчётов, она ими и ограничена:
| Предел | Ключ сайта | Ключ аккаунта |
|---|---|---|
постановка задания — то же ведро, что у POST /reports |
60 в минуту | 240 в минуту |
невыполненных заданий (queued и running) |
2 на ключ | 4 на аккаунт |
| опрос состояния — ведро чтения | 50 в секунду | 200 в секунду |
Невыполненных заданий столько же, сколько одновременных отчётов у POST /reports: очередь — это способ не ждать ответа, а не способ заказать больше работы. Сверх предела — 429 с кодом rate_limited и заголовком Retry-After.
Выгрузка аудитории сегмента
POST /audiences/export отвечает на вопрос «кто из ваших посетителей попал в сегмент за период». Это самый дорогой запрос сервиса, поэтому он работает только заданием очереди.
curl -X POST https://analytics.daratech.ru/api/v1/audiences/export \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{
"segment_id": "9d4c1a2b-7e35-4f18-9a60-1b2c3d4e5f60",
"date_from": "2026-06-01",
"date_to": "2026-09-14"
}'
| Поле запроса | Значение |
|---|---|
segment_id |
UUID сохранённого сегмента сайта |
segment |
условия сегмента прямо в запросе, вместо segment_id; оба поля сразу — ошибка |
date_from, date_to |
период в часовом поясе сайта, включительно |
filters |
условия сверх сегмента — та же форма, что у POST /reports |
robots |
exclude (по умолчанию), include, only |
limit |
потолок строк выгрузки: до 100 000 (по умолчанию столько же) |
Условие обязательно: без segment_id или segment метод отвечает 422. Выгрузка аудитории — это выгрузка сегмента, а не «отдайте всех посетителей сайта».
Готовая выгрузка приходит страницами того же GET /jobs/{job_id}: параметры limit (до 10 000 строк, по умолчанию 1000) и cursor, а next_cursor ведёт на следующую страницу — как во всех списках API.
{"job": {"job_id": "3a71c0d5-8e62-4b19-9f04-5c7d2e8a1b60", "kind": "audience", "status": "done",
"created_at": "2026-09-14T12:00:00Z", "started_at": "2026-09-14T12:00:05Z",
"finished_at": "2026-09-14T12:01:40Z", "rows": 24180,
"expires_at": "2026-09-15T12:01:40Z", "error": null},
"report": null,
"audience": {"date_from": "2026-06-01",
"date_to": "2026-09-14",
"total": 24180,
"truncated": false,
"visitors": [
{"visitor_id": "5f0c6c9e-2b8a-4c1e-9a57-0d3b7e41c2a8", "user_id": "u-501",
"last_visit_at": "2026-09-14T09:12:30Z", "visits": 7},
{"visitor_id": "8a1d4b02-6c73-4e59-b0f1-2d6a9c3e7f45", "user_id": null,
"last_visit_at": "2026-09-13T18:44:02Z", "visits": 2}]},
"next_cursor": "QkxNVjJHN0FaWFA0S0RSNldTM0VZTjVUQ0pINDJVTTdGOVFCVjhYRA"}
| Поле строки | Значение |
|---|---|
visitor_id |
посетитель — значение cookie _dara_a на вашем домене |
user_id |
ваш идентификатор пользователя, который вы сами передали — командой тега dara('user', …) или методом POST /users; null — этого посетителя вы не называли; * — вы прислали в нём адрес почты или телефон, и таким мы его не храним |
last_visit_at |
последний визит в периоде |
visits |
сколько визитов у посетителя в периоде |
total — сколько посетителей в выгрузке всего; truncated: true значит, что сегмент шире потолка limit и в выгрузку попали самые недавние посетители.
Что выгрузка отдаёт и чего не отдаёт. В ней только идентификаторы — ваши и общий с вами visitor_id. Ни адреса почты, ни телефона, ни имени, ни даты рождения в ней нет и появиться не может: персональные данные посетителей зашифрованы и наружу не выходят никогда — ни этим методом, ни любым другим. Это не «выгрузка в рекламные системы»: ни один рекламный кабинет не умеет сопоставлять чужой visitor_id, и обещать вам подачу аудитории напрямую мы не будем.
Как ей пользоваться. Вы получаете ответ «вот ваши идентификаторы тех, кто попал в сегмент», и дальше работаете с ними у себя: поднимаете этих людей в своей CRM и сами отправляете в свой рекламный кабинет то, что и так ваше; персонализируете сайт по visitor_id из cookie _dara_a; передаёте по ним конверсии с сервера. Наша роль — ответить, кто из ваших людей в сегменте, а не что это за люди.
Отсюда и предусловие: если сайт никогда не присылал user_id, в выгрузке будут только visitor_id — они полезны, если вы храните значение cookie _dara_a у себя (см. «Идентификаторы» выше и Данные о посетителях).
Каталог измерений и метрик
GET /meta отдаёт всё, что принимает POST /reports: идентификаторы измерений и метрик с названиями и группами, подписи значений и пределы одного запроса. В кабинете видны названия, а идентификаторы — нет, поэтому каталог нужен любому, кто составляет отчёты не руками. Ответ одинаков у всех сайтов, ключ нужен только для авторизации.
export DARA_ANALYTICS_KEY='ak_XXXXXXXX…'
curl https://analytics.daratech.ru/api/v1/meta \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
Ответ (здесь сокращён: измерений 65, метрик 31, наборов подписей 21):
{"registry_version": "2bcfcc0ddf1b9e64",
"groups": [{"id": "audience", "title": "Аудитория"}, {"id": "time", "title": "Время"},
{"id": "source", "title": "Источник"}, {"id": "page", "title": "Страница"},
{"id": "actions", "title": "Действия"}, {"id": "tech", "title": "Технологии"},
{"id": "geo", "title": "География"}, {"id": "visitor", "title": "Посетитель"},
{"id": "service", "title": "Служебные"}],
"dimensions": [
{"id": "source_type", "title": "Тип источника", "group": "source", "level": "visit", "value_type": "text",
"labels": "source_type", "empty_label": "Не определено", "filter_ops": ["eq", "neq", "in"], "param": null},
{"id": "url", "title": "Страница", "group": "page", "level": "hit", "value_type": "text",
"labels": null, "empty_label": "Не определено",
"filter_ops": ["eq", "neq", "contains", "regex", "in"], "param": null},
{"id": "event_param", "title": "Параметр события", "group": "actions", "level": "hit", "value_type": "text",
"labels": null, "empty_label": "Не определено",
"filter_ops": ["eq", "neq", "contains", "regex", "in"],
"param": {"title": "Имя параметра", "max_length": 128}},
{"id": "interest", "title": "Интерес", "group": "audience", "level": "both", "value_type": "catalog_id",
"labels": null, "empty_label": "Не определено", "filter_ops": ["eq", "neq", "in"], "param": null}],
"metrics": [
{"id": "visits", "title": "Визиты", "type": "int", "levels": ["visit", "hit"], "additive": true, "param": null},
{"id": "bounce_rate", "title": "Отказы", "type": "percent", "levels": ["visit"], "additive": false,
"param": null},
{"id": "revenue", "title": "Выручка", "type": "money", "levels": ["hit"], "additive": true, "param": null}],
"labels": {"source_type": {"direct": "Прямые заходы", "search": "Поисковые системы", "ad": "Реклама"}},
"limits": {"dimensions": 5, "metrics": 10, "filters": 20, "filter_values": 100, "filter_value_length": 2048,
"sort": 3, "limit": 10000, "limit_default": 100, "period_months": 25},
"attribution": ["last_significant", "first", "last"],
"robots": ["exclude", "include", "only"]}
| Поле | Значение |
|---|---|
registry_version |
версия каталога: меняется вместе с составом измерений и метрик. Каталог можно держать у себя и обновлять, когда версия сменилась |
groups[] |
группы измерений: id и название. На id ссылается dimensions[].group |
dimensions[] |
id — для dimensions, filters[].dimension и sort[].field; title — название по-русски, то же, что в кабинете; empty_label — подпись пустого значения; filter_ops — операции filters, которые есть у этого измерения |
dimensions[].level |
где живёт измерение: visit — у визита, hit — у хита, both — у обоих |
dimensions[].value_type |
text — значение строкой (код, адрес, дата); catalog_id — номер строки общего справочника: темы, категории товаров, города, регионы |
dimensions[].labels |
имя набора подписей в labels или null, если значения не кодовые |
metrics[] |
id — для metrics и sort[].field; type — как показывать значение (int, float, percent, seconds, money); additive — значение можно складывать по строкам (у долей и средних нельзя) |
metrics[].levels |
уровни запроса, на которых метрика считается |
labels |
наборы подписей по имени: код значения → название |
limits |
пределы одного запроса POST /reports — те же, что в таблице полей запроса выше |
attribution, robots |
значения одноимённых полей запроса |
param |
у измерения или метрики с параметром: что значит параметр и его наибольшая длина. Такое измерение просится токеном id:параметр — event_param:plan, goal_reaches:order |
Коды целей для метрик goal_reaches:{код} и conversion:{код} каталог не перечисляет — они свои у каждого сайта: их отдаёт GET /goals (раздел «Цели» выше).
Какие измерения и метрики можно просить вместе, каталог говорит прямо, подбирать не нужно:
- если в запросе есть измерение с
level: "hit"или метрика безvisitвlevels, весь отчёт считается по хитам; - в таком отчёте каждая метрика должна поддерживать
hit, иначе422с кодомincompatibleна поле метрики. Напримерurlиbounce_rateвместе не считаются: отказы — метрика визита; - измерения между собой совместимы всегда: измерение визита в отчёте по хитам берётся из визита этого хита.
Сайты
Сайты аккаунта заводятся, перечисляются и удаляются тем же ключом — заходить в кабинет за каждым сайтом не нужно. Эти методы работают только ключом аккаунта aa_…: ключ сайта распоряжается лишь своим сайтом и получает 403. Заголовок X-Dara-Site здесь не нужен и не принимается — сайт называет сам адрес.
curl https://analytics.daratech.ru/api/v1/sites?limit=100 \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
Ответ:
{"sites": [{"site_uuid": "7b3c1e42-9f58-4d0a-8b61-2c4e5f7a9d13", "site_key": "as_XXXXXXXXXXXXXXXXXXXX",
"name": "Магазин", "domains": [{"host": "shop.example.ru", "subdomains": true}],
"timezone": "Europe/Moscow", "currency": "RUB", "status": "active", "role": "owner",
"created_at": "2026-09-14T12:00:00Z", "deleted_at": null, "restorable": false,
"restore_until": null,
"first_hit_at": "2026-01-14T09:20:11Z", "last_hit_at": "2026-09-26T11:59:03Z"}],
"next_cursor": null, "total": 1}
| Поле | Значение |
|---|---|
site_uuid |
идентификатор сайта: его ставят в заголовок X-Dara-Site и в адрес /sites/{site_uuid} |
site_key |
ключ сайта as_… — тот же, что в коде тега; он публичный, и без него тег не поставить |
name, domains |
название и домены сайта; первый домен основной, subdomains — считать ли его поддомены тем же сайтом |
timezone, currency |
пояс и валюта сайта: в них считаются даты и деньги отчётов |
status |
active — сайт работает; blocked — заблокирован поддержкой, данные не принимаются; deleted — удалён и ждёт очистки |
role |
чем вы приходитесь сайту: owner — сайт ваш, admin — вас позвали администратором в чужой аккаунт |
restorable |
сайт ещё можно вернуть методом восстановления: срок не истёк и очистка данных не началась |
deleted_at, restore_until |
когда сайт удалён и до какого времени его ещё можно восстановить; restore_until — null, когда вернуть сайт уже нельзя |
first_hit_at, last_hit_at |
первый и последний хит: по ним видно, какой из сайтов перестал присылать данные |
- По умолчанию список показывает действующие сайты; свои из них (
role:owner) принимает заголовокX-Dara-Site, сайты сrole:admin— нет (см. ниже). Заблокированные показывает?status=blocked, удалённые и ждущие очистки —?status=deleted, все три —?status=all. - Заблокированный сайт занимает слот в пределе числа сайтов, поэтому он виден в списке: иначе сайтов было бы видно меньше, чем занято слотов. Данные такой сайт не принимает, и заголовок
X-Dara-Siteотвечает по нему403. - В списке
?status=deletedполеrestorableговорит, можно ли ещё вернуть сайт.false— срок вышел или очистка уже началась; такой сайт отвечает на восстановление422, и проверять это самому не нужно. - Список отдаётся страницами, обход — курсором: правила одни на все списки и описаны выше, в общих правилах.
- В списке есть и сайты, где вы администратор по приглашению (
role:admin), а не только ваши собственные (role:owner): иначе сайтов клиента, позвавшего вас в свой кабинет, из кода было бы не видно вовсе. Разница важна: удалить и восстановить можно только свой сайт, чужой отвечает на это404— так же, как несуществующий. Приглашение «наблюдателем» сайт в список не добавляет, и удалённые сайты в списке только ваши собственные. - Заголовок
X-Dara-Siteшире не стал: сайт, где вы только администратор, по нему отвечает404, и отчёты, цели и посетителей по нему пока не спросить. Чтобы работать с сайтом из кода целиком, попросите владельца передать вам сайт или выпустите ключ в его аккаунте.
Завести сайт
curl -X POST https://analytics.daratech.ru/api/v1/sites \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Магазин", "domains": [{"host": "shop.example.ru", "subdomains": true}],
"timezone": "Europe/Moscow", "currency": "RUB"}'
Ответ 201 Created — созданный сайт в поле site, вместе с site_uuid и site_key: этого хватает, чтобы сразу поставить тег и начать спрашивать отчёты.
- Обязательные поля —
nameиdomains(хотя бы один домен).timezoneне задан —Europe/Moscow,currencyне задана —RUB. Хост приводится так же, как в кабинете: нижний регистр, punycode, безhttps://, порта, пути и ведущегоwww.. - Срок хранения данных при создании не задаётся: он берётся из квот вашего аккаунта, и действующее значение показывает
GET /site(retention_months). - Повтор того же запроса безопасен: если такой сайт уже есть и совпадает во всём, ответ —
200и тот же сайт, второго не появляется. Повтор после таймаута не заводит близнеца. Если же отличается хоть одно поле, создаётся новый сайт: один домен вправе иметь, например, боевой и тестовый счётчик. - Число ДЕЙСТВУЮЩИХ сайтов в аккаунте ограничено (по умолчанию 30). Предел достигнут —
422с кодомtoo_manyу поляsites. Удаление освобождает слот сразу. Сайты, где вы администратор по приглашению, слотов не занимают: предел считает только ваши собственные. - Суточного предела на создание сайтов нет. Подключайте хоть сотню в день: мешать этому вправе только предел действующих сайтов и общий предел частоты запросов ключа.
- Пояс, валюту и квоты сайта через API не меняют — только в кабинете: смена пояса и валюты меняет смысл уже посчитанных дат и денег.
Изменить сайт
curl -X PATCH https://analytics.daratech.ru/api/v1/sites/7b3c1e42-9f58-4d0a-8b61-2c4e5f7a9d13 \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"domains": [{"host": "shop.example.ru", "subdomains": true}, {"host": "mirror.example.ru"}]}'
- Меняются только
nameиdomains, по одному или оба; ответ — сайт после правки в полеsite. Домены приводятся и проверяются так же, как при создании;domainsзаменяет список целиком, первый — основной. - Тег узнаёт новые домены сам в течение 30 секунд — переустанавливать код не нужно.
- Только ключом аккаунта; сайт, где вы администратор по приглашению, изменить нельзя —
404, как и чужой.
Удалить и восстановить сайт
curl -X DELETE https://analytics.daratech.ru/api/v1/sites/7b3c1e42-9f58-4d0a-8b61-2c4e5f7a9d13 \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "shop.example.ru"}'
- Удаление подтверждается основным доменом в поле
domain— так же, как в кабинете его вводят руками. Не тот домен —422, и сайт остаётся на месте: одна опечатка в идентификаторе не должна снести живой сайт. - Данные не стираются сразу: сайт получает
status: "deleted", приём хитов прекращается в течение минуты, а отчёты по нему больше не отдаются. В ответе приходитrestore_until— до этого времени сайт можно вернуть. Потом данные удаляются, и вернуть их уже нельзя. - Повтор удаления безопасен: свой уже удалённый сайт отвечает
200и той же строкой, срок восстановления не продлевается. Запрос, потерянный на таймауте, можно просто повторить. - Сайт, которого нет, и сайт чужого аккаунта отвечают одинаково —
404. - Заблокированный сайт через API не удаляется (
403): сначала разберитесь с блокировкой в поддержке.
curl -X POST https://analytics.daratech.ru/api/v1/sites/7b3c1e42-9f58-4d0a-8b61-2c4e5f7a9d13/restore \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
- Восстановление возвращает сайту
status: "active"вместе со всеми данными, которые ещё не удалены. Тело запроса не нужно. - Сайт не удалён, срок восстановления истёк или очистка уже началась —
422с пояснением вmessage. - Восстановление занимает слот в пределе числа сайтов: если после удаления вы завели новые сайты и упёрлись в предел, сначала освободите место.
Сайт запроса
GET /site отвечает про сайт запроса: ключу сайта — про его сайт, ключу аккаунта — про сайт из заголовка X-Dara-Site. Часовой пояс и валюта нужны, чтобы показывать даты и деньги отчётов так же, как их считает сервис, и не держать их копию в своих настройках; срок хранения — чтобы не просить период, за который данных уже нет; расход квоты объясняет sampled в отчёте.
curl https://analytics.daratech.ru/api/v1/site \
-H "Authorization: Bearer $DARA_ANALYTICS_KEY"
{"site_uuid": "7b3c1e42-9f58-4d0a-8b61-2c4e5f7a9d13",
"site_key": "as_XXXXXXXXXXXXXXXXXXXX",
"name": "Магазин",
"timezone": "Europe/Moscow",
"currency": "RUB",
"first_hit_at": "2026-01-14T09:20:11Z",
"last_hit_at": "2026-09-26T11:59:03Z",
"retention_months": 13,
"quota": {"date": "2026-09-26", "hits_per_day": 1000000, "hits_today": 154302, "sampled_since": null},
"network": {"state": "member", "open": true, "since": "2026-02-01T00:00:00Z"}}
| Поле | Значение |
|---|---|
site_uuid |
идентификатор сайта: его ставят в заголовок X-Dara-Site и в адрес /sites/{site_uuid} |
site_key |
ключ сайта as_… — тот же, что в коде тега; он публичный. По нему видно, про какой сайт пришёл ответ |
name |
название сайта в кабинете |
timezone |
пояс сайта: в нём считаются date_from и date_to отчётов и сутки в измерениях времени |
currency |
валюта сайта, ISO 4217: в ней метрики денег |
first_hit_at, last_hit_at |
первый и последний хит сайта; last_hit_at обновляется не чаще раза в минуту, null — данных ещё нет |
retention_months |
сколько месяцев живут хиты и визиты. Период запроса можно просить до limits.period_months из GET /meta, но глубже этого срока данных нет — отчёт честно вернёт пустоту |
quota.date |
сутки квоты, ГГГГ-ММ-ДД по Москве: квоты считаются московскими сутками, а не сутками сайта |
quota.hits_per_day, quota.hits_today |
предел хитов в сутки и сколько их принято за эти сутки (роботы тоже) |
quota.sampled_since |
с какого времени отчёты сайта считаются по выборке из-за квоты; null — выборки по квоте нет. Если отчёт пришёл с sampled: true, а здесь null, дело не в квоте, и повтор запроса может помочь |
network.state |
участие в сети: member — участник, quarantine — карантин |
network.open |
открыты ли сайту данные сети сейчас: участник и есть свежие хиты |
network.since |
с какого времени сайт участник; null — ещё не был |
Смена срока хранения задним числом не действует: у каждой записи свой срок, поставленный при приёме. После увеличения срока старые данные не возвращаются, после уменьшения — исчезают по своему сроку. Условия участия в сети и что делать с карантином — в разделе Сеть ДАРА.
MCP: подключить ИИ-ассистента
https://analytics.daratech.ru/api/v1/mcp — сервер MCP (Model Context Protocol): ИИ-ассистент, который его поддерживает, сам строит отчёты по вашему вопросу — «откуда пришли покупатели в сентябре и как это сравнить с августом» — и отвечает цифрами из ваших данных. Своих данных у MCP нет: каждый инструмент — это запрос к этому же API вашим ключом, с теми же правилами, лимитами и без персональных данных.
Как подключить. В настройках ассистента добавьте удалённый сервер MCP (транспорт Streamable HTTP) с адресом https://analytics.daratech.ru/api/v1/mcp. Дальше — один из двух способов входа.
- Через кабинет (OAuth) — так подключаются коннекторы Claude, ChatGPT и другие ассистенты, где вставить ключ некуда. Ассистент сам откроет страницу ДАРА АНАЛИТИКИ: войдите в кабинет, проверьте название ассистента и адрес, куда он вернётся, и нажмите «Разрешить». Ассистент получит доступ на чтение ко всем сайтам вашего аккаунта, как ключ аккаунта. В кабинете, в разделе «Аккаунт → Ключи аккаунта», появится ключ «MCP: <название ассистента>» — отзовите его, и ассистент отключится сразу. Токены доступа живут час и обновляются ассистентом сам; если ассистентом не пользоваться 60 дней, подключите его заново.
- По ключу — для ассистентов и программ, где можно задать заголовок:
Authorization: Bearer ak_…(ключ сайта: ассистент работает с одним сайтом) илиAuthorization: Bearer aa_…(ключ аккаунта: ассистент видит ваши сайты и выбирает нужный). Лучше выпустить для ассистента отдельный ключ: его можно отозвать, не трогая остальных интеграций.
Токен OAuth открывает только то, что нужно ассистенту: инструменты MCP и методы чтения, которые они вызывают. Для остальных методов API он не подходит — нужен ключ.
Инструменты — только чтение:
| Инструмент | Что делает |
|---|---|
list_sites |
сайты, доступные ключу: UUID, название, домены, пояс, валюта |
get_site |
пояс, валюта, суточная квота и её расход, срок хранения |
describe |
измерения и метрики для отчётов, коды целей, сегменты и воронки сайта |
run_report |
отчёт по измерениям и метрикам за период, со сравнением периодов |
run_funnel |
сохранённая воронка за период |
Пределы строже, чем у API: ассистент за минуту способен заказать больше отчётов, чем человек за день, а сервер общий.
- Не больше 15 вызовов инструментов в минуту на ключ сайта (60 — на ключ аккаунта) и один вызов за раз. Вызов тратит и обычные лимиты методов API, как любой запрос.
- В отчёте — 20 строк по умолчанию, не больше 100; итоги и общее число строк приходят всегда, чтобы ассистент честно говорил «показаны 20 из 412».
- Если числа посчитаны по выборке, ответ это помечает, и ассистенту прямо сказано предупредить вас.
- Записи нет никакой: ни конверсий и заказов, ни сайтов, целей и сегментов. Выгрузки аудитории и карточки посетителя в MCP тоже нет: длинный список людей разговору не помогает, а портрет посетителя уехал бы в модель ассистента.
О данных в разговоре. Ответы инструментов попадают в контекст ассистента, а что он хранит и на чём учится — решают его правила, а не наши. Значения измерений — адреса и заголовки страниц, UTM-метки, рефереры — пишут посторонние люди; ассистент получает их как данные, а не как указания.
Обмен на уровне протокола — для тех, кто пишет свой клиент:
POST /api/v1/mcp HTTP/1.1
Host: analytics.daratech.ru
Authorization: Bearer ak_…
Content-Type: application/json
Accept: application/json, text/event-stream
{"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {"name": "run_report", "arguments": {"date_from": "2026-09-01", "date_to": "2026-09-30",
"dimensions": ["source_type"], "metrics": ["visits", "revenue"]}}}
- Одно сообщение JSON-RPC 2.0 на запрос, ответ —
application/json; потока событий и сессий нет. Методы:initialize,ping,tools/list,tools/call. Редакции протокола — 2025-11-25, 2025-06-18 и 2025-03-26. - С ключом аккаунта сайт называется аргументом
siteинструмента — UUID сайта изlist_sites. - Отказ API (неверная метрика, лимит) приходит ответом инструмента с
isError: trueи текстом причины — ассистент его прочитает и поправит запрос.
Примеры на PHP
<?php
function dara_analytics(string $method, string $path, ?array $body = null): array
{
$ch = curl_init('https://analytics.daratech.ru/api/v1' . $path);
$options = [
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('DARA_ANALYTICS_KEY'),
'Content-Type: application/json',
],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CONNECTTIMEOUT => 3,
CURLOPT_TIMEOUT => 30,
];
if ($body !== null) {
$options[CURLOPT_POSTFIELDS] = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
}
curl_setopt_array($ch, $options);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($response === false || $status >= 400) {
throw new RuntimeException('ДАРА АНАЛИТИКА: HTTP ' . $status . ' ' . ($response ?: curl_error($ch)));
}
return json_decode($response, true, 512, JSON_THROW_ON_ERROR);
}
$result = dara_analytics('POST', '/conversions', ['conversions' => [
['id' => 'deal-7731', 'goal' => 'crm_deal_won', 'user_id' => 'u-501', 'value' => 12000],
]]);
echo 'Принято: ', $result['accepted'], "\n";
// При оформлении заказа, в запросе посетителя: id посетителя сохраняется вместе с заказом
$cookie = $_COOKIE['_dara_a'] ?? '';
$visitorId = is_string($cookie)
&& preg_match('/\A[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\z/i', $cookie)
? strtolower($cookie)
: null;
// Позже, в фоновой задаче или обработчике очереди, — отправка заказа
$order = [
'transaction_id' => 'T-20931',
'user_id' => 'u-501',
'currency' => 'RUB',
'revenue' => 5480,
'products' => [
['id' => 'SKU-1017', 'name' => 'Платье миди', 'price' => 2490, 'qty' => 2],
['id' => 'SKU-2044', 'name' => 'Ремень', 'price' => 500, 'qty' => 1],
],
];
if ($visitorId !== null) {
$order['visitor_id'] = $visitorId;
}
dara_analytics('POST', '/orders', ['orders' => [$order]]);
$report = dara_analytics('POST', '/reports', [
'dimensions' => ['source_type'],
'metrics' => ['visits'],
'date_from' => '2026-09-01',
'date_to' => '2026-09-14',
]);
foreach ($report['data'] as $row) {
echo $row['dimensions'][0], ': ', $row['metrics'][0], "\n";
}
Примеры на Python
import os
import httpx
api = httpx.Client(
base_url="https://analytics.daratech.ru/api/v1",
headers={"Authorization": f"Bearer {os.environ['DARA_ANALYTICS_KEY']}"},
timeout=30,
)
result = api.post("/conversions", json={"conversions": [
{"id": "deal-7731", "goal": "crm_deal_won", "user_id": "u-501", "value": 12000},
]})
result.raise_for_status()
print("Принято:", result.json()["accepted"])
api.post("/orders", json={"orders": [{
"transaction_id": "T-20931",
"visitor_id": "0b7e6f8a-3c2d-4e5f-9a1b-2c3d4e5f6a7b",
"currency": "RUB",
"revenue": 5480,
"products": [
{"id": "SKU-1017", "name": "Платье миди", "price": 2490, "qty": 2},
{"id": "SKU-2044", "name": "Ремень", "price": 500, "qty": 1},
],
}]}).raise_for_status()
report = api.post("/reports", json={
"dimensions": ["source_type"],
"metrics": ["visits"],
"date_from": "2026-09-01",
"date_to": "2026-09-14",
})
report.raise_for_status()
for row in report.json()["data"]:
print(row["dimensions"][0], row["metrics"][0])
Как встроить
- Очередь на сервере. Копите конверсии и заказы в очереди и отправляйте пачками. При сетевой ошибке,
429или5xxповторяйте ту же пачку с паузой: дубли поidконверсии иtransaction_idзаказа не учитываются. - Таймауты. Не держите ответ посетителю в ожидании API: отправка в аналитику не должна задерживать оформление заказа.
- Ключ. Храните ключ в секретах сервера и не пишите в логи целиком — достаточно префикса.
Машинное описание методов, полей и схем — OpenAPI: GET https://analytics.daratech.ru/api/v1/openapi.json, без авторизации. Его отдаёт сам API, поэтому оно всегда совпадает с тем, что сервис принимает.