AliBord API v1
Публічна REST API для інтеграції вашого бізнесу з AliBord. Створюйте оголошення, отримуйте статистику, керуйте каталогом.
Вступ
AliBord API — це REST-інтерфейс. Базовий URL: https://www.alibord.com
Усі запити та відповіді у форматі JSON у кодуванні UTF-8.
Усі дати — в часовому поясі Europe/Kyiv у форматі ISO 8601.
Як отримати API-ключ
- Зайдіть у свій особистий кабінет AliBord.
- Відкрийте розділ «Налаштування» → «API та інтеграції».
- Натисніть «Створити ключ», виберіть потрібні scopes та додайте whitelist.
- Скопіюйте ключ одразу — після закриття вікна він більше не показується.
Автентифікація
Кожен запит має містити заголовок Authorization з Bearer-токеном:
Authorization: Bearer sk_live_abcdef1234567890abcdef1234567890abcdef1234567890
Whitelist (IP / домени)
Для безпеки кожен API-ключ повинен мати whitelist. Доступ до API можливий лише з вказаних IP-адрес і доменів.
| Поле | Роль | Приклад |
|---|---|---|
allowed_ips | Білий список IP — обов'язковий, перевіряється сервером | 192.0.2.10, 203.0.113.5 |
allowed_domains | Дозволені домени — використовуються для CORS (браузерні запити) | my-shop.com, api.my-shop.com |
Origin / Referer легко підробити з curl/сервера, тому домен не є замком — лише CORS-налаштуванням для браузерних запитів.Сервер-сайд: якщо IP клієнта не у whitelist — одразу 403 ip_not_whitelisted. Якщо whitelist порожній — 403 whitelist_incomplete.
Для браузера: додайте ваш домен (наприклад, my-shop.com) у whitelist — інакше браузер заблокує запит через CORS.
Scopes (права доступу)
Scopes — це «дозволи», які обмежують, що саме може ключ. Призначайте лише потрібні — за принципом мінімальних прав.
| Scope | Дозволяє |
|---|---|
listings:read | Читати свої оголошення, категорії, атрибути, міста |
listings:write | Створювати і редагувати оголошення, завантажувати медіа |
Якщо ключу бракує scope — API повертає 403 scope_required з назвою необхідного scope у полі hint.
Ліміти запитів
Залежно від типу акаунта ключ має різні ліміти на хвилину і на добу.
| Ліміт | Особистий акаунт | Бізнес-акаунт |
|---|---|---|
| Запитів на хвилину | 60 | 600 |
| Запитів на добу | 5 000 | 100 000 |
При перевищенні — 429 rate_limit_exceeded або 429 daily_limit_exceeded. Поточний стан можна дивитися у /api/v1/ping.
HTTP-методи
API використовує стандартні HTTP-методи.
| Метод | Призначення | Тіло | Ідемпотентний |
|---|---|---|---|
GET | Отримання даних (read-only) | — | так |
POST | Створення сутності / виконання дії | JSON | ні |
PUT / PATCH | Повне / часткове оновлення | JSON | так |
DELETE | Видалення | — | так |
Заголовки запиту
| Заголовок | Обов'язковий | Приклад |
|---|---|---|
Authorization | так | Bearer sk_live_... |
Accept | рекомендовано | application/json |
Content-Type | для POST/PUT/PATCH | application/json; charset=utf-8 |
Origin / Referer | автоматично (браузер) | використовується для перевірки whitelist |
User-Agent | опціонально | для діагностики в логах |
Тіло запиту
Для POST/PUT/PATCH тіло передається у форматі JSON із заголовком Content-Type: application/json.
Для GET параметри передаються у query-рядку.
Приклад:
POST /api/v1/listings
Authorization: Bearer sk_live_...
Content-Type: application/json
{
"title": "iPhone 17 Pro Max 256Gb",
"price": 55000,
"currency": "UAH",
"condition": "new",
"city_ref": "db5c88d0-391c-11dd-90d9-001a92567626",
"photos": ["https://cdn.example.com/1.jpg", "https://cdn.example.com/2.jpg"]
}
Формат відповіді
Усі відповіді — JSON з полем ok. true — успіх, false — помилка.
Успіх
{
"ok": true,
...
}
Помилка
{
"ok": false,
"error": "error_code",
"hint": "...",
...
}
Для колекцій використовується поле count і масив з назвою сутності:
{
"ok": true,
"count": 2,
"listings": [ { ... }, { ... } ]
}
HTTP-коди відповіді
| Код | Значення |
|---|---|
200 OK | Успішний запит |
201 Created | Сутність створено |
204 No Content | Успіх без тіла відповіді |
400 Bad Request | Помилка у форматі запиту (невалідний JSON, відсутні поля) |
401 Unauthorized | Невалідний / відсутній токен |
402 Payment Required | Недостатньо коштів на балансі (для платних дій) |
403 Forbidden | Доступ заборонено (whitelist / scope / статус ключа) |
404 Not Found | Ресурс не знайдено |
405 Method Not Allowed | Метод не дозволений для цього endpoint |
413 Payload Too Large | Розмір тіла перевищує ліміт |
415 Unsupported Media Type | Невалідний Content-Type |
422 Unprocessable Entity | Дані пройшли валідацію формату, але не пройшли бізнес-правила |
429 Too Many Requests | Перевищено ліміт запитів |
500 Internal Server Error | Внутрішня помилка сервера |
Коди помилок
При помилці тіло містить поле error з машинним кодом і hint з людським поясненням.
{ "ok": false, "error": "error_code", ... }
| HTTP | Code | Опис |
|---|---|---|
| 401 | missing_bearer_token | Заголовок Authorization відсутній або не містить Bearer-токен |
| 401 | invalid_key | Ключ не знайдено, деактивовано або має невірний формат |
| 403 | key_suspended / key_revoked | Ключ тимчасово призупинено або видалено власником |
| 403 | whitelist_incomplete | У ключа не заданий whitelist — доступ заборонено |
| 403 | ip_not_whitelisted | IP запиту не в whitelist ключа |
| 403 | scope_required | У ключа немає необхідного scope — див. hint |
| 429 | rate_limit_exceeded | Перевищено ліміт запитів за хвилину |
| 429 | daily_limit_exceeded | Перевищено добовий ліміт запитів |
GET /api/v1/ping
Перевірка ключа і швидкий погляд на ліміти / scopes / IP клієнта.
Відповідь
{
"ok": true,
"message": "pong",
"key_id": 1,
"key_prefix": "sk_live_abcdef12",
"user_id": 36,
"account_type": "personal",
"scopes": ["listings:read", "listings:write"],
"scopes_granted": ["listings:read", "listings:write"],
"rate_limit_per_minute": 60,
"daily_limit": 5000,
"total_requests": 42,
"server_time": "2026-04-20T12:34:56+03:00",
"your_ip": "203.0.113.5"
}
GET /api/v1/my/listings
Список власних оголошень з пагінацією по курсору. Scope: listings:read.
Query-параметри
| Параметр | Default | Опис |
|---|---|---|
lang | uk | Мова відповіді: uk, ru, en |
status | active | active, sold, archived, inactive, all |
limit | 20 | Розмір сторінки, 1–100 |
cursor | — | ID останнього оголошення попередньої сторінки |
fields | усі поля | Список полів через кому — щоб отримати тільки потрібні (економія трафіку) |
Відповідь
{
"ok": true,
"lang": "uk",
"count": 2,
"next_cursor": 118,
"listings": [
{
"id": 123,
"slug": "iphone-17-pro-max-256gb",
"title": "iPhone 17 Pro Max 256Gb",
"price": 55000,
"currency": "UAH",
"condition": "new",
"city": "Київ",
"status": "active",
"views": 248,
"vip": true,
"top": false,
"vip_until": "2026-05-20 00:00:00",
"top_until": null,
"photo": "https://cdn.alibord.com/listings/3/abc.webp",
"photos": [
"https://cdn.alibord.com/listings/3/abc.webp",
"https://cdn.alibord.com/listings/3/def.webp"
],
"url": "https://alibord.com/uk/board/iphone-17-pro-max-256gb",
"created_at": "2026-04-15 10:22:01"
}
]
}
Значення next_cursor передавайте у параметр cursor наступного запиту. Якщо сторінка не повна — next_cursor відсутній.
Поля
| Поле | Тип | Опис |
|---|---|---|
id | int | ID оголошення |
slug | string | SEO-адреса |
title | string | Заголовок вибраною мовою |
price | number | Ціна, може бути дробовою |
currency | string | UAH, USD, EUR |
condition | string \| null | new, used |
city | string \| null | Назва міста вибраною мовою |
status | string | active, sold, archived, inactive |
views | int | Кількість переглядів |
vip, top | bool | Чи активне VIP / TOP просування |
vip_until, top_until | datetime \| null | До якої дати діє просування |
photo | string \| null | Головне фото (перше з масиву) |
photos | array | Усі фото оголошення |
url | string | Пряме посилання на сторінку оголошення |
created_at | datetime | Дата створення |
fields і limit — відповідь буде меншою, і ліміти витрачаються економніше.GET /api/v1/my/listings/{id}
Повні дані одного оголошення, включно з описом, атрибутами, контактами та статусом модерації. Scope: listings:read.
Відповідь
{
"ok": true,
"lang": "uk",
"listing": {
"id": 123,
"slug": "iphone-17-pro-max-256gb",
"title": "iPhone 17 Pro Max 256Gb",
"description": "...",
"price": 55000,
"currency": "UAH",
"condition": "new",
"city": "Київ",
"city_ref": "e71f8bb8-4b33-11e4-ab6d-005056801329",
"category_id": 42,
"status": "pending",
...
},
"moderation": {
"decision": "pending",
"reason": null,
"reviewed_at": null
}
}
Блок moderation повертається лише для оголошень у стані pending або blocked. У reason буде причина блокування, якщо модератор її залишив.
POST /api/v1/my/listings
Створення нового оголошення. Scope: listings:write.
pending, публікація — після схвалення модератором.Тіло запиту (JSON)
| Поле | Тип | Обов'язкове | Опис |
|---|---|---|---|
title | string | так | Заголовок, 5–120 символів |
description | string | так | Опис, 20–5000 символів |
price | number | так* | Ціна. Необов'язкове якщо price_type = exchange / free |
price_type | string | ні | sell, exchange, free |
currency | string | ні | UAH, USD, EUR |
category_id | int | так | ID категорії (див. /categories) |
condition | string | ні | new / used |
city_ref | string | так* | UUID міста з /cities/search |
photos | array<string> | ні | URL фото (до 12). Отримати можна через /upload/photo |
photos_wm | array<string> | ні | URL фото з водяним знаком, у тому ж порядку що і photos |
photos_3d | array<string> | ні | URL кадрів 3D-огляду (див. 3D-віджет) |
video_url / video_thumbnail | string | ні | URL відео і його прев'ю (див. /upload/video) |
attributes | object | ні | Об'єкт з атрибутами категорії (див. /categories/{id}/attributes) |
contact_name / contact_phone | string | ні | Ім'я і телефон (якщо не вказати — беруться з профілю) |
hide_phone | bool | ні | Приховати телефон у оголошенні |
Відповідь
HTTP/1.1 201 Created
{
"ok": true,
"listing_id": 2468,
"slug": "iphone-17-pro-max-256gb",
"status": "active",
"url": "https://alibord.com/uk/board/iphone-17-pro-max-256gb"
}
422 duplicate.PUT PATCH /api/v1/my/listings/{id}
Часткове редагування оголошення. Передавайте лише ті поля які треба оновити — решта залишаються як були. Scope: listings:write.
Тіло (JSON, усі поля опційні)
{
"title": "iPhone 17 Pro Max 256Gb",
"description": "...",
"price": 52000,
"currency": "UAH",
"condition": "new",
"category_id": 42,
"city_ref": "e71f8bb8-...",
"contact_name": "...",
"contact_phone": "+380...",
"hide_phone": false,
"delivery": { ... },
"photos": ["https://...", "..."],
"photos_wm": ["https://...", "..."],
"photos_3d": ["https://...", "..."],
"video_url": "https://...",
"video_thumbnail": "https://...",
"attributes": { "brand": "apple", "memory_gb": 256 }
}
photos, photos_3d або video_url — це повна заміна відповідного набору. Щоб залишити попереднє — не передавайте поле взагалі.active, після оновлення воно автоматично переводиться у pending і проходить перевірку заново — як на сайті.{ "ok": true, "listing_id": 2468, "status": "pending" }
POST /api/v1/my/listings/{id}/status
Зміна статусу оголошення — активувати або деактивувати. Scope: listings:write. Дії «продано» і «видалити» через API свідомо недоступні — виконуйте їх з сайту або адмінки.
Тіло (JSON)
{ "status": "inactive" }
Можливі значення
| Значення | Що робить |
|---|---|
active | Повернути в публікацію (дозволено лише з inactive/sold) |
inactive | Деактивувати — оголошення приховується з каталогу |
active дозволений лише зі стану inactive. З pending / blocked / sold активувати через API не можна — це робить модератор/адмін на сайті.GET /api/v1/categories
Дерево категорій для створення оголошення. Назви — вибраною мовою (?lang=uk|ru|en). Scope: listings:read.
{
"ok": true,
"lang": "uk",
"categories": [
{
"id": 1,
"slug": "electronics",
"name": "Електроніка",
"children": [
{ "id": 11, "slug": "phones", "name": "Телефони", "children": [...] }
]
}
]
}
GET /api/v1/categories/{id}/attributes
Атрибути конкретної категорії — потрібні у полі attributes при створенні оголошення. Scope: listings:read.
{
"ok": true,
"category_id": 11,
"attributes": [
{
"code": "brand",
"name": "Виробник",
"type": "select",
"required": true,
"options": [
{ "value": "apple", "label": "Apple" },
{ "value": "samsung", "label": "Samsung" }
]
},
{ "code": "memory_gb", "name": "Пам'ять, ГБ", "type": "number" }
]
}
Типи: text, number, select, boolean, range. Для select значення передається як value з масиву options.
GET /api/v1/cities/search
Пошук міст для поля city_ref. Повертає ref Нової Пошти. Scope: listings:read.
Query-параметри
| Параметр | Default | Опис |
|---|---|---|
q | — | Рядок пошуку (min 2 символи) |
lang | uk | Мова назви: uk, ru, en |
limit | 10 | Максимум результатів (1–50) |
{
"ok": true,
"cities": [
{
"ref": "e71f8bb8-4b33-11e4-ab6d-005056801329",
"name": "Київ",
"type": "м.",
"region": "Київська",
"label": "м. Київ, Київська",
"warehouses": 1823
}
]
}
POST /api/v1/ai/suggest-category
AI-підказка категорії за заголовком і описом. Зручно для автоматичного заповнення форми. Scope: listings:write.
{
"title": "iPhone 17 Pro Max 256Gb",
"description": "...",
"lang": "uk"
}
Успіх
{
"ok": true,
"category_id": 117,
"category_path": "Електроніка › Телефони › Apple",
"category_slug": "apple"
}
POST /api/v1/upload/photo
Завантаження фото на CDN — отримати URL для поля photos при створенні оголошення. Scope: listings:write.
Формат: multipart/form-data, поле file. Ліміт — 12 MB. Підтримуються JPG, PNG, WebP, HEIC. Автоматично конвертується у WebP і зменшується до 1600px.
curl -X POST "https://www.alibord.com/api/v1/upload/photo" \
-H "Authorization: Bearer sk_live_..." \
-F "file=@/path/to/image.jpg"
HTTP/1.1 201 Created
{
"ok": true,
"url": "https://pub-...r2.dev/listings/36/1776203001_123.webp",
"url_wm": "https://pub-...r2.dev/pub/36/abc123...webp",
"width": 1200,
"height": 1600
}
url — оригінал, url_wm — із водяним знаком. При створенні оголошення використовуйте обидва набори відповідно у photos / photos_wm.POST /api/v1/upload/video
Завантаження короткого відео (до 60 сек) для оголошення. Scope: listings:write.
- Формати: MP4, MOV, WebM (H.264 або H.265)
- Макс. розмір: 50 MB, макс. тривалість: 60 сек
- На оголошення — одне відео
HTTP/1.1 201 Created
{
"ok": true,
"url": "https://pub-...r2.dev/listings/36/1776213111_456.mp4",
"thumbnail": "https://pub-...r2.dev/listings/36/1776213111_456_thumb.webp"
}
3D-віджет
Готовий віджет для захоплення 3D-огляду товару через камеру пристрою. Вбудовується одним рядком, завантажує фото через ваш серверний проксі (щоб не розкривати API-ключ).
Вбудовування
<script src="https://www.alibord.com/widget/capture3d.js"></script>
<button onclick="startCapture()">3D</button>
<script>
function startCapture() {
AliBord.capture3d({
uploadEndpoint: '/my-proxy/upload-photo',
lang: 'uk',
onComplete: (urls) => console.log(urls.length),
onCancel: () => {},
onError: (err) => console.error(err),
});
}
</script>
Як працює
- Користувач натискає кнопку «3D»
- Віджет просить доступ до камери і відкриває fullscreen
- Знімається серія кадрів (автоматично або по натисканню)
- Кадри відправляються POST-запитами на ваш
uploadEndpoint - Ваш сервер пересилає їх на
/api/v1/upload/photoз API-ключем - У колбек
onComplete(urls)повертається масив URL — передавайте його у поліphotos_3dпри створенні оголошення
/api/v1/upload/photo напряму з браузера — це розкриває API-ключ. Завжди використовуйте серверний проксі.Приклад серверного проксі (PHP)
<?php
$API_KEY = getenv('ALIBORD_API_KEY');
if (empty($_FILES['file'])) {
http_response_code(400);
echo json_encode(['ok'=>false, 'error'=>'no_file']); exit;
}
$ch = curl_init('https://www.alibord.com/api/v1/upload/photo');
$cfile = new CURLFile($_FILES['file']['tmp_name'], $_FILES['file']['type'], $_FILES['file']['name']);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $API_KEY],
CURLOPT_POSTFIELDS => ['file' => $cfile],
CURLOPT_TIMEOUT => 60,
]);
$resp = curl_exec($ch);
http_response_code((int)curl_getinfo($ch, CURLINFO_HTTP_CODE));
header('Content-Type: application/json; charset=utf-8');
echo $resp;
Модерація
Усі оголошення, створені через API, проходять ту саму модерацію, що й оголошення з сайту.
- Оголошення із безпечних категорій зазвичай публікуються автоматично — відразу
active. - Решта переходять у
pendingі перевіряються модератором (~15 хв у робочий час).
| Status | Що робить |
|---|---|
pending | Чекає перевірки модератором |
active | Опубліковано і видно в каталозі |
blocked | Відхилено модератором — див. moderation.reason |
sold | Позначено проданим |
archived | Перенесено в архів за терміном |
inactive | Деактивовано власником |
Щоб слідкувати за станом, запитуйте GET /my/listings/{id} — у блоці moderation буде рішення і причина (якщо є).
Приклад: cURL
# Список своїх оголошень (англійською)
curl -H "Authorization: Bearer sk_live_..." \
"https://www.alibord.com/api/v1/my/listings?lang=en&limit=20"
# Тільки потрібні поля — економимо трафік
curl -H "Authorization: Bearer sk_live_..." \
"https://www.alibord.com/api/v1/my/listings?fields=id,title,price,currency,photo&limit=50"
# Створення нового оголошення
curl -X POST "https://www.alibord.com/api/v1/my/listings" \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{
"title": "iPhone 17 Pro Max 256Gb",
"description": "...",
"price": 55000,
"currency": "UAH",
"category_id": 42,
"condition": "new",
"city_ref": "e71f8bb8-4b33-11e4-ab6d-005056801329",
"photos": ["https://my-shop.com/images/iphone-1.jpg"],
"contact_phone": "+380501234567"
}'
Приклад: PHP
<?php
$API_BASE = 'https://www.alibord.com';
$API_KEY = 'sk_live_...';
$ch = curl_init($API_BASE . '/api/v1/my/listings?lang=en&limit=10&fields=id,title,price,currency,city,photo');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $API_KEY,
'Accept: application/json',
],
]);
$body = curl_exec($ch);
$data = json_decode($body, true);
curl_close($ch);
if (!empty($data['ok'])) {
foreach ($data['listings'] as $l) {
echo $l['title'] . ' — ' . $l['price'] . ' ' . $l['currency'] . "\n";
}
} else {
echo 'Error: ' . ($data['error'] ?? 'unknown');
}
Приклад: JavaScript (fetch)
// УВАГА: у браузері напряму — тільки через ваш серверний проксі! Ключ не має потрапити у фронтенд.
const params = new URLSearchParams({
lang: 'uk', limit: '20',
fields: 'id,title,price,currency,city,photo,url'
});
const r = await fetch('https://www.alibord.com/api/v1/my/listings?' + params, {
headers: { 'Authorization': 'Bearer sk_live_...' }
});
const data = await r.json();
if (data.ok) {
data.listings.forEach(l => console.log(l.title, l.price, l.currency, l.city));
} else {
console.error(data.error);
}
allowed_domains ключа. Інакше браузер заблокує відповідь.