Пошук AItransform для розробників
Один тег підключає віджет із фільтрами, підказками під час введення, бейджами та блоком статей. Три HTTP-методи дають повний доступ до пошуку з вашого коду: результати по категоріях, фільтри й фасети (зокрема за атрибутами фіду), підказки, події кліку та покупки. Усі приклади нижче — реальні відповіді демо-каталогу.
Початок роботи
key у тілі JSONGET /v1/health → {"ok": true}Ключ
Ключ видається в кабінеті після активації акаунта. Він передається у полі key тіла запиту — заголовків авторизації немає. Один ключ = один каталог (індекс).
Невідомий, вимкнений або прострочений ключ не дає помилки на пошуку: /v1/search і /v1/suggest відповідають 200 з порожнім результатом. Лише /v1/event повертає 404 unknown key. Тому під час інтеграції перевіряйте total і наявність поля lang — у відповіді за невідомим ключем його немає.
CORS і дозволені домени
API відповідає Access-Control-Allow-Origin: * на методи POST, GET, OPTIONS: викликати його з браузера можна напряму, без проксі. Обмеження задається не CORS, а списком доменів у кабінеті («Налаштування»):
- Хост береться із заголовка
Origin, якщо його немає — зReferer. Запит без обох (сервер-сервер) проходить перевірку. - Порожній список — обмежень немає.
- Запис
shop.uaдозволяєshop.uaі всі піддомени; запис=shop.ua— лише точний збіг. Схема, порт, шлях і регістр не враховуються. - Не в списку —
403 {"detail": "origin not allowed for this key"}.
SDK не потрібен
Достатньо fetch або curl. Перший запит:
curl -s https://api.aitransform.fun/v1/search \
-H "Content-Type: application/json" \
-d '{"key": "ВАШ_КЛЮЧ", "q": "шолом", "per_category": 1}'
Версіонування
Версія зафіксована у шляху: /v1. Зміни в межах v1 лише додаткові — з'являються нові необов'язкові параметри та нові поля відповіді, наявні поля не перейменовуються і не зникають. Ігноруйте невідомі поля та не покладайтеся на порядок ключів у JSON. Поля, позначені нижче як «лише коли…», можуть бути відсутні.
Віджет
Найшвидший спосіб підключення: один тег наприкінці <head>. Сніпет із вашим ключем є в кабінеті.
<script defer src="https://aitransform.fun/cdn/widget.js" data-key="ВАШ_КЛЮЧ"></script>
Атрибути data-*
| Атрибут | За замовчуванням | Значення |
|---|---|---|
data-key | обов'язковий | Ключ каталогу. Без нього лоадер пише в консоль [AItransform] data-key не указан в <script> і нічого не завантажує. |
data-lang | uk | Мова інтерфейсу та голосового вводу: uk або ru (будь-яке інше значення = uk). |
data-currency | грн | Підпис валюти біля ціни. data-currency="" — без валюти. |
data-in-stock | увімкнено | Показувати лише товари в наявності. 0 або false — показувати й відсутні. |
data-input-selector | автовизначення | CSS-селектор поля пошуку на сайті, до якого прив'язується віджет (клік відкриває панель). Без атрибута шукається input[type="text"], input[type="search"] або поле з placeholder «Поиск» / «Search». |
data-datalayer | вимкнено | 1 або true — дублювати події aitransform_search / aitransform_click / aitransform_purchase у window.dataLayer (GTM). |
data-suggest | як у кабінеті (типово вимкнено) | Рядок підказок «Можливо, ви шукаєте» під час введення: 1 — показувати, 0 — ні. Без атрибута діє перемикач кабінету (нижче). |
data-filters | як у кабінеті (типово вимкнено) | Панель фільтрів (сортування, ціна, бренди, атрибути): 1 — показувати, 0 — ні. Без атрибута діє перемикач кабінету. |
Перемикачі в кабінеті
Підказки «Можливо, ви шукаєте» і панель фільтрів типово вимкнені (покупець бачить результати й категорії, як раніше); вони вмикаються в кабінеті («Налаштування») для всього каталогу. Сервер повідомляє поточний стан у полі widget ({suggest, filters}) кожної відповіді /v1/search і /v1/suggest, а віджет застосовує його з наступної відповіді — без перезбирання й без змін на сайті. Атрибути data-suggest / data-filters на тезі перевизначають кабінет для конкретного сайту; без атрибута діє налаштування кабінету. З вимкненими підказками віджет не викликає /v1/suggest під час набору (лише при відкритті, для блоку «Популярні запити»); з вимкненою панеллю не надсилає facets: true, а обрані фільтри й сортування скидаються.
Що бачить покупець
Панель віджета показує все, що повертає /v1/search, — окремої розмітки на сайті не потрібно:
- Фільтри й сортування. Поруч із результатами — панель з вибором сортування (за релевантністю, спочатку дешевші, спочатку дорожчі), полями «від — до» та чипами цінових діапазонів із гістограми
facets.price, брендами зfacets.brands(перші 12, далі «Показати ще») та групами атрибутів фіду зfacets.params(Колір, Розмір… — перші 6 ключів, далі «Показати ще»). Активні фільтри зібрані чипами над панеллю, кожен знімається окремо, «Скинути» прибирає все. На телефоні панель захована за кнопкою «Фільтри» з лічильником активних фільтрів. Групи без значень не показуються, а чипи діапазонів зникають, коли розкиду цін немає, тож каталог без атрибутів бачить лише те, що має сенс. Панель можна вимкнути в кабінеті або атрибутомdata-filters="0". - Підказки під час введення. Від 2 символів, через 150 мс після останньої клавіші, віджет питає
/v1/suggestі показує рядок «Можливо, ви шукаєте»: до 6 популярних запитів і до 4 категорій («у категорії …»). Клік підставляє запит (для категорії — одразу відкриває її), а сам набраний текст підказкою не вважається. Рядок можна вимкнути в кабінеті або атрибутомdata-suggest="0". - Бейджі на картках. З поля
badgesтовару: «Акція» (sale; не дублюється, коли поруч уже закреслена стара ціна), «Новинка» (new), «Хіт» (hit); закріплений правилом товар (pinned) отримує «Рекомендовано». - «Статті та сторінки». Коли у відповіді є блок
content(клієнт підключив контент-фід, нижче), над категоріями з'являється список до 5 статей, сторінок, акцій чи пунктів FAQ зі сніпетом і типом. Клік відкриває сторінку в новій вкладці й дає подіюaitransform:clickзcontent: true; до/v1/eventтакий клік не надсилається, щоб не змішувати сторінки з артикулами. - Порядок. Категорії віджет упорядковує сам, крім відповідей із
rules_appliedcategoryабоpin— тоді зберігається порядок сервера. Сортування за ціною переставляє товари всередині категорій, порядок категорій не змінює. - Уточнення — не новий пошук. Зміна фільтра чи сортування перезапитує сервер одразу (без затримки), але не породжує нову подію
aitransform:searchі не запускає редирект правила; в аналітику кабінету такі запити теж не потрапляють.
Як монтується
- Лоадер захищений від подвійного підключення (
window.__aitransformLoaded) і зберігає конфігурацію уwindow.AITRANSFORM. - Стилі додаються у
<head>; контейнер<div id="aitransform-root">створюється наприкінці<body>автоматично — окремий контейнер розмічати не потрібно. - Віджет прив'язується до наявного поля пошуку сайту. Панель також відкривається за хешем
#/search/в URL. - Ідентифікатор сесії (
session_id) — UUID уsessionStorage["ait_sid"]; останні запити —localStorage["ait_recent"](до 5). - До API віджет надсилає
POST /v1/searchз тілом{key, q, in_stock, session_id, facets: true}(facets: true— лише поки панель фільтрів увімкнена); коли покупець щось обрав у панелі, додаютьсяfilters(лише непорожні частини:price_min,price_max,brands,params) іsort(лишеprice_asc/price_desc). При відкритті —POST /v1/suggestз{key, q: "", limit: 5}для блоку «Популярні запити», під час набору — з{key, q, limit: 6}. Виправлення розкладки, одруків, склейку слів і морфологію виконує сервер.
{
"key": "ВАШ_КЛЮЧ",
"q": "шолом",
"in_stock": true,
"session_id": "3f1c…",
"facets": true,
"filters": {"price_min": 500, "price_max": 5000, "brands": ["LS2 Helmets"], "params": {"Колір": ["чорний"]}},
"sort": "price_asc"
}
Події для сайту
Віджет надсилає window.dispatchEvent(new CustomEvent("aitransform:<name>", {detail})). З data-datalayer="1" той самий об'єкт потрапляє у window.dataLayer з полем event.
| CustomEvent | dataLayer event | Поля detail | Коли |
|---|---|---|---|
aitransform:search | aitransform_search | q (string), total (number), corrected (string | null), match_mode (string | null) | Через 1,5 с після останньої відповіді пошуку (запит «устоявся»), або одразу при кліку на товар чи редиректі. |
aitransform:click | aitransform_click | q (string), mpn (string), pos (number), link (string); для кліку по статті додатково content: true, а mpn = URL сторінки | Клік по картці товару (після відправки /v1/event) або по пункту блоку «Статті та сторінки» (без /v1/event). |
aitransform:purchase | aitransform_purchase | mpns (string[]), order_id (string), value (number | null), q (string) | Після виклику window.AITRANSFORM.track("purchase", …) — нижче. |
window.addEventListener("aitransform:search", (e) => {
const { q, total, corrected, match_mode } = e.detail;
if (total === 0) analytics.track("search_no_results", { q });
if (corrected) console.log(`«${q}» показано як «${corrected}» (${match_mode})`);
});
window.addEventListener("aitransform:click", (e) => {
const { q, mpn, pos, link } = e.detail;
analytics.track("search_click", { q, mpn, pos, link });
});
Події магазину: window.AITRANSFORM.track()
Лоадер публікує window.AITRANSFORM.track(type, payload) — спосіб повідомити пошук про покупку, додавання в кошик чи перегляд без власного коду для /v1/event. Викликати можна одразу після тега віджета: до завантаження бандла виклики стають у чергу й надсилаються, щойно віджет завантажиться. Ключ і session_id підставляються самі.
| Аргумент | Значення |
|---|---|
type | purchase, add_to_cart, view або click. Інший тип — виклик ігнорується, повертається false. |
payload.mpns | Масив артикулів (як mpn у відповіді пошуку), до 20; порожні та дублікати відкидаються, довші за 128 символів обрізаються. Можна передати один payload.mpn. |
payload.order_id | Номер замовлення у вашому магазині, до 64 символів. Необов'язковий. |
payload.value | Сума замовлення, число ≥ 0 (рядок «2500.50» теж приймається), округлюється до копійок. Необов'язкова. |
payload.q, payload.pos | Запит (до 200 символів) і позиція — для click / add_to_cart, коли ви їх знаєте. |
Функція повертає true, якщо подію надіслано (sendBeacon, не блокує сторінку), і false для purchase без жодного артикула — сервер відповів би 400. Для purchase віджет додатково генерує aitransform:purchase (і aitransform_purchase у dataLayer при data-datalayer="1").
<script defer src="https://aitransform.fun/cdn/widget.js" data-key="ВАШ_КЛЮЧ"></script>
<script>
// артикули замовлення — ті самі mpn, що у фіді та у відповіді пошуку
window.AITRANSFORM.track("purchase", { mpns: ["184216", "345625"], order_id: "A-1", value: 2500 });
</script>
// кнопка «В кошик» на сторінці товару
window.AITRANSFORM.track("add_to_cart", { mpn: "184216" });
// власна аналітика
window.addEventListener("aitransform:purchase", (e) => {
const { mpns, order_id, value, q } = e.detail; // ["184216", "345625"], "A-1", 2500, ""
analytics.track("purchase", { order_id, value, items: mpns.length });
});
Покупка важить у популярності товару 5 кліків і формує бейдж «Хіт»; сума value у ранжування не потрапляє ніколи — вона лише підсумовується у звіті (докладніше у /v1/event).
Редирект і правила мерчандайзингу
Якщо у відповіді /v1/search є redirect.url (правило «редирект» з кабінету), віджет переходить за адресою лише за явним Enter або після того, як запит простояв без змін 1,5 с. Приймаються тільки адреси http(s)://; будь-яка нова літера, взаємодія з результатами, зміна фільтра або закриття панелі скасовує перехід; адреса поточної сторінки ігнорується. Коли rules_applied містить category або pin, віджет зберігає порядок категорій сервера; інакше впорядковує їх сам.
Пошук
Повертає до 10 категорій (найбільших за кількістю збігів), у кожній — до per_category товарів. Одним запитом: виправлення розкладки й одруків, склейка та розбиття слів, українська морфологія, ціна з тексту запиту, фільтри (зокрема за атрибутами фіду), сортування, пагінація в межах категорії, фасети, бейджі товарів і блок статей.
Запит
| Поле | Тип | За замовчуванням | Обмеження та зміст |
|---|---|---|---|
key | string | обов'язкове | Ключ каталогу; для пошуку береться не більше 64 символів. |
q | string | обов'язкове (може бути порожнім) | Текст запиту. До 200 символів (обрізається по останньому пробілу, не посеред слова; якщо пробілу немає — рівно на 200 символах), керівні та невидимі символи прибираються, пробіли згортаються. Далі: нижній регістр, виділення ціни, видалення стоп-слів, обрізання пунктуації навколо слів; слова довші за 32 символи відкидаються; беруться перші 12 слів. |
in_stock | bool | false | Лише товари в наявності. Те саме, що filters.availability: "in_stock". |
session_id | string | null | null | Ідентифікатор сесії покупця для аналітики (склеювання набору по літерах). До 64 символів. |
filters.price_min, filters.price_max | number | null | null | Межі ціни, кожна від 0 до 1012. Явна межа переважає над знайденою в тексті запиту; якщо min > max — межі міняються місцями. Некоректне значення — 400. |
filters.brands | string[] | null | null | До 20 брендів; порівняння без урахування регістру, значення обрізаються до 64 символів, дублікати й порожні відкидаються. |
filters.availability | string | null | null | in_stock або out_of_stock. |
filters.params | object | null | null | Фільтр за атрибутами фіду: {"Колір": ["чорний", "білий"], "Розмір": ["XL"]} — АБО всередині ключа, І між ключами. Збіг точний зі значенням з фіду, тож передавайте те, що повернув facets.params. До 10 ключів (кожен непорожній, до 64 символів, без крапки), до 20 значень на ключ (кожне до 256 символів; порожні й дублікати відкидаються). Порушення — 400 з українським detail (таблиця помилок); значення не масивом — 422. Уточнення атрибутами не вважається новим пошуком в аналітиці і вимикає закріплення. |
sort | string | relevance | relevance, price_asc, price_desc. Сортування за ціною впорядковує товари всередині категорій; порядок самих категорій не змінюється. |
page | object | null | null | Пагінація однієї категорії: замість 10 категорій повертається одна з потрібним вікном товарів. Увесь конвеєр (включно з виправленням одруків) працює в межах цієї категорії. |
page.category | string | обов'язкове, якщо є page | Назва категорії як у відповіді (categories[].name), до 256 символів. |
page.from | int | 0 | Зсув, 0…10000. |
page.size | int | 20 | Розмір вікна, 1…100; from + size не більше 10000. |
per_category | int | null | null (= 100) | Скільки товарів повертати в кожній категорії, 1…100. |
facets | bool | false | true додає у відповідь блок facets (ціновий діапазон, бренди, наявність, атрибути фіду). |
400) виконується до звернення до пошукового ядра, але лише коли після обробки тексту є що шукати. Запит, що складається з самої ціни або стоп-слів (наприклад, до 500 грн), одразу повертає порожній результат із заповненим price_filter. Неправильний тип поля (рядок замість числа) дає 422.Ціна в тексті запиту
Сервер розпізнає бюджет прямо у фразі, прибирає його з тексту й повертає межі у price_filter. Приклад: шолом до 1000 грн → пошук за шолом, price_filter: {"min": null, "max": 1000}.
| Шаблон | Результат | Приклад |
|---|---|---|
від N до M, от N до M | діапазон | шолом від 500 до 5000 грн |
N - M грн (дефіс, тире) | діапазон — лише з позначкою валюти | 1000-2000 грн; без валюти 20-34-7, 3.00-18 — розміри, не ціни |
до, не дорожче, дешевше, максимум N | верхня межа | дешевше 2000 |
від, дорожче, мінімум N | нижня межа (може поєднуватися з верхньою) | дорожче 2000 не дорожче 5000 |
N грн без ключового слова | верхня межа (бюджет) | шолом 3000 грн |
- Позначки валюти:
грн,грн.,гривень,гривні,uah,₴. Число може мати розділювачі тисяч і десяткову кому або крапку. - Без позначки валюти число вважається ціною, лише якщо воно не менше 50 і після нього немає одиниці виміру (
мм см м л мл кг г шт об дюйм "):до 10 мм,від 12лишаються в тексті пошуку. Число, що продовжується літерою (10w40), ніколи не ціна. - Від'ємна межа відкидається; якщо
min > max— межі міняються місцями. Явніfilters.price_min/price_maxпереважають над знайденими в тексті. - Після виділення ціни з тексту прибираються стоп-слова:
купити купить куплю ціна цена ціни цены дешево дешевий дешеві дешевые дешёвые недорого недорогий недорогі грн гривень гривні uah ₴ замовити заказать замовлення доставка доставкою наличие магазин інтернет-магазин интернет-магазин київ киевта фразав наявності.
Розуміння запиту
Конвеєр іде етапами й запускає наступний лише тоді, коли попередній дав менше 8 товарів (для склейки — менше 50); запит із нормальною видачею нічого додаткового не коштує. Кожне перетворення видно у відповіді: match_mode називає етап, corrected і query_used містять запит, за яким реально шукали, suggestion — варіант, який не застосовано. Усі приклади — з демо-каталогу.
| Що робить сервер | Коли | Приклад |
|---|---|---|
| Розкладка всього запиту | у тексті є латиниця, збігів менше 8 | ijkjv → шолом, 1 514 товарів (layout) |
| Розкладка по словах — перенабираються лише латинські слова без збігів, слова зі збігами лишаються | латинське слово без результатів поруч зі словом або кодом, що має результати | ijkjv ls2 → шолом ls2, 89 товарів; rfh,.hfnjh delta → карбюратор delta, 84 (layout) |
| Склейка слів — сусідні слова від 3 літер пробуються разом | склеєне слово знаходить помітно більше, ніж написане окремо (від 8 до 49 збігів — щонайменше вп'ятеро) | бензо пила → бензопила, 2 955 товарів (compound); мото шолом лишається як є — 48 товарів проти 21 у «мотошолом» (склеєне слово знаходить менше, ніж написане окремо) |
| Розбиття слова — одне довге слово (від 7 літер) без збігів ділиться на два, обидві частини мають бути справжніми словами каталогу | слово не знайдено, а пара знаходить від 3 товарів | тросгазу → трос газу, 425 товарів; ручкагазу → ручка газу, 196 (compound) |
| Українська морфологія — словник hunspell uk_UA у кожному слові запиту | завжди; форми, які раніше «рятувала» лише толерантність до одруків, тепер є повноцінними словами | літієвий → 80 товарів, закриття → 13, літнього → 10 — усе exact, без підказок; товари, знайдені лише за словоформою, стоять після точних збігів |
| Одруки — за довжиною слова (до 3 літер без правок, 4–5 — одна, від 6 — до двох) | слово запиту має менше 3 точних збігів | шолм → шолом, 1 514 (corrected) |
| Виправлення за контекстом — кожне слово існує, але пара не зустрічається: замінюється одне слово | 2+ слова без кодів, менше 8 збігів, заміна знаходить щонайменше 8 товарів і втричі більше, ніж було | стрічка запалювання → свічка запалювання, 501 товар; підкладка циліндра → прокладка циліндра, 742 (corrected). Якщо написана пара має 1–7 товарів, вони лишаються, а заміна йде в suggestion |
- Артикули й коди моделей (
ms180,мс-170,kc3) ніколи не склеюються, не розбиваються й не перенабираються. - Бренд ніколи не переписується виправленням за контекстом; слово, що має власні результати, не замінюється силоміць — лише пропонується.
- Виправлення розкладки за словами і за контекстом повертають ті самі поля банера (
corrected,query_used), що й раніше, тож інтеграції, які вже показують «Показано за запитом …», нічого змінювати не потрібно.
Відповідь
| Поле | Тип | Зміст |
|---|---|---|
query | string | Запит, як його ввели (після обрізання до 200 символів та очищення). Завжди ваш, навіть при відповіді з кешу. |
query_used | string | Текст, за яким реально шукали: без ціни й стоп-слів, з виправленнями. "", коли шукати не було чого. |
corrected | string | null | Запит після виправлення розкладки або одруків, якщо сервер його змінив. Показуйте «Показано за запитом …». null, коли змін не було або результатів немає. |
suggestion | string | null | Схожий варіант, який не застосовано («можливо, ви мали на увазі»). |
match_mode | string | Який етап конвеєра дав результат: таблиця нижче. |
total | int | Кількість знайдених товарів (з урахуванням правил мерчандайзингу). |
categories | array | До 10 категорій — найбільших за кількістю збігів: {name, parent, count, products[]}. parent — коренева батьківська категорія або null. |
categories[].products | array | До per_category товарів, поля — нижче. |
price_filter | {min, max} | null | Чинні межі ціни: з тексту запиту та явних фільтрів разом. |
took_ms | int | Час роботи конвеєра на сервері. Для відповіді з кешу — час початкового обчислення. |
cached | bool | Відповідь узято з кешу (180 с). |
lang | "uk" | "ru" | Мова каталогу з налаштувань кабінету; визначає title_display. Відсутнє лише у відповіді за невідомим ключем. |
widget | {suggest, filters} | Перемикачі віджета з кабінету (обидва bool): показувати підказки під час введення та панель фільтрів (вище). Читається з налаштувань на кожен запит і в кеш не потрапляє — відповідь із кешу несе актуальні значення. Відсутнє лише у відповіді за невідомим ключем. |
page | {category, from, size, total} | Лише коли в запиті був page. total — товарів у цій категорії. |
facets | object | Лише при facets: true: структура нижче. |
content | array | Лише коли у клієнта підключено контент-фід, у запиті є текст і щось знайдено: до 5 статей, сторінок, акцій чи пунктів FAQ — {title, url, type, snippet, image}, приклад нижче. Без контент-фіду ключа немає. |
redirect | {url} | Лише коли спрацювало правило «редирект» для запиту, як його ввели. Результати при цьому теж повертаються — рішення за клієнтом. |
rules_applied | string[] | Лише коли хоча б одне правило мерчандайзингу вплинуло на відповідь: відсортована підмножина ["category", "pin", "redirect"]. |
Поля товару
Повертаються лише ті ключі, які є в документі каталогу.
| Поле | Тип | Зміст |
|---|---|---|
mpn | string | Артикул / ідентифікатор товару з фіду. Використовується в /v1/event та правилах закріплення. |
title | string | Повна назва з фіду (у двомовних каталогах — обидві половини). |
title_display | string | Назва мовою каталогу (lang): для двомовної назви — відповідна половина, інакше повна назва. Саме її показуйте покупцеві. |
price | string | Ціна рядком, наприклад "3054" або "675.62". |
oldprice | string | Лише коли у фіді є стара ціна і вона більша за поточну. |
label | string | Лише коли у фіді є мітка (YML label/sales_notes, GMC custom_label_0, CSV label). |
product_type | string | Категорія товару — збігається з categories[].name. |
availability | string | in_stock або out_of_stock. |
image_link | string | Зображення; після конвертації — WebP на https://aitransform.fun/imgcache/…. |
link | string | Посилання на сторінку товару на вашому сайті. |
pinned | true | Лише на товарі, закріпленому правилом мерчандайзингу. |
badges | string[] | Лише коли непорожній: підмножина ["sale", "new", "hit"] у цьому порядку. sale — стара ціна вища за поточну; new — товар створено за останні 30 днів (потрібна дата created_at у документі, фіди її поки не дають); hit — товар у топ-5 % за подіями покупців (нічний перерахунок популярності, від 20 оцінених товарів, щонайменше 10 хітів). Віджет показує їх як «Акція / Новинка / Хіт». |
Значення match_mode
Конвеєр іде етапами, доки не знайде результат. Кожен наступний етап запускається лише тоді, коли попередній дав замало.
| Значення | Що сталося |
|---|---|
exact | Знайдено всі слова запиту (з урахуванням словоформ). Це ж значення повертається, коли не знайдено нічого взагалі (total: 0, corrected: null). |
layout | Виправлено розкладку клавіатури — всього запиту (ijkjv → шолом) або лише окремих латинських слів без збігів (ijkjv ls2 → шолом ls2). Спрацьовує, коли в тексті є латиниця і збігів менше 8. |
compound | Слова склеєно або розбито: бензо пила → бензопила, тросгазу → трос газу. corrected і query_used містять переписаний запит, як і для layout. |
phonetic | Слово (кирилиця, від 6 літер, без збігів) знайдено за звучанням в іншому алфавіті: вудман → WOODMAN. |
corrected | Виправлено одрук («ви мали на увазі» застосовано): шолм → шолом; коротше за 6 літер слово в іншому алфавіті теж іде цим шляхом: дилта → delta. Те саме значення дає виправлення за контекстом: стрічка запалювання → свічка запалювання. |
fuzzy | Нечіткий збіг як останній засіб. |
relaxed | Запит скорочено до частини слів (наприклад, відкинуто бренд). Вимкнено за замовчуванням, вмикається на боці сервера. |
Фасети
При facets: true відповідь отримує блок:
{
"price": {
"min": 43.0,
"max": 12488.0,
"buckets": [
{"from": 0.0, "to": 2500.0, "count": 962},
{"from": 2500.0, "to": 5000.0, "count": 533},
{"from": 5000.0, "to": 7500.0, "count": 12},
{"from": 7500.0, "to": 10000.0, "count": 3},
{"from": 10000.0, "to": 12500.0, "count": 4}
]
},
"brands": [
{"name": "LS2 Helmets", "count": 89},
{"name": "VLAND", "count": 82},
{"name": "FXW", "count": 81}
],
"availability": [
{"name": "in_stock", "count": 1514}
],
"params": []
}
price:min/max(число абоnull) і до 8 суміжних діапазонів із «круглим» кроком (1 / 2 / 2,5 / 5 × 10k);buckets: [], коли розкиду цін немає.brands: до 20 брендів, назва — у найпоширенішому написанні з фіду.availability: кількість товарів у наявності та без.params: атрибути з фіду (YML<param>, GMCg:product_detail) — до 8 найпоширеніших ключів каталогу, у кожному до 12 значень з кількістю у поточній видачі; ключі без жодного значення не повертаються. Демо-каталог атрибутів не має, тому вище[]; каталог із фіду відповідає так:
"params": [
{"key": "Колір", "values": [{"value": "чорний", "count": 41}, {"value": "білий", "count": 12}]},
{"key": "Розмір", "values": [{"value": "XL", "count": 30}, {"value": "L", "count": 27}]}
]
- Кожен фасет рахується з усіма фільтрами користувача, крім свого власного: обраний бренд не ховає решту брендів, гістограма цін не звужується під ціновий фільтр, а фасет «Колір» показує всі кольори, навіть коли обрано
filters.params.Колір(інші ключіparamsпри цьому враховуються). Для запиту зpageфасети рахуються в межах цієї категорії. - Набір ключів
paramsвизначається за вибіркою каталогу і кешується на 5 хвилин: атрибут, що є менш ніж у 5 % товарів, у фасети не потрапляє.
Порядок категорій
За замовчуванням категорії впорядковані за релевантністю: ключ сортування — count × (середній score трьох найкращих товарів)², при рівності — за кількістю. Тож невелика категорія з дуже точними збігами може стояти вище за велику з випадковими.
Правила мерчандайзингу з кабінету змінюють цей порядок: категорії із закріпленими товарами йдуть першими (у порядку закріплення), далі top-категорії (у порядку правил), решта — за релевантністю, bottom — останніми; hide прибирає категорію і віднімає її count від total. Закріплений товар, категорії якого немає у відповіді, створює першу категорію з назвою його product_type (або «Рекомендовані»).
Закріплення не застосовується для запиту з page, з сортуванням за ціною, з фільтром брендів, атрибутів (filters.params) або out_of_stock; з in_stock відкидаються закріплені товари без наявності; з ціновими межами — товари поза межами або без ціни. На запиті з page правила top/bottom/hide не діють.
Порядок товарів у категорії. Окрім релевантності та бонусів за наявність і фото, товар без ціни ранжується з коефіцієнтом 0,8, товар без опису у фіді — 0,9: вони опускаються в межах своєї категорії, але з видачі не зникають і на total не впливають. Бонус популярності за подіями покупців (клік 1, кошик 3, покупка 5) залишається не більшим за 5 %.
Кеш і аналітика
- Відповіді кешуються на сервері на 180 с. Ключ кешу — каталог, нормалізований текст (нижній регістр, одинарні пробіли) і всі параметри запиту разом з мовою каталогу. У відповіді з кешу
cached: true, аquery— завжди ваш. - Правила мерчандайзингу застосовуються після кешу: зміна правила в кабінеті видно одразу. Після планового оновлення фіду ціни та наявність у кеші можуть відставати до 180 с.
- В аналітику кабінету потрапляють лише «прості» пошуки: без
page, зsort: "relevance", без брендів, безfilters.params, без явних цінових меж і безavailability, крімin_stock. Уточнення фільтрами новими пошуками не вважаються. Блокcontentмає власний запис у кеші й в аналітику не потрапляє. - Набір по літерах склеюється: запит, що протягом 3 с продовжує попередній із тим самим
session_id, оновлює попередній рядок (ш → шолом= один пошук). Передавайтеsession_id, щоб статистика була чесною. - IP не зберігається — лише хеш із добовою сіллю. Термін зберігання логу — 90 днів за замовчуванням, у кабінеті можна задати від 7 до 365.
Приклади
Усі відповіді нижче — з демо-каталогу (мото-вело запчастини). Для стислості запити містять per_category: 1, а в довгих відповідях показано частину категорій — структура і значення реальні.
{"key": "ВАШ_КЛЮЧ", "q": "шолом", "per_category": 1}
{
"query": "шолом",
"corrected": null,
"total": 1514,
"categories": [
{
"name": "Шоломи закриті (інтеграл)",
"count": 646,
"parent": "Мотоекіпірування",
"products": [
{
"mpn": "345625",
"title": "Шлем 111 №44 Шолом 111 №44",
"price": "3054",
"product_type": "Шоломи закриті (інтеграл)",
"availability": "in_stock",
"image_link": "https://aitransform.fun/imgcache/e6/e60166f45acc782996fb7049561ff5727ab7853f.webp",
"link": "https://motozilla.com.ua/product/shlem-111-44/",
"title_display": "Шолом 111 №44"
}
]
},
{
"name": "Шоломи відкриті (без підборіддя)",
"count": 248,
"parent": "Мотоекіпірування",
"products": [
{
"mpn": "246782",
"title": "Шлем 002 DRIFT Шолом 002 DRIFT",
"price": "1863",
"product_type": "Шоломи відкриті (без підборіддя)",
"availability": "in_stock",
"image_link": "https://aitransform.fun/imgcache/ec/ec3dbfa530bf36a2c72700fc905fdca0abb1cba3.webp",
"link": "https://motozilla.com.ua/product/shlem-002-drift-10-04395/",
"title_display": "Шолом 002 DRIFT"
}
]
},
{
"name": "Велошоломи",
"count": 66,
"parent": "Велозапчастини",
"products": [
{
"mpn": "203073",
"title": "Шлем велосипедный FSK AH404 Шолом велосипедний FSK AH404",
"price": "770",
"product_type": "Велошоломи",
"availability": "in_stock",
"image_link": "https://aitransform.fun/imgcache/71/7107fd1e18c9ff5e96e9ccab07333f80bfa25268.webp",
"link": "https://motozilla.com.ua/product/shlem-velosipednyy-fsk-ah404/",
"title_display": "Шолом велосипедний FSK AH404"
}
]
}
],
"match_mode": "exact",
"took_ms": 148,
"price_filter": null,
"query_used": "шолом",
"suggestion": null,
"lang": "uk",
"widget": {"suggest": true, "filters": true},
"cached": false
}
Фільтри, сортування, пагінація та фасети
{
"key": "ВАШ_КЛЮЧ",
"q": "шолом",
"filters": {
"price_min": 500,
"price_max": 5000,
"brands": ["LS2 Helmets"],
"availability": "in_stock"
},
"sort": "price_asc",
"page": {"category": "Шоломи закриті (інтеграл)", "from": 0, "size": 2},
"facets": true
}
{
"query": "шолом",
"corrected": null,
"total": 44,
"categories": [
{
"name": "Шоломи закриті (інтеграл)",
"count": 44,
"parent": "Мотоекіпірування",
"products": [
{
"mpn": "012760",
"title": "Шлем-интеграл (mod:358) (size:XXL, белый, COOL RIDERS) LS-2 Шолом-інтеграл (mod: 358) (size: XXL, білий, COOL RIDERS) LS-2 LS2 Helmets",
"price": "2165",
"product_type": "Шоломи закриті (інтеграл)",
"availability": "in_stock",
"image_link": "https://aitransform.fun/imgcache/2b/2b71bf09c1237a4b42e3bd6718dcbf43ad145cd1.webp",
"link": "https://motozilla.com.ua/product/shlem-integral-mod358-sizexxl-belyy-cool-riders-ls-2/",
"title_display": "Шолом-інтеграл (mod: 358) (size: XXL, білий, COOL RIDERS) LS-2 LS2 Helmets"
},
{
"mpn": "042329",
"title": "Шлем-интеграл (mod:358) (size:XL, бело-синий с черным) LS-2 Шолом-інтеграл (mod: 358) (size: XL, біло-синій з чорним) LS-2 LS2 Helmets",
"price": "2165",
"product_type": "Шоломи закриті (інтеграл)",
"availability": "in_stock",
"image_link": "https://aitransform.fun/imgcache/1a/1a3aa02fb1e298118998158cf3e2ca88b6efcb8d.webp",
"link": "https://motozilla.com.ua/product/shlem-integral-mod358-sizexl-belo-siniy-s-chernym-ls-2/",
"title_display": "Шолом-інтеграл (mod: 358) (size: XL, біло-синій з чорним) LS-2 LS2 Helmets"
}
]
}
],
"match_mode": "exact",
"took_ms": 61,
"price_filter": {"min": 500, "max": 5000},
"query_used": "шолом",
"suggestion": null,
"page": {"category": "Шоломи закриті (інтеграл)", "from": 0, "size": 2, "total": 44},
"facets": {
"price": {
"min": 364.0,
"max": 8889.0,
"buckets": [
{"from": 0.0, "to": 2000.0, "count": 1},
{"from": 2000.0, "to": 4000.0, "count": 41},
{"from": 4000.0, "to": 6000.0, "count": 3},
{"from": 6000.0, "to": 8000.0, "count": 4},
{"from": 8000.0, "to": 10000.0, "count": 1}
]
},
"brands": [
{"name": "FXW", "count": 63},
{"name": "LS2 Helmets", "count": 44},
{"name": "VIRTUE", "count": 41},
{"name": "VLAND", "count": 37},
{"name": "BEON", "count": 30}
],
"availability": [{"name": "in_stock", "count": 44}],
"params": []
},
"lang": "uk",
"cached": false
}
Зверніть увагу: фасет brands перелічує й інші бренди, хоча в запиті обрано LS2 Helmets, а гістограма цін не звужена до 500–5000 — кожен фасет ігнорує власний фільтр. params: [] — у демо-каталозі немає атрибутів.
Виправлення розкладки та одруків, ціна з тексту
Верхня частина відповідей на три запити (масив categories опущено):
{
"query": "ijkjv",
"corrected": "шолом",
"total": 1514,
"match_mode": "layout",
"took_ms": 36,
"price_filter": null,
"query_used": "шолом",
"suggestion": null,
"lang": "uk",
"cached": false
}
{
"query": "шолм",
"corrected": "шолом",
"total": 1514,
"match_mode": "corrected",
"took_ms": 43,
"price_filter": null,
"query_used": "шолом",
"suggestion": null,
"lang": "uk",
"cached": false
}
{
"query": "шолом до 1000 грн",
"corrected": null,
"total": 383,
"match_mode": "exact",
"took_ms": 19,
"price_filter": {"min": null, "max": 1000},
"query_used": "шолом",
"suggestion": null,
"lang": "uk",
"cached": false
}
{
"query": "до 500 грн",
"corrected": null,
"total": 0,
"categories": [],
"match_mode": "exact",
"took_ms": 0,
"price_filter": {"min": null, "max": 500},
"query_used": "",
"suggestion": null,
"cached": false,
"lang": "uk"
}
Склейка слів, розкладка в одному слові, виправлення за контекстом
Верхня частина відповідей (масив categories опущено); поля банера ті самі, що й для звичайної розкладки чи одруку.
{
"query": "бензо пила",
"corrected": "бензопила",
"total": 2955,
"match_mode": "compound",
"took_ms": 66,
"price_filter": null,
"query_used": "бензопила",
"suggestion": null,
"lang": "uk",
"cached": false
}
{
"query": "тросгазу",
"corrected": "трос газу",
"total": 425,
"match_mode": "compound",
"took_ms": 45,
"price_filter": null,
"query_used": "трос газу",
"suggestion": null,
"lang": "uk",
"cached": false
}
{
"query": "ijkjv ls2",
"corrected": "шолом ls2",
"total": 89,
"match_mode": "layout",
"took_ms": 51,
"price_filter": null,
"query_used": "шолом ls2",
"suggestion": null,
"lang": "uk",
"cached": false
}
{
"query": "стрічка запалювання",
"corrected": "свічка запалювання",
"total": 501,
"match_mode": "corrected",
"took_ms": 190,
"price_filter": null,
"query_used": "свічка запалювання",
"suggestion": null,
"lang": "uk",
"cached": false
}
Бейджі товару та блок статей
Форма полів, які з'являються лише тоді, коли є що показати: badges на товарі зі старою ціною чи в топі популярності, content — у клієнта з підключеним контент-фідом (значення для ілюстрації структури; демо-каталог контент-фіду не має).
{
"query": "шолом",
"corrected": null,
"total": 812,
"match_mode": "exact",
"took_ms": 58,
"price_filter": null,
"query_used": "шолом",
"suggestion": null,
"cached": false,
"lang": "uk",
"categories": [
{
"name": "Шоломи закриті (інтеграл)",
"parent": "Мотоекіпірування",
"count": 646,
"products": [
{"mpn": "184216", "title": "Шлем LS2 FF353 Шолом LS2 FF353", "title_display": "Шолом LS2 FF353", "price": "3990", "oldprice": "4500", "image_link": "…", "link": "…", "availability": "in_stock", "product_type": "Шоломи закриті (інтеграл)", "badges": ["sale", "hit"]}
]
}
],
"content": [
{"title": "Як обрати шолом", "url": "https://shop.ua/blog/helmet", "type": "article", "snippet": "…Шолом має бути зручним. Розмір визначає обхват голови…", "image": "https://shop.ua/i/helmet.jpg"}
]
}
content[].type—article,page,promo,faqабоother;snippet— до 200 символів тексту навколо першого слова запиту;imageлише коли є.- Блок шукається за тим самим текстом, що й товари (без ціни та стоп-слів), тож запит лише зі стоп-слів («доставка») контенту не дає — для таких запитів є правило «редирект».
Відповідь із правилами мерчандайзингу
Форма додаткових полів, коли в кабінеті налаштовано редирект, закріплення та порядок категорій (значення для ілюстрації структури):
{
"query": "шолом",
"corrected": null,
"total": 1516,
"match_mode": "exact",
"took_ms": 58,
"price_filter": null,
"query_used": "шолом",
"suggestion": null,
"cached": false,
"lang": "uk",
"redirect": {"url": "https://shop.ua/helmets"},
"rules_applied": ["category", "pin", "redirect"],
"categories": [
{
"name": "Шоломи закриті (інтеграл)",
"parent": "Мотоекіпірування",
"count": 646,
"products": [
{"mpn": "184216", "title": "…", "title_display": "…", "price": "3054", "image_link": "…", "link": "…", "availability": "in_stock", "product_type": "Шоломи закриті (інтеграл)", "pinned": true}
]
}
]
}
curl і fetch
curl -s https://api.aitransform.fun/v1/search \
-H "Content-Type: application/json" \
-d '{"key": "ВАШ_КЛЮЧ", "q": "шолом до 1000 грн", "in_stock": true, "per_category": 20, "facets": true}'
const res = await fetch("https://api.aitransform.fun/v1/search", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
key: "ВАШ_КЛЮЧ",
q: "шолом",
session_id: sessionId, // ваш UUID сесії покупця
filters: { availability: "in_stock" },
sort: "relevance",
per_category: 20,
facets: true
})
});
if (!res.ok) throw new Error((await res.json()).detail);
const data = await res.json();
if (data.redirect) location.assign(data.redirect.url);
for (const cat of data.categories) {
console.log(cat.name, cat.count, cat.products.map((p) => p.title_display));
}
Підказки
Автодоповнення для поля вводу: популярні запити покупців, категорії та товари за префіксом. Відповіді кешуються на 60 с.
| Поле запиту | Тип | За замовчуванням | Зміст |
|---|---|---|---|
key | string | обов'язкове | Ключ каталогу. |
q | string | "" | Префікс (до 200 символів; нижній регістр, одинарні пробіли). |
limit | int | 5 | Кількість запитів і товарів. Тихо обмежується діапазоном 1…10, помилки не буде. |
| Поле відповіді | Зміст |
|---|---|
queries[] — {q, count} | Популярні запити з результатами за останні 30 днів, чия показана форма (після виправлення) починається з q; до limit. Одруки складаються у своє виправлення. |
categories[] — {name, count} | До 5 категорій серед товарів, назви яких відповідають префіксу. Не залежить від limit. |
products[] — {mpn, title, price, image_link, link} | До limit товарів за автодоповненням назви. Полів availability і product_type тут немає. |
widget — {suggest, filters} | Перемикачі віджета з кабінету (bool), ті самі, що й у /v1/search (вище); є і при порожньому q, у кеш не потрапляє. Відсутнє лише у відповіді за невідомим ключем. |
Порожній q повертає 8 найпопулярніших запитів за 30 днів і порожні categories та products — так віджет будує блок «Популярні запити». Невідомий ключ — 200 {"queries": [], "categories": [], "products": []}.
{"key": "ВАШ_КЛЮЧ", "q": "шол", "limit": 2}
{
"queries": [
{"q": "шолом", "count": 190},
{"q": "шолом до 1000 грн", "count": 33}
],
"categories": [
{"name": "Шоломи закриті (інтеграл)", "count": 645},
{"name": "Шоломи відкриті (без підборіддя)", "count": 248},
{"name": "Шоломи кросові (ендуро)", "count": 173},
{"name": "Шоломи трансформери (модуляр)", "count": 137},
{"name": "Скло для шоломів (візор)", "count": 92}
],
"products": [
{
"mpn": "345625",
"title": "Шлем 111 №44 Шолом 111 №44",
"price": "3054",
"image_link": "https://aitransform.fun/imgcache/e6/e60166f45acc782996fb7049561ff5727ab7853f.webp",
"link": "https://motozilla.com.ua/product/shlem-111-44/"
},
{
"mpn": "235205",
"title": "Шлем 111 №18 Шолом 111 №18",
"price": "3054",
"image_link": "https://aitransform.fun/imgcache/a1/a120e8346debb4bf016f7b1664fdfc51e0c38744.webp",
"link": "https://motozilla.com.ua/product/shlem-111-18-mtch-10-01770/"
}
],
"widget": {"suggest": true, "filters": true}
}
GET-форма
Та сама відповідь. У GET-запиті ключ потрапляє до URL і журналів доступу, тому з браузера й віджета використовуйте POST.
Події
Повідомляє серверу про дію покупця з результатами пошуку. Віджет надсилає click сам, а покупку, кошик і перегляд сайт передає через window.AITRANSFORM.track(); при власній інтеграції надсилайте події ви.
| Поле | Тип | За замовчуванням | Обмеження та зміст |
|---|---|---|---|
key | string | обов'язкове | Ключ каталогу, до 64 символів. |
type | string | обов'язкове | click, add_to_cart, view або purchase. |
q | string | "" | Запит, за яким показано результати, до 200 символів. |
mpn | string | "" | Артикул товару з відповіді пошуку, до 128 символів. |
mpns | string[] | null | null | Кілька артикулів однією подією (замовлення з кількох товарів): до 20, кожен до 128 символів; порожні й дублікати відкидаються, mpn і mpns об'єднуються. Приймається для будь-якого типу; на кожен артикул пишеться окремий запис. |
order_id | string | null | null | Номер замовлення у магазині, до 64 символів. Пишеться в кожен запис події. |
value | number | null | null | Сума замовлення, 0…1012, зберігається з двома знаками. Ніколи не впливає на ранжування — лише підсумовується у звіті. |
pos | int | null | null | Позиція товару у видачі, 0…10000. |
session_id | string | null | null | Той самий ідентифікатор сесії, що й у пошуку, до 64 символів. |
Успіх — 204 No Content з порожнім тілом. Перевірки йдуть у порядку: тип → довжини → pos → value → артикули → ключ → домен, тому неправильний тип з невідомим ключем дає 400, а не 404. purchase без жодного артикула — 400 purchase needs mpn or mpns; mpns не масивом або value не числом — 422. Тексти помилок цього методу англійські (це помилка інтеграції, а не повідомлення покупцеві).
Що живить. Події формують звіт «Аналітика» в кабінеті: кліки, додавання в кошик, CTR, таблиця кліків за артикулами. Покупки рахуються окремо в даних звіту: кількість, конверсія (покупки ÷ пошуки) і таблиця куплених артикулів із сумою замовлень — сума береться з value, як його передав сайт, і не перевіряється; у кабінеті ці плитки поки не показуються. view приймається і зберігається, у звітах поки не показується.
Популярність товарів. У рушій закладено бонус за подіями: click важить 1, add_to_cart — 3, purchase — 5, події за 30 днів із періодом напіврозпаду 14 днів; пошук множить релевантність на коефіцієнт не більше 1,05, тож популярність переставляє лише товари з близькою релевантністю і ніколи не витягує нерелевантні (view і value не враховуються). Той самий перерахунок позначає топ-5 % товарів бейджем hit. Перерахунок виконується щоночі (о 04:45 за часом сервера) за подіями останніх 30 днів, тож нові кліки й покупки впливають на видачу після наступного перерахунку.
{
"key": "ВАШ_КЛЮЧ",
"type": "purchase",
"mpns": ["184216", "345625"],
"order_id": "A-1",
"value": 2500,
"session_id": "3f1c…"
}
const payload = JSON.stringify({
key: "ВАШ_КЛЮЧ",
type: "click",
q: "шолом",
mpn: "345625",
pos: 0,
session_id: sessionId
});
// не блокує перехід на сторінку товару
if (!navigator.sendBeacon("https://api.aitransform.fun/v1/event", new Blob([payload], { type: "application/json" }))) {
fetch("https://api.aitransform.fun/v1/event", { method: "POST", headers: { "Content-Type": "application/json" }, body: payload, keepalive: true });
}
Помилки
Тіло помилки — {"detail": "…"}. Для 422 поле detail — масив об'єктів {type, loc, msg, input} (валідація типів).
| Код | Метод | Коли | detail |
|---|---|---|---|
| 200 | search, suggest | Невідомий, вимкнений або прострочений ключ | порожній результат: total: 0, query_used: "", без lang; для suggest — порожні масиви |
| 204 | event | Подію прийнято | тіла немає |
| 400 | search | Некоректний sort | sort must be one of relevance, price_asc, price_desc |
| 400 | search | per_category поза 1…100 | per_category must be 1..100 |
| 400 | search | Некоректний filters.availability | filters.availability must be one of in_stock, out_of_stock |
| 400 | search | Понад 20 брендів | filters.brands: at most 20 values |
| 400 | search | Від'ємна, нескінченна або більша за 1012 межа ціни | filters.price_min/price_max must be >= 0 |
| 400 | search | Понад 10 ключів у filters.params | filters.params: забагато ключів: 11 (максимум 10) |
| 400 | search | Порожній, задовгий або з крапкою ключ у filters.params | filters.params: ключ не може бути порожнім, filters.params: ключ задовгий (максимум 64 символи), filters.params: ключ «a.b» не може містити крапку |
| 400 | search | Понад 20 значень або задовге значення у filters.params | filters.params: «Колір»: забагато значень: 21 (максимум 20), filters.params: «Колір»: значення задовге (максимум 256 символів) |
| 400 | search | page без категорії | page.category is required |
| 400 | search | page.from поза 0…10000 | page.from must be 0..10000 |
| 400 | search | page.size поза 1…100 | page.size must be 1..100 |
| 400 | search | from + size понад 10000 | page.from + page.size must be <= 10000 |
| 400 | event | Невідомий type | type must be one of click, add_to_cart, view, purchase |
| 400 | event | Задовге поле (key > 64, q > 200, mpn або елемент mpns > 128, session_id > 64, order_id > 64) | field too long |
| 400 | event | pos поза 0…10000 | pos out of range |
| 400 | event | Понад 20 елементів у mpns | mpns: at most 20 items |
| 400 | event | value від'ємне, NaN або понад 1012 | value must be >= 0 |
| 400 | event | purchase без mpn і mpns | purchase needs mpn or mpns |
| 403 | search, suggest, event | Хост із Origin/Referer не в списку дозволених доменів | origin not allowed for this key |
| 404 | event | Невідомий або неактивний ключ | unknown key |
| 422 | усі | Неправильний тип поля або відсутнє обов'язкове поле | масив, наприклад [{"type": "int_parsing", "loc": ["body", "per_category"], "msg": "Input should be a valid integer, unable to parse string as an integer", "input": "abc"}] |
| 502 | search, suggest | Пошукове ядро недоступне | search backend unavailable |
Ліміти та обмеження
| Параметр | Обмеження | Поведінка при перевищенні |
|---|---|---|
q (search, suggest, event) | 200 символів | обрізається по останньому пробілу (search, suggest); 400 в event |
| Слів у запиті | 12; слово до 32 символів | зайві слова відкидаються мовчки |
key | 64 символи | обрізається (search, suggest); 400 в event |
session_id | 64 символи | обрізається (search); 400 в event |
per_category | 1…100, за замовчуванням 100 | 400 |
filters.brands | 20 значень по 64 символи | 400 понад 20; значення обрізаються |
filters.price_min/max | 0…1012 | 400 |
filters.params | 10 ключів по 64 символи, 20 значень на ключ по 256 символів | 400 з українським текстом |
facets.params у відповіді | 8 ключів по 12 значень | решта не повертається |
content у відповіді | 5 документів, сніпет до 200 символів | решта не повертається |
page.from / page.size | 0…10000 / 1…100, сума до 10000 | 400 |
page.category | 256 символів | обрізається |
| Категорій у відповіді | 10 | категорію поза топ-10 можна запитати через page, якщо ви знаєте її назву (з фіду) |
limit у suggest | 1…10, за замовчуванням 5 | тихо приводиться до діапазону |
mpn / pos в event | 128 символів / 0…10000 | 400 |
mpns / order_id / value в event | 20 артикулів по 128 символів / 64 символи / 0…1012 | 400 |
| Кеш відповідей | search 180 с, suggest 60 с | — |
| Тайм-аут пошукового ядра | 15 с на виклик | 502 |
Фіди й оновлення каталогу
Каталог завантажується з вашого фіду за URL (http/https, gzip підтримується; адреси приватних мереж не приймаються). URL і формат задаються в кабінеті, там же кнопка «Індексувати».
| Формат | Що читається |
|---|---|
yml (Prom.ua, Яндекс YML) | <offer>: name або model, vendorCode або @id → mpn, vendor, price, oldprice, categoryId → назва з дерева <category>, picture, url, @available (за замовчуванням в наявності), label або sales_notes, <param name>. |
google (Google Merchant RSS) | <item>: g:title/title, g:id або g:mpn, g:brand, g:price (за наявності g:sale_price вона стає ціною, а g:price — старою ціною), g:product_type або g:google_product_category, g:image_link, g:link/link, g:availability, g:custom_label_0 → мітка, g:product_detail → параметри. |
csv (UTF-8, заголовки без урахування регістру) | назва title|name|название|назва; артикул id|sku|mpn|артикул|код; бренд brand|vendor|бренд; ціна price|цена|ціна; категорія category|product_type|категория|категорія; зображення image|image_link|picture; посилання link|url|ссылка; наявність availability|available; стара ціна oldprice|old_price|старая цена|стара ціна; мітка label|метка|мітка; опис description|опис|описание. Рядки без назви пропускаються. |
- Документ у каталозі:
mpn(ідентифікатор),title(назва + бренд, якщо його ще немає в назві),brand,price,product_type,image_link,link,availability, за наявностіoldprice(лише коли більша за ціну),label(до 64 символів),description(до 2000 символів; товар без опису ранжується трохи нижче в своїй категорії),params(до 50; ключ до 64 символів, значення до 256 — саме вони стають фасетамиfacets.params). - Розмір фіду: до 512 МБ (і стиснутого, і розпакованого).
- Оновлення: планове завдання раз на 6 годин перевіряє каталоги й оновлює ті, чий інтервал минув; інтервал обирається в кабінеті — 1, 2, 3, 6, 12 або 24 години (за замовчуванням 6). Оскільки завдання запускається раз на 6 годин, інтервали 1–3 години фактично дають оновлення кожні 6 годин. Ручне оновлення — кнопка «Індексувати».
- Без простою: кожне оновлення будує новий версійний індекс і атомарно перемикає на нього псевдонім — пошук працює весь час.
- Зображення: після індексації картинки завантажуються, зменшуються та зберігаються у WebP за адресою
https://aitransform.fun/imgcache/<2 символи>/<sha1>.webp; саме вона повертається вimage_link. Доки конвертація триває, віддається оригінальна адреса з фіду.
Контент-фід: статті, сторінки, акції, FAQ
Другий, необов'язковий фід — сторінки сайту, які варто показувати поруч із товарами: блог, умови доставки, акції, відповіді на питання. Підключається в кабінеті: «Каталог» → картка «Контент» → URL і формат → «Зберегти» → «Індексувати контент». На картці видно кількість документів, час останнього оновлення й останню помилку; далі фід перечитується за тим самим інтервалом, що й товарний. Порожній URL + «Зберегти» відключає фід. Результат — блок content у відповіді /v1/search і «Статті та сторінки» у віджеті.
| Формат | Що читається |
|---|---|
rss | <item>: title, link або guid, description або content:encoded, category → тип, enclosure / media:content / media:thumbnail → зображення, pubDate або dc:date. |
atom | <entry>: title, link rel="alternate", content або summary, category term|label → тип, link rel="enclosure" → зображення, updated або published. Формат визначається за кореневим тегом, тож Atom, збережений як rss, теж індексується. |
csv (UTF-8, колонки в будь-якому порядку) | назва title|назва|название|name; адреса url|link|посилання|ссылка; текст text|body|текст|опис|описание|description|content; тип type|тип|category|категорія|категория; зображення image|picture|зображення|картинка; дата updated|date|дата|pubdate. |
json | Масив об'єктів із тими самими ключами, що й у CSV, або об'єкт з масивом у items, entries, content чи data. |
- Документ:
title(до 300 символів),url(ідентифікатор; лишеhttp(s); при повторі адреси залишається останній),text(до 4000 символів, HTML-теги прибираються),type,image,updated. Пункт без назви або з адресою неhttp(s)пропускається. - Тип зводиться до
article/page/promo/faq/other: саме значення або підказки в назві категорії («статті», «blog», «новини» →article; «сторінка» →page; «акці», «знижк», «sale» →promo; «faq», «питання» →faq); порожня категорія —article. - Ліміти: файл до 20 МБ (стиснутий і розпакований), до 20 000 адрес; ті самі перевірки URL, що й для товарного фіду (лише публічні адреси).
- Індекс: окремий, з псевдонімом
<індекс>_content, з тими самими аналізаторами й синонімами, що й товарний; оновлюється без простою, а якщо новий фід «схуд» понад 10 % — залишається попередній. Синоніми з кабінету застосовуються і до контенту.
Приватне API
https://aitransform.fun/cabinet/ працює через маршрути /cab/* із сесійною кукою. Це внутрішній інтерфейс сторінки кабінету, а не публічний контракт: він може змінюватися без попередження і не призначений для інтеграцій. Публічними є лише /v1/search, /v1/suggest, /v1/event і /v1/health.Зміни
Усі зміни в межах v1 додаткові: старі поля відповіді та параметри запиту зберігаються.
Перемикачі віджета в кабінеті («Налаштування»): підказки «Можливо, ви шукаєте» і панель фільтрів. Відповіді /v1/search і /v1/suggest отримали поле widget: {suggest, filters}; атрибути лоадера data-suggest / data-filters перевизначають кабінет для сайту.
Віджет: панель фільтрів (сортування, ціна з діапазонами, бренди, атрибути фіду; на телефоні — за кнопкою «Фільтри»), підказки «Можливо, ви шукаєте» під час введення, бейджі «Акція / Новинка / Хіт / Рекомендовано», блок «Статті та сторінки», хелпер window.AITRANSFORM.track() і подія aitransform:purchase. Запит віджета до /v1/search тепер містить facets: true та обрані filters / sort; aitransform:click для статті має content: true.
Розуміння запиту: склейка і розбиття слів (match_mode: "compound"), розкладка по окремих словах, українська морфологія в кожному слові, виправлення за контекстом. /v1/search: filters.params і facets.params (атрибути фіду), products[].badges, блок content; товари без ціни чи опису ранжуються нижче в категорії. /v1/event: тип purchase, поля mpns, order_id, value; покупка важить 5 у популярності. Кабінет: контент-фід (RSS / Atom / CSV / JSON), пропозиції синонімів із журналу запитів. Фіди: поле description, ліміт 512 МБ.
Правила мерчандайзингу в /v1/search: нові поля redirect, rules_applied, products[].pinned; порядок категорій за правилами top/bottom/hide. Список дозволених доменів і мова каталогу з кабінету; термін зберігання логу по клієнту. Віджет: редирект за Enter або після 1,5 с, серверний порядок категорій.
API v2 запиту: filters (ціна, бренди, наявність), sort, page, per_category, facets; у відповіді — lang, page, facets, title_display, oldprice, label. Синоніми по клієнту, популярність товарів за подіями (перерахунок — окреме планове завдання, поки не ввімкнене).
Бекенд пошуку, етап 1: додаткові поля відповіді match_mode, took_ms, price_filter, query_used, suggestion, cached; ціна з тексту запиту; кеш 180 с; аналітика запитів і /v1/event; /v1/suggest; оновлення фідів без простою.
Віджет: події aitransform:search / aitransform:click і dataLayer, атрибути data-* лоадера, історія запитів, ізоляція CSS.