API PromoPilot
Jedno REST API całego ekosystemu PromoPilot: uruchamiaj promocję linkową, śledź pozycje, przeprowadzaj audyty SEO i skany bezpieczeństwa z własnego kodu — CRM, SaaS, panelu agencji.
- Adres bazowy:
https://promopilot.link/api/v1
- Format to JSON przez HTTPS; każda odpowiedź zawiera pole ok.
- Projekty utworzone przez API pojawiają się w panelu w osobnej sekcji „Projekty API” i mają odpowiednią plakietkę — nie mieszają się z osobistymi.
- Specyfikacja do odczytu maszynowego: openapi.json
curl https://promopilot.link/api/v1/ping
# {"ok":true,"pong":true,"time":"2026-07-20T12:00:00Z"}
Autoryzacja i klucze
Klucze API wydawane są w panelu klienta: Panel → API dla programistów. Tworząc klucz, wybierasz, do których usług ma dostęp (zakresy): promotion, rank, audit, shield. Klucz w całości pokazywany jest tylko raz — przechowujemy wyłącznie jego odcisk SHA-256.
Authorization: Bearer ppk_twoj_klucz# или
X-Api-Key: ppk_twoj_klucz
Bezpieczeństwo: przechowuj klucz w zmiennych środowiskowych lub magazynie sekretów i nie publikuj go w kodzie klienckim ani w repozytoriach. Skompromitowany klucz natychmiast odwołaj w panelu — odwołanie działa od razu. Do różnych integracji używaj osobnych kluczy z minimalnym niezbędnym zakresem.
Rozliczenia i saldo
- Wszystkie płatne operacje obciążają osobiste saldo konta — to samo co w panelu. Nie ma osobnego „salda API”.
- Promocja: cena według taryfy, kaskady elastycznej lub ceny globalnej, minus Twój rabat indywidualny; subskrybenci Pro automatycznie otrzymują dodatkowe 10% zniżki.
- Przed uruchomieniem możesz poznać dokładną cenę: POST /promotion/quote (bez obciążenia).
- Rank Tracker rozlicza się za faktycznie wykonane sprawdzenia; SEO Audit full opłacany jest z góry według szacowanego rozmiaru; Shield deep/comprehensive ma stałą cenę skanu.
- Jeśli brakuje środków, otrzymasz HTTP 402 insufficient_funds z polami required, balance, shortfall oraz linkiem topup_url. Doładowanie salda odbywa się w panelu.
Format błędów
{
"ok": false,
"error": {
"code": "insufficient_funds",
"message": "Za mało środków na saldzie. Doładuj je w panelu i powtórz żądanie.",
"details": {"required": 44.1, "balance": 10.0, "shortfall": 34.1, "topup_url": "…"}
}
}
| Code | HTTP | Description |
unauthorized | 401 | Klucz nie został przekazany, nie znaleziono go lub został odwołany |
forbidden_scope | 403 | Klucz nie ma dostępu do tej usługi — wystaw klucz z odpowiednim zakresem |
pro_required | 403 | Funkcja wymaga planu Pro (na przykład branding white-label) |
forbidden | 403 | Działanie jest zabronione (na przykład usunięcie projektu osobistego przez API) |
not_found | 404 | Nie znaleziono obiektu lub należy on do innego konta |
insufficient_funds | 402 | Za mało środków: w details znajdą się required, balance, shortfall i topup_url |
validation_error | 422 | Nieprawidłowe parametry; pole wskazano w details.field |
domain_mismatch | 422 | Link nie prowadzi do domeny projektu |
already_running | 409 | Proces już trwa (na przykład audyt projektu) |
needs_verify | 409 | Shield: domena nie została zweryfikowana |
need_estimate | 409 | Audit: najpierw audyt express, aby oszacować rozmiar |
rate_limited | 429 | Przekroczono limit żądań; ponów po Retry-After sekundach |
report_archived | 410 | Raport został zarchiwizowany zgodnie z okresem przechowywania; konta Pro otrzymują dostęp automatycznie |
level1_disabled | 503 | Promocja jest tymczasowo wyłączona przez platformę |
api_disabled | 503 | Publiczne API jest tymczasowo wyłączone |
server_error | 500 | Błąd wewnętrzny — spróbuj ponownie później |
Limity
- 120 żądań na minutę na klucz (limit podstawowy).
- Uruchomienia promocji — do 20 na minutę; tworzenie projektów — do 30 na godzinę; uruchamianie audytów i skanów — do 10 na minutę.
- Po przekroczeniu limitu otrzymasz HTTP 429 z nagłówkiem Retry-After. Zalecana częstotliwość odpytywania statusów to raz na 30–60 sekund.
Lokalizacja
Komunikaty czytelne dla ludzi (message) wracają w języku z parametru ?lang= (ru, en, uk, pl) albo nagłówka Accept-Language; domyślnie w języku Twojego konta. Maszynowe kody błędów i statusy nie są tłumaczone — opieraj na nich swoją logikę.
Dokumentacja punktów końcowych
Konto
GET
/me
Profil: saldo, waluta, rabat indywidualny, status Pro, zakresy klucza.
Przykładowa odpowiedź
{"ok":true,"user":{"id":2,"balance":124.50,"currency":"USD","promotion_discount_percent":10,"is_pro":true,...},"key":{"label":"CRM","scopes":["promotion"]}}
GET
/balance
Bieżące saldo i link do doładowania.
Przykładowa odpowiedź
{"ok":true,"balance":124.50,"currency":"USD","topup_url":"…/client/balance.php"}
GET
/key
Informacje o przedstawionym kluczu: zakresy i statystyki użycia.
Przykładowa odpowiedź
{"ok":true,"key":{"label":"CRM","scopes":["promotion","rank"],"requests_total":1520,...}}
Projekty i linki zakres: promotion
GET
/projects
Lista projektów. Domyślnie tylko utworzone przez API; ?all=1 dołącza osobiste.
| Parametr | Gdzie | Description |
all |
query |
„1” — zwróć zarówno projekty osobiste, jak i API |
Przykładowa odpowiedź
{"ok":true,"projects":[{"id":311,"name":"Client A","created_via":"api","links_count":3,"active_runs":1,...}]}
POST
/projects
Utwórz projekt. Otrzyma oznaczenie created_via=api i pojawi się w panelu w sekcji „Projekty API”. Możesz od razu przekazać linki — ich domena musi być zgodna z domeną projektu.
| Parametr | Gdzie | Description |
name * |
body |
Project Name |
url * |
body |
Adres witryny (strona główna) |
language |
body |
Język treści (ru/en/uk/pl/…), domyślnie ru |
region |
body |
Region odbiorców |
topic |
body |
Topic |
wishes |
body |
Uwagi do tekstów |
links |
body |
Tablica obiektów {url, anchor, language, wish} |
Przykładowa odpowiedź
{"ok":true,"project":{"id":312,"created_via":"api",...},"links":{"added":2,"domain_errors":0}}
GET
/projects/{id}
Projekt wraz z linkami i ostatnimi uruchomieniami.
Przykładowa odpowiedź
{"ok":true,"project":{"id":312,"links":[...],"runs":[...]}}
PATCH
/projects/{id}
Zaktualizuj name / description / language / wishes / region / topic.
Przykładowa odpowiedź
{"ok":true,"project":{...}}
DELETE
/projects/{id}
Usuń projekt. Dozwolone tylko dla projektów utworzonych przez API.
Przykładowa odpowiedź
{"ok":true,"deleted":true}
POST
/projects/{id}/links
Dodaj link do promocji. Domena linku musi być zgodna z domeną projektu (lub być jej subdomeną) — w przeciwnym razie wystąpi błąd domain_mismatch.
| Parametr | Gdzie | Description |
url * |
body |
Pełny adres URL strony |
anchor |
body |
Anchor (puste — dobierzemy automatycznie) |
language |
body |
Page language |
wish |
body |
Uwagi do tekstów dla tego linku |
Przykładowa odpowiedź
{"ok":true,"link":{"id":915,"url":"https://site.com/page",...},"links_count":4}
DELETE
/projects/{id}/links/{linkId}
Usuń link z projektu.
Przykładowa odpowiedź
{"ok":true,"deleted":true}
GET
/promotion/tariffs
Aktywne taryfy (dla tariff_id), ceny jednostkowe kaskady elastycznej, cena globalna i Twój rabat indywidualny.
Przykładowa odpowiedź
{"ok":true,"tariffs":[{"id":3,"slug":"pro","name":"Pro","price_per_link":49.0,"cascade":{...}}],"your_discount_percent":10}
POST
/promotion/quote
Wstępna wycena uruchomienia bez obciążenia — te same parametry co przy starcie.
| Parametr | Gdzie | Description |
tariff_id |
body |
ID taryfy |
level1_count…crowd_per_article |
body |
Parametry kaskady elastycznej |
Przykładowa odpowiedź
{"ok":true,"pricing_mode":"tariff","base_price":49.0,"discount_percent":10,"total":44.1,"balance":124.5,"sufficient_funds":true}
POST
/promotion/runs
Uruchom promocję linku. Koszt jest pobierany z osobistego salda (rabaty i bonus Pro naliczają się automatycznie). Ponowne uruchomienie tego samego linku przy aktywnej kaskadzie zwróci already_active.
| Parametr | Gdzie | Description |
project_id * |
body |
ID projektu |
url * |
body |
Link do promocji (albo przekaż link_id) |
link_id |
body |
ID linku projektu — alternatywa dla url |
tariff_id |
body |
Uruchomienie według taryfy |
level1_count |
body |
Kaskada elastyczna: publikacji L1 |
level2_per_level1 |
body |
L2 na każdą L1 |
level3_per_level2 |
body |
L3 na każdą L2 |
crowd_per_article |
body |
Linków crowd na artykuł |
schedule_start_at |
body |
Odroczony start (data-czas ISO) |
Przykładowa odpowiedź
{"ok":true,"run_id":501,"status":"queued","charged":44.1,"discount_percent":10,"balance_after":80.4,"currency":"USD"}
GET
/promotion/runs
Stronicowana lista uruchomień. Filtry: project_id, status (active | queued | running | completed | failed | …), initiated_via (api | web).
| Parametr | Gdzie | Description |
page, per_page |
query |
Stronicowanie (do 100 na stronę) |
Przykładowa odpowiedź
{"ok":true,"runs":[{"id":501,"status":"level1_active","progress":{"done":3,"total":12,"pct":25},...}],"pagination":{...}}
GET
/promotion/runs/{id}
Status uruchomienia: etap, postęp i podział na poziomy (total / success / verified).
Przykładowa odpowiedź
{"ok":true,"run":{"id":501,"status":"level2_active","levels":[{"level":1,"total":5,"verified":5},...]}}
GET
/promotion/runs/{id}/nodes
Wszystkie publikacje kaskady: sieć, URL publikacji, status weryfikacji. ?verified=1 zwraca tylko potwierdzone linki.
| Parametr | Gdzie | Description |
verified |
query |
„1” — tylko zweryfikowane publikacje |
Przykładowa odpowiedź
{"ok":true,"run_id":501,"nodes":[{"level":1,"network":"telegraph","url":"https://telegra.ph/…","verified":true,...}]}
Raporty i white-label (Pro) zakres: promotion
GET
/promotion/runs/{id}/report
Raport końcowy (tylko zweryfikowane publikacje, jak w panelu). ?format=pdf zwróci link do PDF. W planie Pro raport nosi Twoją markę white-label (zob. /branding); pola można nadpisać parametrami brand_*.
| Parametr | Gdzie | Description |
format |
query |
json (domyślnie) lub pdf |
brand_company |
query |
Pro: nazwa firmy w nagłówku |
brand_logo_url |
query |
Pro: adres URL logo |
brand_accent_color |
query |
Pro: kolor akcentu w HEX |
brand_footer_text |
query |
Pro: tekst stopki |
Przykładowa odpowiedź
{"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
Bieżąca marka white-label i status Pro.
Przykładowa odpowiedź
{"ok":true,"is_pro":true,"branding_active":true,"branding":{"company_name":"Acme","logo_url":"…","accent_color":"#6366f1",...}}
PUT
/branding
Zapisz markę white-label (tylko Pro). Ta sama marka obowiązuje w panelu (Ustawienia → White-label).
| Parametr | Gdzie | Description |
company_name * |
body |
Nazwa firmy (wymagane) |
logo_url, website, email, phone, address, footer_text, accent_color |
body |
Pozostałe pola marki |
Przykładowa odpowiedź
{"ok":true,"branding":{...}}
Rank Tracker zakres: rank
GET
/rank/projects
Projekty monitorowania pozycji.
Przykładowa odpowiedź
{"ok":true,"projects":[{"id":7,"domain":"site.com","region":"UA","keywords_count":50,"top10":12,...}]}
POST
/rank/projects
Utwórz projekt: domena, region (UA/US/PL/…), urządzenie (desktop/mobile).
| Parametr | Gdzie | Description |
domain * |
body |
Domena lub adres URL witryny |
name |
body |
Nazwa (domyślnie domena) |
region |
body |
Region wyszukiwania, domyślnie UA |
device |
body |
desktop | mobile |
Przykładowa odpowiedź
{"ok":true,"project":{"id":8,...}}
GET
/rank/projects/{id}
Projekt oraz wszystkie słowa kluczowe z ostatnimi pozycjami.
Przykładowa odpowiedź
{"ok":true,"project":{...},"keywords":[{"id":91,"keyword":"kup okna","last_position":4,"last_checked_at":"…"}]}
POST
/rank/projects/{id}/keywords
Dodaj słowa kluczowe: ciąg z podziałami wierszy albo tablica.
| Parametr | Gdzie | Description |
keywords * |
body |
Ciąg „kw1\nkw2” albo tablica ciągów |
tag |
body |
Etykieta grupy |
target_url |
body |
Target page |
Przykładowa odpowiedź
{"ok":true,"added":25,"skipped":0}
POST
/rank/projects/{id}/check
Zakolejkuj sprawdzenie pozycji (wszystkie słowa albo keyword_ids[]). Obciążenie następuje za faktycznie wykonane sprawdzenia według wewnętrznych stawek serwisu. Wyniki pobierz z GET /rank/projects/{id}.
| Parametr | Gdzie | Description |
keyword_ids |
body |
Tablica ID słów (domyślnie wszystkie) |
Przykładowa odpowiedź
{"ok":true,"queued":50,"note":"…"}
SEO Audit zakres: audit
GET
/audit/projects
Projekty audytu.
Przykładowa odpowiedź
{"ok":true,"projects":[{"id":4,"domain":"site.com","start_url":"https://site.com/",...}]}
POST
/audit/projects
Utwórz projekt audytu.
| Parametr | Gdzie | Description |
url * |
body |
Adres witryny |
name |
body |
Name |
include_subdomains |
body |
true — skanuj subdomeny |
Przykładowa odpowiedź
{"ok":true,"project":{"id":5,...}}
GET
/audit/projects/{id}/estimate
Szacowany rozmiar witryny i cena pełnego audytu (do wyceny potrzebny jest co najmniej jeden audyt express).
Przykładowa odpowiedź
{"ok":true,"has_estimate":true,"estimated_pages":430,"full_audit_price":5.0,"currency":"USD"}
POST
/audit/projects/{id}/crawls
Uruchom audyt. express jest bezpłatny (limit dzienny), full to audyt pełny, opłacany z góry z salda według szacowanego rozmiaru.
| Parametr | Gdzie | Description |
mode |
body |
express (domyślnie) | full |
Przykładowa odpowiedź
{"ok":true,"crawl_id":88,"mode":"full","max_pages":500,"charged":5.0}
GET
/audit/crawls/{id}
Status i wyniki: Health Score, błędy/ostrzeżenia oraz zestawienie problemów według kategorii (po zakończeniu).
Przykładowa odpowiedź
{"ok":true,"crawl":{"id":88,"status":"done","health_score":79,"pages_crawled":430,"issues":{...}}}
GET
/audit/projects/{id}/crawls
Historia audytów projektu.
Przykładowa odpowiedź
{"ok":true,"crawls":[...]}
Security Shield zakres: shield
GET
/shield/projects
Chronione witryny.
Przykładowa odpowiedź
{"ok":true,"projects":[{"id":3,"domain":"site.com","verified":true,...}]}
POST
/shield/projects
Utwórz lub znajdź projekt po adresie URL. W odpowiedzi znajdziesz verify_token do potwierdzenia domeny.
| Parametr | Gdzie | Description |
url * |
body |
Adres witryny |
name |
body |
Name |
Przykładowa odpowiedź
{"ok":true,"project":{"id":3,"verified":false,"verify_token":"pp-verify-…"},"verification_hint":"…"}
POST
/shield/projects/{id}/verify
Sprawdź weryfikację domeny (rekord DNS TXT, plik .well-known albo metatag z verify_token). Wymagane przy płatnych skanach.
Przykładowa odpowiedź
{"ok":true,"verified":true,"method":"dns"}
POST
/shield/projects/{id}/scans
Uruchom skan: free (podstawowy, limit dzienny), deep lub comprehensive — te dwa są pobierane z salda.
| Parametr | Gdzie | Description |
mode |
body |
darmowy | głęboki | kompleksowy |
lang |
body |
Język raportu AI (ru/uk/en/pl) |
Przykładowa odpowiedź
{"ok":true,"scan_id":41,"mode":"deep","charged":19.0}
GET
/shield/scans/{id}
Status, ocena (score/grade), werdykt w formie sygnalizacji, zestawienie znalezisk według wagi i publiczny link do raportu.
Przykładowa odpowiedź
{"ok":true,"scan":{"id":41,"status":"done","score":86,"grade":"B","verdict":{...},"findings_summary":{"critical":0,"high":1,...},"share_report_url":"…"}}
Przykłady kodu
cURL — utwórz projekt i uruchom promocję
curl -X POST https://promopilot.link/api/v1/projects \
-H "Authorization: Bearer ppk_TWOJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{
"name": "Client Site",
"url": "https://client-site.com",
"language": "ru",
"links": [{"url": "https://client-site.com/services", "anchor": "usługi firmy"}]
}'
curl -X POST https://promopilot.link/api/v1/promotion/runs \
-H "Authorization: Bearer ppk_TWOJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{"project_id": 312, "url": "https://client-site.com/services"}'
PHP
<?php
$key = getenv('PROMOPILOT_API_KEY'); // nigdy nie trzymaj klucza w kodzie
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 "Started, run_id={$run['run_id']}, pobrano {$run['charged']}";
} catch (RuntimeException $e) {
if ($e->getMessage() === 'insufficient_funds') {
// doładuj saldo i ponów
}
}
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"Doładuj saldo: brakuje {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"])
# Odpytywanie statusu
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("Doładuj saldo:", 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
Pełna specyfikacja do odczytu maszynowego, do generowania klientów i importu do Postman/Insomnia: https://promopilot.link/api/v1/openapi.json
Pytania i sugestie dotyczące API kieruj przez wsparcie w panelu albo Telegram. Zgodność wsteczna: zmiany łamiące kontrakt trafiają wyłącznie do nowej wersji (/api/v2), a obecne pola nie są usuwane bez zapowiedzi w changelogu.