Документация

Всё, чтобы запустить A/B-тест на своём сайте: от одной строки кода до чтения итогов и работы через API.

Быстрый старт

  1. Добавьте сайт в кабинете — получите скрипт сайта.
  2. Подключите скрипт сайта: строкой в <head> с CDN Сплита, файлом на своём хостинге или в сборку приложения.
  3. Создайте тест: варианты, доли трафика и цель, которая должна стать чаще.
  4. Покажите вариант: в коде сайта — или без кода, правками страницы или другим адресом. Цели тоже можно засчитывать без кода: по странице, клику или форме. Готовый код для вашего теста будет на его странице.
  5. Проверьте по чек-листу и запустите. Дальше Сплит сам скажет, когда можно делать выводы.

Скрипт сайта

После каждой правки тестов Сплит собирает для сайта один минифицированный скрипт — около 3 КБ сжатым. В нём правила всех тестов сайта: кто попадает в тест, какие варианты, правки страницы, цели без кода. Вариант посетителя скрипт считает сам, прямо в браузере, — запросов «какой вариант?» к Сплиту нет, к нам уходят только показы и цели, пачками и в фоне. Если Сплит недоступен, сайт работает как обычно.

Три способа подключить

С CDN Сплита — рекомендуем
Строка в <head>. Скрипт обновляется сам: правки тестов доходят до посетителей в течение 5 минут, в остальное время браузер берёт его из кэша.
На своём хостинге
Скачайте скрипт на странице сайта в кабинете или забирайте его в деплое и отдавайте со своего сервера или CDN — загрузка сайта не зависит от Сплита совсем. После правок тестов файл нужно обновить. Подробнее — в разделе «Скрипт на своём сайте».
В сборку приложения
Импортируйте файл первой строкой — вариант известен сразу при старте, без загрузки и ожидания. Обновляется вместе с выкладкой приложения.

Обычный способ — async

Страница не ждёт загрузку файла: если он недоступен, посетители видят сайт как обычно. Подходит для вариантов, которые меняет код.

html
<script async src="https://ab.pixeltools.ru/s/ключ.js"></script>

Без мигания — синхронно и до стилей

Если вариант меняет вёрстку через CSS и важно, чтобы посетитель не увидел смену, поставьте строку без async самой первой в <head>. Плата за это — страница ждёт скрипт (он маленький и почти всегда берётся из кэша).

html
<head>
  <script src="https://ab.pixeltools.ru/s/ключ.js"></script>
  <link rel="stylesheet" href="/styles.css">
</head>

Без мигания для правок страницы и редиректа

Если тест меняет страницу без кода или отправляет на другой адрес, поставьте выше кода Сплита строку, которая ненадолго прячет страницу. Сплит снимет скрытие сам, как только применит вариант, а если что-то пойдёт не так — через секунду.

html
<head>
  <!-- 1. Прячет страницу, пока не встанут правки варианта, — не дольше секунды -->
  <style>html.ptsab-hide{opacity:0!important}</style>
  <script>document.documentElement.classList.add('ptsab-hide');setTimeout(function(){document.documentElement.classList.remove('ptsab-hide')},1000)</script>
  <!-- 2. Код Сплита: сам снимет скрытие, как только применит вариант -->
  <script async src="https://ab.pixeltools.ru/s/ключ.js"></script>
</head>

Согласие на куки

Если на сайте баннер согласия, включите в настройках сайта «Ждать согласия на куки». До согласия скрипт не ставит куку и не шлёт события, а посетитель видит сайт как есть. Согласие передаёт ваш баннер одной строкой:

js
// Баннер согласия на куки: посетитель нажал «Принимаю»
window.ptsAb?.consent()

// Скрипт сайта вшит в сборку — согласие можно передать заранее
window.ptsAbOptions = {consent: true}

Несколько поддоменов

Посетитель определяется по куке на домене сайта. Чтобы на example.ru, app.example.ru и id.example.ru он был одним человеком и видел один вариант, укажите в настройках сайта домен куки .example.ru.

Если на сайте есть Content-Security-Policy

text
Content-Security-Policy:
  script-src 'self' https://ab.pixeltools.ru;
  connect-src 'self' https://ab.pixeltools.ru;

Конструкторы: Тильда, WordPress и другие

Строка — в поле «Код в <head>», варианты — CSS-правилами там же.

html
<!-- Тильда, WordPress и другие конструкторы: в «Код в <head>» -->
<script src="https://ab.pixeltools.ru/s/ключ.js"></script>

<!-- Без async и до стилей: вариант встанет до отрисовки, ничего не мигнёт -->
<style>
  html[data-ab-hero="b"] .t-btn { background: #0fa9bd; }
</style>

Скрипт на своём сайте

Скрипт сайта — обычный JS-файл, брать его с CDN Сплита не обязательно. Положите его к себе — и при загрузке страниц сайт вообще не обращается к Сплиту: правила тестов уже внутри файла, вариант считается в браузере. К нам уходят только показы и цели.

  1. Скачайте скрипт — кнопка «Скачать скрипт» на странице сайта в кабинете или постоянный адрес https://ab.pixeltools.ru/s/ключ.js.
  2. Положите файл на свой сервер или CDN, например в public/split-ключ.js.
  3. Подключите его строкой в <head> — или импортом первой строкой в сборку приложения.
  4. Обновляйте после правок тестов: создали, запустили или остановили тест — возьмите файл заново. Удобнее всего — в каждом деплое.

Забирать в деплое

bash
# В скрипте деплоя сайта — свежий скрипт Сплита рядом со статикой
curl -fsS -o public/split-ключ.js https://ab.pixeltools.ru/s/ключ.js

Подключить в <head>

html
<script async src="/split-ключ.js"></script>

<!-- или через библиотеку: init('ключ-сайта', {src: '/split-ключ.js'}) -->

Или вшить в сборку приложения

Вариант известен сразу при старте приложения — без загрузки и ожидания.

js
// main.js — первой строкой
import './split-ключ.js'

if (ptsAb.variant('home-cta') === 'b') {
  // новый вариант
}

// Вход на сайте: до этого импорта, например в index.html
// <script>window.ptsAbOptions = {user: 'ID пользователя'}</script>

Пока файл не обновлён, посетители видят старые правила: новый тест на сайте не начнётся, а остановленный продолжит показываться (в статистику такие показы уже не попадут). Нужно, чтобы остановка срабатывала сама и быстро, — подключайте скрипт с CDN Сплита.

Библиотека split.js

Для сайтов со сборкой (React, Vue, Vite, webpack) — маленькая библиотека без зависимостей. Строка в <head> не нужна: init() подгрузит готовый файл с правилами тестов сайта — статику с CDN, которую браузер потом берёт из кэша. Вариант считается прямо в браузере, к серверу Сплита за ним никто не ходит. variant() ждёт только загрузку этого файла (на первой странице — десятки миллисекунд, дальше мгновенно из кэша, но не дольше полутора секунд), goal() отметит цель, даже если файл ещё грузится. Не хотите ждать и на первой странице — положите файл к себе (раздел «Скрипт на своём сайте») или считайте вариант на сервере через Split.php.

js
import {init, variant, value, goal} from './lib/split.js'

// Один раз при старте: подключит скрипт сайта с CDN Сплита.
// Свой хостинг — {src: '/split-ключ.js'}; вход на сайте — {userId: user.id}
init('ключ-сайта')

// Вариант посетителя; null — показывайте как сейчас
if (await variant('home-cta') === 'b') {
  showNewButton()
}
const title = await value('home-cta', 'title', 'Как сейчас')

// Цель — после того, как действие случилось
goal('signup')

Рядом с каждым файлом — типы TypeScript (.d.ts): автодополнение и проверка работают без настройки. Архив «Всё для фронтенда» содержит библиотеку, обёртки и типы.

Если файл с правилами не загрузился (блокировщик, сеть), variant() вернёт null — показывайте сайт как сейчас. Посетитель останется на контроле и в статистику не попадёт.

Сервер и свои SDK

Сервер: вариант до рендера

Split.php — PHP 8.1+, без зависимостей. Считает вариант тем же способом и по той же куке, что скрипт сайта в браузере: бэк и фронт видят посетителя в одном варианте. Правила берёт из готового файла сайта и кэширует — если Сплит недоступен, работает на последних. Куку посетителя ставит ответом сервера, поэтому Safari её не урезает, а показы и цели отправляет одним запросом уже после того, как посетитель получил страницу.

Архив — готовый Composer-пакет. Распакуйте его в packages/split/, подключите как локальный репозиторий и выполните composer update pixeltools/split — автозагрузка и провайдер Laravel подключатся сами:

json
{
  "repositories": [
    {"type": "path", "url": "packages/split"}
  ],
  "require": {
    "pixeltools/split": "*"
  }
}
php
<?php

use PixelTools\Split\Split;

// Кука посетителя, предпросмотр по ссылке и отправка событий после ответа — сами
$split = Split::fromGlobals('ключ-сайта', userId: $user?->id);

// Вариант до рендера: без мигания и без JS. null — показывайте как сейчас
$hero = $split->variant('home-cta') === 'b' ? 'hero-new' : 'hero';
$title = $split->value('home-cta', 'title', 'Как сейчас');

// Цель, которую знает только сервер: оплата, регистрация через OAuth
$split->goal('purchase', 4990);

Laravel

Провайдер делает всё сам: один Split на запрос через DI, пользователь из auth, правила в кэше приложения (Cache::flexible — устаревшие отдаются, пока в фоне не придут свежие), кука без шифрования и отправка событий после ответа. В шаблонах — директивы @variant и @splitValue.

php
// config/services.php
'split' => [
    'key' => env('SPLIT_KEY'),
],

// Провайдер подключается сам (package discovery) — в bootstrap/providers.php ничего добавлять не нужно

Node.js, Python и Go

Те же SDK на других языках — по одному файлу без зависимостей. Вариант считается тем же способом, что в браузере и Split.php: это проверяется тестом на сотнях посетителей во всех четырёх языках. Файлы — в карточках выше.

js
import {Split} from './split-node.mjs'

// Express, Fastify, Next.js — любой обработчик запроса
const split = await Split.fromRequest('ключ-сайта', req, {userId: user?.id})
res.setHeader('Set-Cookie', split.setCookies())

if (split.variant('home-cta') === 'b') {
  // новый вариант; null — показывайте как сейчас
}
const title = split.value('home-cta', 'title', 'Как сейчас')
split.goal('signup')

// Показы и цели — после ответа
res.on('finish', () => split.flush())

Правила для других языков

Те же правила в JSON — для своего SDK на Go, Python, Node. Расчёт варианта простой, его можно повторить на любом языке:

js
GET https://ab.pixeltools.ru/s/ключ.json
{
  "key": "ключ",
  "cookieDomain": ".example.ru",
  "endpoint": "https://ab.pixeltools.ru/api/v1/collect",
  "experiments": [
    {"key": "home-cta", "salt": "x9k2ab1c", "traffic": 100,
     "variants": [["a", 50], ["b", 50, {"p": {"title": "Попробовать"}}]]}
  ],
  "goals": [{"n": "thanks", "on": "url", "m": "/thanks"}]
}

// Вариант: FNV-1a 32 бита + fmix32 (как в Split.php)
// в тесте, если hash(salt + ':t:' + visitorId) % 100 < traffic
// вариант — hash(salt + ':v:' + visitorId) % сумма_весов, по весам по порядку

Создание теста

Мастер в кабинете проводит по четырём шагам. Что значит каждое поле:

Название и гипотеза
Для вас: что меняем и что должно вырасти. «Если поднять кнопку выше первого экрана, регистраций станет больше».
Ключ теста
Латиница, цифры, - и _. По нему код сайта узнаёт вариант: ptsAb.variant('home-cta'). После запуска не меняется.
Как показываем
Кодом на сайте — разработчик показывает вариант в коде. Правками страницы — без кода: новый текст, стиль, скрытие элемента. Разными страницами — у варианта свой адрес. Подробнее — в разделе «Без программиста».
Варианты
Первый — контроль, сайт как есть. У каждого — ключ (a, b, c…) и доля. Доли относительные: 50 и 50 — пополам, 1 и 3 — четверть и три четверти.
Трафик
Какая часть посетителей попадает в тест. Остальные видят сайт как есть и в статистику не попадают.
Цели
Действия, которые должны стать чаще. Основная решает, кто победил; остальные видны рядом — чтобы заметить, если рост одного сломал другое.
Страницы
Для тестов на CSS: посетитель засчитывается, когда открыл страницу с этой частью адреса. Для правок страницы — где их применять (пусто — везде). Для разных страниц — исходная страница, с которой посетителей вариантов отправит на их адреса.

Вариант закрепляется за посетителем: он увидит его и при следующих визитах. Поэтому после запуска ключ, варианты и доли не меняются — иначе люди переедут между вариантами и итоги смешаются.

Варианты на сайте

ptsAb.variant('ключ') возвращает вариант посетителя и засчитывает показ. Вызывайте его там, где посетитель увидит отличие: если вызвать на каждой странице, в тест попадут и те, кто до изменённой страницы не дошёл.

js
import {init, variant} from './split.js'

init('ключ-сайта') // один раз при старте

const v = await variant('home-cta') // null — показывайте как сейчас
if (v === 'b') {
  // Кнопка выше
}

Пока файл с правилами не загрузился, вариант неизвестен — показывайте сайт как сейчас. Если он так и не загрузился (блокировщик, сеть), посетитель останется на контроле и в статистику не попадёт.

Без программиста

Правки страницы

В мастере выберите «Правками страницы», откройте свой сайт кнопкой «Выбрать на сайте», наведите на элемент и кликните — селектор подставится сам. Что можно сделать с элементом:

  • Заменить текст — новый текст вместо старого
  • Заменить HTML — новая разметка внутри элемента
  • Скрыть — элемент пропадает
  • Атрибут — например href=/new-page или src картинки
  • Свой CSS — любые стили, без выбора элемента

Правки встают сами, в том числе на элементах, которые сайт дорисовал позже. Посетитель попадает в тест, когда на странице есть элемент, который меняет тест, — так же засчитывается и контроль, поэтому сравнение честное.

Разные страницы

У каждого нового варианта свой адрес — например, новый лендинг. На исходной странице Сплит засчитывает показ и сразу отправляет посетителя варианта на его адрес, сохраняя метки и параметры. Контроль остаётся на месте.

Значения вариантов

В тесте «кодом на сайте» можно задать поля — тексты, цены, флаги — со своим значением у каждого варианта. Код сайта читает их один раз, а менять значения можно в кабинете без выкладки сайта.

js
// В кабинете у теста hero заданы поля title и price для каждого варианта
const title = ptsAb.value('hero', 'title', 'Как сейчас') // третий аргумент — если посетитель не в тесте
const price = ptsAb.value('hero', 'price', '990')

// split.js / React / Vue
const title = await value('hero', 'title', 'Как сейчас')
const title = useValue('hero', 'title', 'Как сейчас')

Цели

ptsAb.goal('ключ') отмечает, что посетитель дошёл до цели. Засчитывается одна конверсия на человека и только после того, как он увидел вариант. Ставьте вызов туда, где действие уже точно случилось.

js
// После успешной отправки формы (а не по клику на кнопку — форма может не пройти проверку)
form.addEventListener('submit', async (e) => {
  e.preventDefault()
  const response = await fetch('/lead', {method: 'POST', body: new FormData(form)})
  if (response.ok) window.ptsAb?.goal('lead')
})

Цели без кода

В мастере у цели выберите, как её засчитать: «Открыл страницу» — по части адреса, например /thanks; «Кликнул по элементу» и «Отправил форму» — по селектору, который можно выбрать прямо на сайте. Сплит поймает это сам. Отправку формы надёжнее засчитывать страницей «спасибо»: событие отправки срабатывает и тогда, когда сервер форму не принял.

Цели Яндекс Метрики и Google Analytics

Если цели уже настроены в Метрике или GA, заново размечать сайт не нужно: выберите тип «Цель Яндекс Метрики» или «Событие Google Analytics» и укажите идентификатор. Сплит поймает вызовы ym(…, 'reachGoal', 'order') и gtag('event', 'purchase'), которые сайт уже делает. А в настройках сайта можно указать счётчик Метрики — тогда варианты уйдут туда параметрами визита.

Выручка

Цель можно считать как выручку: в мастере у цели выберите «Выручка» и передавайте сумму вторым аргументом. Сплит сравнит выручку на посетителя — это главная метрика для магазинов: вариант может давать меньше заказов, но больше денег.

js
// Цель-выручка: второй аргумент — сумма заказа
ptsAb.goal('purchase', 4990)

// Split.php — то же на сервере, например после оплаты
$split->goal('purchase', $order->total);

Просмотры страниц, время на видимой вкладке и клики Сплит считает сам — отдельных вызовов не нужно.

Новые и вернувшиеся

По умолчанию «новый» — тот, у кого не было куки Сплита, когда он попал в тест. Если сайт знает лучше (например, человек зарегистрирован давно, но зашёл с нового устройства), передайте сегмент атрибутом:

html
{{-- Вошедший давно — «вернувшийся», только что зарегистрировался — «новый» --}}
<script async
  src="https://ab.pixeltools.ru/s/ключ.js"
  @auth data-segment="{{ auth()->user()->created_at->lt(now()->subDay()) ? 'returning' : 'new' }}" @endauth
></script>

Посетители и устройства

Посетитель для Сплита — это браузер на устройстве. При первом визите он получает случайный ID в куке _ptsab на 400 дней, а вариант считается из этого ID. Тот же ID — тот же вариант при каждом визите, без запросов к нам. Поэтому после запуска нельзя менять варианты и доли: иначе люди переедут между вариантами.

Вошедшие пользователи

Если на сайте есть вход, передайте ID пользователя. На новом устройстве такого человека Сплит выведет ID посетителя из ID пользователя — вариант будет тем же, что на других его устройствах, а в статистике он будет одним человеком. Устройство, где человек уже был до входа, остаётся собой: иначе показ до входа и покупка после разъехались бы по двум людям.

html
<!-- Вошедший пользователь: на всех его устройствах один вариант и один человек в статистике.
     К Сплиту уходит только хеш ID вместе с ключом сайта, сам ID — нет -->
<script async src="https://ab.pixeltools.ru/s/ключ.js"
  @auth data-user="{{ auth()->id() }}" @endauth></script>

// split.js
init('ключ-сайта', {userId: currentUser?.id})

// Split.php — а в Laravel провайдер возьмёт пользователя из auth сам
Split::fromGlobals('ключ-сайта', userId: $user?->id);

Safari и кука с сервера

Safari живут куки, поставленные из JavaScript, только 7 дней, а после перехода по ссылке с рекламными метками — сутки. Кто вернулся позже, получит новый ID и может попасть в другой вариант. Лечится одной строкой на сервере сайта: переставить ту же куку ответом сервера — такие Safari не урезает. Split.php делает это сам.

bash
# В http { }: переставить куку Сплита с сервера, если она пришла от браузера
map $cookie__ptsab $ptsab_cookie {
  "~^[\w-]{8,64}$" "_ptsab=$cookie__ptsab; Path=/; Max-Age=34560000; SameSite=Lax";
  default "";
}

# В server { }: пустое значение nginx не отправляет
add_header Set-Cookie $ptsab_cookie;

Почему не отпечаток браузера

Отпечаток (fingerprint) узнаёт человека по модели устройства, шрифтам и экрану без его ведома. Это персональные данные, для которых нужно согласие; у тысяч людей с одинаковыми телефонами отпечаток совпадает и склеивает их в одного; Safari, Firefox и Brave специально его искажают, и посетитель начинает прыгать между вариантами. Кука плюс ID пользователя надёжнее и честнее.

Инкогнито и очистку кук не обходит никто. Такие посетители засоряют все варианты одинаково, поэтому вывод теста не перекашивается — просто нужно чуть больше трафика.

Аудитория и группы

Кому показывать тест

В мастере, на шаге «Варианты», задайте условия: устройство, источник и кампания (utm или сайт, откуда пришли), новый или вернувшийся, вошёл ли на сайт, язык, параметр адреса или свой атрибут. Все условия должны выполниться. Проверяет их сам скрипт сайта — без запросов. Источник запоминается с первого захода: пришёл из Директа — останется «yandex» и на следующих страницах.

html
<!-- Свои атрибуты для таргетинга: тариф, город, сегмент CRM — до скрипта сайта -->
<script>window.ptsAbOptions = {attrs: {plan: 'pro', city: 'msk'}}</script>

<!-- В тесте: условие «Свой атрибут сайта» plan равно pro -->

Группы взаимоисключения

Тесты на одном месте страницы или на одну цель мешают друг другу. Дайте им одну группу — посетитель попадёт максимум в один из них. Доля трафика теста становится его долей внутри группы; если места в группе не осталось, Сплит не даст запустить тест.

Бандит

Режим «Бандит — заработать больше»: раз в час Сплит отдаёт больше трафика лидеру (не меньше 5% каждому варианту). Уже распределённые посетители остаются в своих вариантах. Бандит экономит деньги на слабом варианте, но вывод о причине у него менее строгий — для проверки гипотезы берите обычный режим.

Контрольная группа сайта

В настройках сайта можно отложить, например, 5% посетителей: они не видят ни одного теста и выкаченного варианта. Сравнение с ними показывает общий эффект всех изменений за месяцы работы — то, что отдельные тесты не покажут.

Выкатка и автостоп

Выкатить победителя без программиста

«Выкатить на всех» на странице теста: тест заканчивается, а выбранный вариант скрипт сайта показывает каждому посетителю. Можно сразу пользоваться выигрышем, а разработчик перенесёт изменение в код, когда будет время. После этого — «Вернуть как было».

Защитные цели

Отметьте у цели «защитная» — например, «Оплатил» в тесте на регистрацию. Если новый вариант значимо её просадит, Сплит сам остановит тест и напишет на почту: регистраций может стать больше, но не ценой денег.

Срок и письма

Можно задать дату остановки — Сплит остановит тест сам. Без даты он напишет, когда появится вывод, когда тест сломается (неверное деление трафика) или замолчит (за сутки ни одного показа). Письма получают владелец сайта и редакторы.

Команда и отчёты

  • Команда. На странице сайта пригласите коллег по почте: редактор меняет и запускает тесты, наблюдатель только смотрит отчёты.
  • История изменений — внизу отчёта: кто и когда менял, запускал, останавливал и выкатывал тест.
  • Ссылка на отчёт — «Поделиться отчётом»: итоги открываются без входа, только чтение. Ссылку можно закрыть в любой момент.
  • PDF — «Скачать PDF»: отчёт без кнопок и настроек, готовый для письма или презентации.

Проверка и запуск

На странице теста есть чек-лист: он сам видит, стоит ли скрипт сайта, попадают ли посетители в тест и приходят ли цели.

  • Посмотрите варианты до запуска. Откройте сайт с ?ab_ключ=b в адресе — увидите вариант b. Работает и для черновика, в статистику не попадает.
  • Пройдите путь сами до цели и проверьте, что в чек-листе появилась цель.
  • Запустите тест. В течение 5 минут скрипт сайта обновится у посетителей и начнёт делить их на варианты.
js
// В консоли браузера на своём сайте
ptsAb.visitorId   // ваш идентификатор посетителя
ptsAb.variants    // {"home-cta": "b"} — какие варианты вам выпали

// Посмотреть конкретный вариант, не попадая в статистику:
// https://example.ru/?ab_home-cta=b

Как читать итоги

Сверху отчёта — вердикт одной фразой. Возможные варианты:

Ждём первых посетителей
В тест никто не попал. Проверьте скрипт сайта, запуск и вызов ptsAb.variant().
Собираем данные
Меньше 100 посетителей в варианте или меньше 10 достижений цели — выводов нет.
Пока не ясно
Разница есть, но может быть случайной (p ≥ 0,05). Сплит покажет, сколько ещё ждать.
Есть победитель
Вариант лучше контроля и это не случайность (p < 0,05). Можно оставлять его для всех.
Новый вариант хуже
Все новые варианты значимо хуже контроля — оставьте как было.
Разницы нет
Интервал эффекта уже уместился в ±10%: заметной разницы нет. Выбирайте вариант, который проще поддерживать.
Сломано деление трафика
Посетители разошлись по вариантам не так, как заданы доли (проверка SRM). Тест сломан — итогам верить нельзя.

Что значат цифры

  • Конверсия — доля посетителей варианта, которые дошли до цели после показа. Для целей-выручки — выручка на посетителя.
  • Интервал (усы на полосах) — где на самом деле может быть конверсия. Сильно перекрываются — разница может быть случайной.
  • Эффект «от и до» — в каких пределах на самом деле лежит разница с контролем. Честнее одной цифры.
  • p — насколько разница может быть случайной. Меньше 0,05 — разнице можно верить.
  • Сколько ждать — посетителей на вариант, чтобы текущая разница стала надёжной, и сколько это дней при вашем трафике.

Как считает Сплит

  • Можно смотреть каждый день. Обычный тест «врёт», если проверять его по ходу: случайная «победа» рано или поздно мелькнёт. Сплит считает последовательным тестом (mSPRT, как Optimizely): вывод верен в любой момент, сколько бы раз вы ни открывали отчёт.
  • Несколько вариантов — поправка Холма: чем больше вариантов, тем строже порог, иначе какой-нибудь «выиграет» случайно.
  • Меньше шума — быстрее вывод (CUPED). Сплит учитывает, как посетитель вёл себя за 14 дней до теста, и убирает эту часть разброса. Тот же вывод — на меньшем трафике.
  • Проверка деления трафика (SRM). Если посетители разошлись не так, как заданы доли, тест сломан — Сплит скажет об этом вместо вывода. Обычно виноват редирект, ошибка в варианте или боты.
  • Боты не считаются. Поисковые роботы, мониторинги и автоматизация видят сайт, но в статистику не попадают.
  • Итоги пересчитываются раз в 5 минут.

Дождитесь вердикта и по возможности недели: так тест захватит и будни, и выходные — поведение в них разное.

API

Через API можно забирать итоги в свои отчёты, создавать, запускать и останавливать тесты. Видны только ваши сайты и тесты. Ключ выпускается в настройках кабинета и передаётся в заголовке. Это серверный ключ: на сайт и во фронтенд его не ставьте.

Адрес
https://ab.pixeltools.ru/api/v1
Ключ
Authorization: Bearer ab_…
Формат
JSON. Ответ — в поле data, ошибки проверки — 422 с полем errors.
Ошибки
401 — нет ключа или он отозван, 404 — нет такого сайта или теста у вас, 429 — больше 120 запросов в минуту.
GET/sites

Ваши сайты: адрес скрипта сайта, его правила в JSON и когда сайт последний раз присылал события.

bash
curl https://ab.pixeltools.ru/api/v1/sites \
  -H "Authorization: Bearer $AB_KEY"
json
{
  "data": [
    {
      "id": 1,
      "name": "example.ru",
      "snippet_url": "https://ab.pixeltools.ru/s/k3y.js",
      "rules_url": "https://ab.pixeltools.ru/s/k3y.json",
      "cookie_domain": ".example.ru",
      "last_event_at": "2026-10-26 14:51:09"
    }
  ]
}
GET/sites/{site}/experiments

Тесты сайта со статусом, вариантами, целями и числом посетителей.

bash
curl https://ab.pixeltools.ru/api/v1/sites/1/experiments \
  -H "Authorization: Bearer $AB_KEY"
json
{
  "data": [
    {
      "id": 7,
      "key": "home-cta",
      "name": "Кнопка на главной",
      "status": "running",
      "traffic": 100,
      "variants": [{"key": "a", "name": "Как сейчас", "weight": 50}, {"key": "b", "name": "Выше", "weight": 50}],
      "goals": [{"key": "signup", "label": "Зарегистрировался"}],
      "primary_goal": "signup",
      "visitors": 2400,
      "started_at": "2026-10-12T10:00:00+03:00",
      "stopped_at": null
    }
  ]
}
POST/sites/{site}/experiments

Создать тест. Создаётся черновиком — запустите отдельным запросом. Поля те же, что в мастере кабинета.

bash
curl -X POST https://ab.pixeltools.ru/api/v1/sites/1/experiments \
  -H "Authorization: Bearer $AB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Кнопка на главной",
    "key": "home-cta",
    "traffic": 100,
    "variants": [{"key": "a", "name": "Как сейчас", "weight": 50}, {"key": "b", "name": "Выше", "weight": 50}],
    "goals": [
      {"key": "signup", "label": "Зарегистрировался"},
      {"key": "purchase", "label": "Оплатил", "metric": "revenue", "on": "ym", "match": "order"}
    ],
    "primary_goal": "signup",
    "guardrails": ["purchase"],
    "audience": [{"field": "device", "op": "is", "value": "mobile"}],
    "layer": "hero",
    "allocation": "fixed",
    "ends_at": "2026-12-01T12:00:00+03:00"
  }'
json
{"data": {"id": 8, "key": "home-cta", "status": "draft", "...": "..."}}
GET/experiments/{id}

Один тест.

bash
curl https://ab.pixeltools.ru/api/v1/experiments/7 \
  -H "Authorization: Bearer $AB_KEY"
json
{"data": {"id": 7, "key": "home-cta", "status": "running", "...": "..."}}
GET/experiments/{id}/results

Итоги: вердикт, конверсии с интервалами, p-value, вовлечённость и данные по дням. Параметр segment — all, new или returning.

bash
curl "https://ab.pixeltools.ru/api/v1/experiments/7/results?segment=new" \
  -H "Authorization: Bearer $AB_KEY"
json
{
  "data": {
    "experiment": "home-cta",
    "segment": "new",
    "primary_goal": "signup",
    "verdict": {
      "state": "winner",
      "variant": "b",
      "uplift": 0.214,
      "p_value": 0.004,
      "required": 3900,
      "progress": 1,
      "days_left": 0
    },
    "variants": [
      {
        "key": "a",
        "visitors": 4710,
        "pageviews": 2.1,
        "time": 96,
        "clicks": 5.4,
        "goals": {"signup": {"converted": 184, "rate": 0.039, "ci": [0.0338, 0.0449], "uplift": null, "p_value": null}}
      },
      {"key": "b", "visitors": 4690, "...": "..."}
    ],
    "days": [{"day": "2026-10-12", "variants": {"a": {"visitors": 310, "converted": 11}, "b": {"...": "..."}}}]
  }
}
POST/experiments/{id}/start

Запустить тест (или запустить снова после остановки). В течение 5 минут скрипт сайта обновится у посетителей и начнёт делить их на варианты.

bash
curl -X POST https://ab.pixeltools.ru/api/v1/experiments/7/start \
  -H "Authorization: Bearer $AB_KEY"
json
{"data": {"id": 7, "status": "running", "...": "..."}}
POST/experiments/{id}/stop

Остановить тест: все снова видят сайт как есть, итоги сохраняются.

bash
curl -X POST https://ab.pixeltools.ru/api/v1/experiments/7/stop \
  -H "Authorization: Bearer $AB_KEY"
json
{"data": {"id": 7, "status": "stopped", "...": "..."}}
POST/experiments/{id}/ship

Выкатить вариант на всех без кода: тест заканчивается, скрипт сайта показывает этот вариант каждому посетителю.

bash
curl -X POST https://ab.pixeltools.ru/api/v1/experiments/7/ship \
  -H "Authorization: Bearer $AB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"variant": "b"}'
json
{"data": {"id": 7, "status": "shipped", "shipped_variant": "b", "...": "..."}}
DELETE/experiments/{id}

Убрать тест в корзину: он останавливается и через 90 дней удаляется навсегда. Вернуть можно в кабинете.

bash
curl -X DELETE https://ab.pixeltools.ru/api/v1/experiments/7 \
  -H "Authorization: Bearer $AB_KEY"
json
{"data": {"id": 7, "status": "trashed", "purge_at": "2027-01-24T12:00:00+03:00"}}

Частые вопросы

Сайт мигает при загрузке — видно старый вариант, потом новый
Вариант применяется после загрузки скрипта. Для правок страницы и редиректа поставьте строку против мигания (раздел «Установка»), для CSS-вариантов — скрипт сайта без async первым в <head>. Для вариантов в коде — показывайте нейтральное состояние, пока вариант не известен, или считайте вариант на сервере через Split.php.
Посетитель увидит оба варианта?
Нет: вариант закреплён за ID в куке и не меняется. Вошедшим передайте data-user — тогда вариант один и на всех их устройствах. Для Safari переставляйте куку с сервера (раздел «Посетители и устройства»).
Сплит замедлит сайт?
Нет. Скрипт сайта весит около 4 КБ сжатым и лежит на CDN, браузер берёт его из кэша. Вариант он считает за доли миллисекунды, без запросов к нам. События уходят пачками в фоне через sendBeacon — страница их не ждёт. С async скрипт вообще не блокирует загрузку, а если Сплит недоступен, сайт работает как обычно.
Чем ограничен бесплатный тариф?
Один сайт, два теста одновременно и 20 000 посетителей в тестах за месяц. Расход видно в настройках. Когда лимит исчерпан, скрипт продолжает работать, идущие тесты собирают данные — нельзя только запустить новый.
Сайт ходит к Сплиту за каждым вариантом?
Нет. Правила лежат готовым файлом, вариант считается в браузере или в Split.php. К Сплиту уходят только показы и цели — пачками и в фоне. Сплит недоступен — сайт работает как обычно, посетители видят контроль.
Можно ли вести несколько тестов на одной странице?
Да, если они меняют разные вещи и не влияют на одну и ту же цель. Иначе результаты будут мешать друг другу — лучше по очереди.
Блокировщики рекламы ломают тест?
Если скрипт заблокирован, посетитель видит контроль и в статистику не попадает. На выводы это не влияет, только чуть уменьшает трафик теста.
Что если поменять тест после запуска?
Название, гипотезу и цели — можно. Ключ, варианты и доли — нет: создайте новый тест.
Сплит мешает Яндекс Метрике?
Нет, это независимые скрипты. Вариант посетителя можно передать в Метрику параметром визита: ym(id, 'params', ptsAb.variants).
Как удалить тест и что будет с данными?
На странице теста или в списке — «В корзину». Тест останавливается и хранится в корзине 90 дней вместе со всеми данными: оттуда его можно вернуть. Через 90 дней он удаляется навсегда. Удалить сразу — кнопка «Удалить навсегда» в корзине.