API PromoPilot
Єдиний REST API екосистеми PromoPilot: запускайте посилальне просування, відстежуйте позиції, проводьте SEO-аудити та сканування безпеки з вашого коду — CRM, SaaS, панелей агентства.
- Базова адреса:
https://promopilot.link/api/v1
- Формат — JSON поверх HTTPS; всі відповіді містять поле ok.
- Проєкти, створені через API, показуються в кабінеті окремим розділом «Проєкти API» і позначаються бейджем — вони не змішуються з особистими.
- Машиночитувана специфікація: openapi.json
curl https://promopilot.link/api/v1/ping
# {"ok":true,"pong":true,"time":"2026-07-20T12:00:00Z"}
Авторизація та ключі
Ключі API видаються в клієнтському кабінеті: Кабінет → API для розробників. При створенні ключа ви обираєте, до яких сервісів він має доступ (скоупи): promotion, rank, audit, shield. Ключ показується повністю один раз — ми зберігаємо тільки його SHA-256 відбиток.
Authorization: Bearer ppk_ваш_ключ
# или
X-Api-Key: ppk_ваш_ключ
Безпека: зберігайте ключ у змінних середовища або секретному сховищі, не публікуйте в клієнтському коді та репозиторіях. Скомпрометований ключ негайно відкликайте в кабінеті — відкликання діє миттєво. Для різних інтеграцій використовуйте різні ключі з мінімально необхідними скоупами.
Білінг та баланс
- Усі платні операції списуються з особистого балансу акаунта — того ж, що й у кабінеті. Окремого «API-балансу» немає.
- Просування: ціна за тарифом / гнучким каскадом / глобальною ціною, мінус ваша персональна знижка; підписники Pro автоматично отримують додаткову знижку 10%.
- Перед запуском можна дізнатися точну ціну: POST /promotion/quote (без списання).
- Rank Tracker — списання за фактом перевірок; SEO Audit full — передоплата за оцінкою розміру; Shield deep/comprehensive — фіксована ціна сканування.
- Якщо коштів недостатньо — HTTP 402 insufficient_funds з полями required, balance, shortfall та посиланням на поповнення topup_url. Поповнення балансу виконується в кабінеті.
Формат помилок
{
"ok": false,
"error": {
"code": "insufficient_funds",
"message": "Недостатньо коштів на балансі. Поповніть баланс у кабінеті та повторіть запит.",
"details": {"required": 44.1, "balance": 10.0, "shortfall": 34.1, "topup_url": "…"}
}
}
| Код | HTTP | Опис |
unauthorized | 401 | Ключ не передано, не знайдено або відкликано |
forbidden_scope | 403 | У ключа немає доступу до цього сервісу — випустіть ключ з потрібним скоупом |
pro_required | 403 | Функція доступна на тарифі Pro (наприклад, white-label брендинг) |
forbidden | 403 | Дія заборонена (наприклад, видалення особистого проєкту через API) |
not_found | 404 | Об'єкт не знайдено або належить іншому акаунту |
insufficient_funds | 402 | Недостатньо коштів: в details прийдуть required, balance, shortfall та topup_url |
validation_error | 422 | Некоректні параметри; поле вказано в details.field |
domain_mismatch | 422 | Посилання веде не на домен проєкту |
already_running | 409 | Процес вже виконується (наприклад, аудит проєкту) |
needs_verify | 409 | Shield: домен не підтверджено |
need_estimate | 409 | Audit: спочатку express-аудит для оцінки розміру |
rate_limited | 429 | Перевищено ліміт запитів; повторіть через Retry-After секунд |
report_archived | 410 | Звіт заархівовано за терміном зберігання; Pro отримує доступ автоматично |
level1_disabled | 503 | Просування тимчасово вимкнено платформою |
api_disabled | 503 | Публічний API тимчасово вимкнено |
server_error | 500 | Внутрішня помилка — повторіть пізніше |
Ліміти
- 120 запитів на хвилину на ключ (базовий ліміт).
- Запуск просування — до 20 на хвилину; створення проектів — до 30 на годину; запуск аудитів/сканів — до 10 на хвилину.
- При перевищенні — HTTP 429 з заголовком Retry-After. Рекомендована частота опитування статусів — раз у 30–60 секунд.
Локалізація
Людочитні повідомлення (message) повертаються мовою з параметра ?lang= (ru, en, uk, pl) або заголовка Accept-Language; за замовчуванням — мова вашого акаунта. Машинні коди помилок і статуси не перекладаються — прив'язуйте логіку до них.
Довідник ендпоінтів
Обліковий запис
GET
/me
Профіль: баланс, валюта, персональна знижка, Pro-статус, скоупи ключа.
Приклад відповіді
{"ok":true,"user":{"id":2,"balance":124.50,"currency":"USD","promotion_discount_percent":10,"is_pro":true,...},"key":{"label":"CRM","scopes":["promotion"]}}
GET
/balance
Поточний баланс та посилання на поповнення.
Приклад відповіді
{"ok":true,"balance":124.50,"currency":"USD","topup_url":"…/client/balance.php"}
GET
/key
Інформація про пред'явлений ключ: скоупи, статистика використання.
Приклад відповіді
{"ok":true,"key":{"label":"CRM","scopes":["promotion","rank"],"requests_total":1520,...}}
Проекти та посилання скоуп: promotion
GET
/projects
Список проектів. За замовчуванням — тільки створені через API; ?all=1 — разом з особистими.
| Параметр | Де | Опис |
all |
query |
«1» — повернути і особисті, і API-проекти |
Приклад відповіді
{"ok":true,"projects":[{"id":311,"name":"Client A","created_via":"api","links_count":3,"active_runs":1,...}]}
POST
/projects
Створити проект. Він отримає мітку created_via=api і з'явиться в кабінеті в розділі «Проекти API». Можна одразу передати посилання — їх домен має співпадати з доменом проекту.
| Параметр | Де | Опис |
name * |
body |
Назва проєкту |
url * |
body |
Адреса сайту (головна сторінка) |
language |
body |
Мова контенту (ru/en/uk/pl/…), за замовчуванням ru |
region |
body |
Регіон аудиторії |
topic |
body |
Тематика |
wishes |
body |
Бажання до текстів |
links |
body |
Масив об'єктів {url, anchor, language, wish} |
Приклад відповіді
{"ok":true,"project":{"id":312,"created_via":"api",...},"links":{"added":2,"domain_errors":0}}
GET
/projects/{id}
Проект з посиланнями та останніми запусками.
Приклад відповіді
{"ok":true,"project":{"id":312,"links":[...],"runs":[...]}}
PATCH
/projects/{id}
Оновити name / description / language / wishes / region / topic.
Приклад відповіді
{"ok":true,"project":{...}}
DELETE
/projects/{id}
Видалити проект. Дозволено тільки для проектів, створених через API.
Приклад відповіді
{"ok":true,"deleted":true}
POST
/projects/{id}/links
Додати просуваюче посилання. Домен посилання має співпадати з доменом проекту (або бути його піддоменом) — інакше помилка domain_mismatch.
| Параметр | Де | Опис |
url * |
body |
Повний URL сторінки |
anchor |
body |
Анкор (пусто — підберемо автоматично) |
language |
body |
Мова сторінки |
wish |
body |
Бажання до текстів для цього посилання |
Приклад відповіді
{"ok":true,"link":{"id":915,"url":"https://site.com/page",...},"links_count":4}
DELETE
/projects/{id}/links/{linkId}
Видалити посилання з проекту.
Приклад відповіді
{"ok":true,"deleted":true}
GET
/promotion/tariffs
Активні тарифи (для tariff_id), юніт-ціни гнучкого каскаду, глобальна ціна та ваша персональна знижка.
Приклад відповіді
{"ok":true,"tariffs":[{"id":3,"slug":"pro","name":"Pro","price_per_link":49.0,"cascade":{...}}],"your_discount_percent":10}
POST
/promotion/quote
Попередній розрахунок вартості запуску без списання — ті ж параметри, що й у запуску.
| Параметр | Де | Опис |
tariff_id |
body |
ID тарифу |
level1_count…crowd_per_article |
body |
Параметри гнучкого каскаду |
Приклад відповіді
{"ok":true,"pricing_mode":"tariff","base_price":49.0,"discount_percent":10,"total":44.1,"balance":124.5,"sufficient_funds":true}
POST
/promotion/runs
Запустити просування посилання. Списує вартість з особистого балансу (знижки та Pro-бонуси застосовуються автоматично). Повторний запуск того ж посилання при активному каскаді поверне already_active.
| Параметр | Де | Опис |
project_id * |
body |
ID проєкту |
url * |
body |
Просувається посилання (або передайте link_id) |
link_id |
body |
ID посилання проєкту — альтернатива url |
tariff_id |
body |
Запуск за тарифом |
level1_count |
body |
Гнучкий каскад: публікацій L1 |
level2_per_level1 |
body |
L2 на кожну L1 |
level3_per_level2 |
body |
L3 на кожну L2 |
crowd_per_article |
body |
Крауд-посилань на статтю |
schedule_start_at |
body |
Відкладений старт (ISO-дата час) |
Приклад відповіді
{"ok":true,"run_id":501,"status":"queued","charged":44.1,"discount_percent":10,"balance_after":80.4,"currency":"USD"}
GET
/promotion/runs
Список запусків з пагінацією. Фільтри: project_id, status (active | queued | running | completed | failed | …), initiated_via (api | web).
| Параметр | Де | Опис |
page, per_page |
query |
Пагінація (до 100 на сторінку) |
Приклад відповіді
{"ok":true,"runs":[{"id":501,"status":"level1_active","progress":{"done":3,"total":12,"pct":25},...}],"pagination":{...}}
GET
/promotion/runs/{id}
Статус запуску: стадія, прогрес, розбивка по рівнях (total / success / verified).
Приклад відповіді
{"ok":true,"run":{"id":501,"status":"level2_active","levels":[{"level":1,"total":5,"verified":5},...]}}
GET
/promotion/runs/{id}/nodes
Усі розміщення каскаду: мережа, URL публікації, статус верифікації. ?verified=1 — тільки підтверджені посилання.
| Параметр | Де | Опис |
verified |
query |
«1» — тільки перевірені розміщення |
Приклад відповіді
{"ok":true,"run_id":501,"nodes":[{"level":1,"network":"telegraph","url":"https://telegra.ph/…","verified":true,...}]}
Звіти та white-label (Pro) скоуп: promotion
GET
/promotion/runs/{id}/report
Фінальний звіт (тільки перевірені розміщення, як в кабінеті). ?format=pdf поверне посилання на PDF. На тарифі Pro звіт брендується вашим white-label (див. /branding); поля можна переопределити параметрами brand_*.
| Параметр | Де | Опис |
format |
query |
json (за замовчуванням) або pdf |
brand_company |
query |
Pro: назва компанії в шапці |
brand_logo_url |
query |
Pro: URL логотипу |
brand_accent_color |
query |
Pro: HEX-колір акценту |
brand_footer_text |
query |
Pro: текст футера |
Приклад відповіді
{"ok":true,"run":{...},"summary":{"verified":12,...},"report":{"level1":[...],"level2":[...],"crowd":[...]},"whitelabel":{...}} — или {"ok":true,"pdf":{"url":"…/uploads/reports/api-cascade-501-….pdf"},"branded":true}
GET
/branding
Поточний white-label бренд та статус Pro.
Приклад відповіді
{"ok":true,"is_pro":true,"branding_active":true,"branding":{"company_name":"Acme","logo_url":"…","accent_color":"#6366f1",...}}
PUT
/branding
Зберегти white-label бренд (тільки Pro). Той же бренд діє в кабінеті (Налаштування → White-label).
| Параметр | Де | Опис |
company_name * |
body |
Назва компанії (обов'язково) |
logo_url, website, email, phone, address, footer_text, accent_color |
body |
Інші поля бренду |
Приклад відповіді
{"ok":true,"branding":{...}}
Rank Tracker скоуп: rank
GET
/rank/projects
Проекти моніторингу позицій.
Приклад відповіді
{"ok":true,"projects":[{"id":7,"domain":"site.com","region":"UA","keywords_count":50,"top10":12,...}]}
POST
/rank/projects
Створити проект: домен, регіон (UA/US/PL/…), пристрій (desktop/mobile).
| Параметр | Де | Опис |
domain * |
body |
Домен або URL сайту |
name |
body |
Назва (за замовчуванням — домен) |
region |
body |
Регіон пошуку, за замовчуванням UA |
device |
body |
десктоп | мобільний |
Приклад відповіді
{"ok":true,"project":{"id":8,...}}
GET
/rank/projects/{id}
Проект + всі ключові слова з останніми позиціями.
Приклад відповіді
{"ok":true,"project":{...},"keywords":[{"id":91,"keyword":"купить окна","last_position":4,"last_checked_at":"…"}]}
POST
/rank/projects/{id}/keywords
Додати ключові слова: рядок з переносами або масив.
| Параметр | Де | Опис |
keywords * |
body |
Рядок «kw1\nkw2» або масив рядків |
tag |
body |
Мітка групи |
target_url |
body |
Цільова сторінка |
Приклад відповіді
{"ok":true,"added":25,"skipped":0}
POST
/rank/projects/{id}/check
Поставити перевірку позицій в чергу (всі слова або keyword_ids[]). Списання — за фактом перевірок за внутрішніми тарифами сервісу. Результати забирайте з GET /rank/projects/{id}.
| Параметр | Де | Опис |
keyword_ids |
body |
Масив ID слів (за замовчуванням — всі) |
Приклад відповіді
{"ok":true,"queued":50,"note":"…"}
SEO Audit скоуп: audit
GET
/audit/projects
Проекти аудиту.
Приклад відповіді
{"ok":true,"projects":[{"id":4,"domain":"site.com","start_url":"https://site.com/",...}]}
POST
/audit/projects
Створити проект аудиту.
| Параметр | Де | Опис |
url * |
body |
Адреса сайту |
name |
body |
Назва |
include_subdomains |
body |
true — сканувати піддомени |
Приклад відповіді
{"ok":true,"project":{"id":5,...}}
GET
/audit/projects/{id}/estimate
Оцінка розміру сайту та ціна повного аудиту (потрібен хоча б один експрес-аудит для оцінки).
Приклад відповіді
{"ok":true,"has_estimate":true,"estimated_pages":430,"full_audit_price":5.0,"currency":"USD"}
POST
/audit/projects/{id}/crawls
Запустити аудит. express — безкоштовно (дневний ліміт), full — повний, передоплата з балансу за оцінкою розміру.
| Параметр | Де | Опис |
mode |
body |
express (за замовчуванням) | full |
Приклад відповіді
{"ok":true,"crawl_id":88,"mode":"full","max_pages":500,"charged":5.0}
GET
/audit/crawls/{id}
Статус і підсумки: Health Score, помилки/попередження, зведення проблем за категоріями (коли завершено).
Приклад відповіді
{"ok":true,"crawl":{"id":88,"status":"done","health_score":79,"pages_crawled":430,"issues":{...}}}
GET
/audit/projects/{id}/crawls
Історія аудитів проекту.
Приклад відповіді
{"ok":true,"crawls":[...]}
Security Shield скоуп: shield
GET
/shield/projects
Сайти під захистом.
Приклад відповіді
{"ok":true,"projects":[{"id":3,"domain":"site.com","verified":true,...}]}
POST
/shield/projects
Створити/знайти проект за URL. У відповіді — verify_token для підтвердження домену.
| Параметр | Де | Опис |
url * |
body |
Адреса сайту |
name |
body |
Назва |
Приклад відповіді
{"ok":true,"project":{"id":3,"verified":false,"verify_token":"pp-verify-…"},"verification_hint":"…"}
POST
/shield/projects/{id}/verify
Перевірити підтвердження домену (DNS TXT-запис, файл .well-known або meta-тег з verify_token). Потрібно для платних сканувань.
Приклад відповіді
{"ok":true,"verified":true,"method":"dns"}
POST
/shield/projects/{id}/scans
Запустити скан: free (базовий, денний ліміт), deep або comprehensive — списуються з балансу.
| Параметр | Де | Опис |
mode |
body |
безкоштовний | глибокий | всебічний |
lang |
body |
Мова ІІ-звіту (ru/uk/en/pl) |
Приклад відповіді
{"ok":true,"scan_id":41,"mode":"deep","charged":19.0}
GET
/shield/scans/{id}
Статус, оцінка (score/grade), світлофор-вердикт, зведення знахідок за критичністю та публічне посилання на звіт.
Приклад відповіді
{"ok":true,"scan":{"id":41,"status":"done","score":86,"grade":"B","verdict":{...},"findings_summary":{"critical":0,"high":1,...},"share_report_url":"…"}}
Приклади коду
cURL — створити проект і запустити просування
curl -X POST https://promopilot.link/api/v1/projects \
-H "Authorization: Bearer ppk_ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{
"name": "Client Site",
"url": "https://client-site.com",
"language": "ru",
"links": [{"url": "https://client-site.com/services", "anchor": "услуги компании"}]
}'
curl -X POST https://promopilot.link/api/v1/promotion/runs \
-H "Authorization: Bearer ppk_ВАШ_КЛЮЧ" \
-H "Content-Type: application/json" \
-d '{"project_id": 312, "url": "https://client-site.com/services"}'
PHP
<?php
$key = getenv('PROMOPILOT_API_KEY'); // никогда не храните ключ в коде
function pp_api(string $method, string $path, array $data = null) {
global $key;
$ch = curl_init('https://promopilot.link/api/v1' . $path);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $key,
'Content-Type: application/json',
'Accept-Language: ru',
],
CURLOPT_POSTFIELDS => $data !== null ? json_encode($data) : null,
]);
$res = json_decode(curl_exec($ch), true);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if (($res['ok'] ?? false) !== true) {
throw new RuntimeException($res['error']['code'] ?? ('http_' . $code));
}
return $res;
}
$project = pp_api('POST', '/projects', ['name' => 'Client', 'url' => 'https://client-site.com']);
try {
$run = pp_api('POST', '/promotion/runs', [
'project_id' => $project['project']['id'],
'url' => 'https://client-site.com/services',
]);
echo "Запущено, run_id={$run['run_id']}, списано {$run['charged']}";
} catch (RuntimeException $e) {
if ($e->getMessage() === 'insufficient_funds') {
// пополните баланс и повторите
}
}
Python
import os, requests
API = "https://promopilot.link/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['PROMOPILOT_API_KEY']}"}
def api(method, path, **json_body):
r = requests.request(method, API + path, headers=HEADERS, json=json_body or None, timeout=30)
data = r.json()
if not data.get("ok"):
code = data.get("error", {}).get("code", f"http_{r.status_code}")
if code == "insufficient_funds":
details = data["error"]["details"]
raise RuntimeError(f"Пополните баланс: не хватает {details['shortfall']}")
raise RuntimeError(code)
return data
project = api("POST", "/projects", name="Client", url="https://client-site.com")
run = api("POST", "/promotion/runs", project_id=project["project"]["id"],
url="https://client-site.com/services")
print("run_id:", run["run_id"], "charged:", run["charged"])
# Поллинг статуса
import time
while True:
st = api("GET", f"/promotion/runs/{run['run_id']}")["run"]
print(st["status"], st["progress"]["pct"], "%")
if st["status"] in ("completed", "failed", "cancelled"):
break
time.sleep(60)
report = api("GET", f"/promotion/runs/{run['run_id']}/report")
Node.js
const API = "https://promopilot.link/api/v1";
const KEY = process.env.PROMOPILOT_API_KEY;
async function api(method, path, body) {
const res = await fetch(API + path, {
method,
headers: {
"Authorization": `Bearer ${KEY}`,
"Content-Type": "application/json",
"Accept-Language": "ru",
},
body: body ? JSON.stringify(body) : undefined,
});
const data = await res.json();
if (!data.ok) {
const code = data.error?.code || `http_${res.status}`;
if (code === "insufficient_funds") {
console.error("Пополните баланс:", data.error.details.topup_url);
}
throw new Error(code);
}
return data;
}
const { project } = await api("POST", "/projects", { name: "Client", url: "https://client-site.com" });
const run = await api("POST", "/promotion/runs", { project_id: project.id, url: "https://client-site.com/services" });
console.log("run:", run.run_id, "charged:", run.charged);
OpenAPI
Повна машиночитувана специфікація для генерації клієнтів та імпорту в Postman/Insomnia: https://promopilot.link/api/v1/openapi.json
Запитання та пропозиції щодо API — через підтримку в кабінеті або Telegram. Зворотна сумісність: зміни, що ламають контракт, виходять тільки новою версією (/api/v2), поточні поля не видаляються без анонсу в changelog.