API 4pricing dla programistów
Połącz własny system (CRM, ERP, sklep internetowy) z 4pricing i odpytuj ceny konkurencji, grupy porównawcze oraz alerty bezpośrednio z poziomu swojego kodu.
Bazowy adres URL: https://4pricing.net/api/v1/pricing · Specyfikacja OpenAPI (JSON)
Szybka integracja w 3 krokach
To najkrótsza droga, by zobaczyć dane z 4pricing na własnej stronie. Skopiujesz jeden plik, wkleisz do niego swój klucz i wgrasz go na hosting — bez frameworka, bez instalacji.
- Aktywnej subskrypcji 4pricing (API jest wliczone w każdy plan).
- Hostingu z PHP — ma go prawie każdy zwykły hosting (cPanel, Plesk i podobne).
- Sposobu na wgranie pliku: menedżera plików u dostawcy hostingu lub programu FTP.
Utwórz swój klucz API
Zaloguj się na panel.4pricing.net i otwórz Monitoring cen → API i klucze.
Kliknij „Wygeneruj klucz”, nadaj mu nazwę (na przykład „mój sklep”) i skopiuj klucz zaczynający się od prc_live_. Wyświetla się tylko raz — jeśli go zgubisz, utwórz nowy, a stary unieważnij.
Skopiuj gotowy plik i wklej swój klucz
Skopiuj poniższy plik PHP do edytora tekstu (Notatnik, TextEdit lub VS Code) i zapisz go pod nazwą 4pricing-prices.php.
php4pricing-prices.php
<?php // ===================================================================== // WKLEJ TUTAJ SWÓJ KLUCZ API (panel.4pricing.net → API i klucze), między cudzysłowami $apiKey = 'WKLEJ_TUTAJ_KLUCZ'; // ===================================================================== header('Content-Type: text/html; charset=utf-8'); function h($text) { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); } function money($cents) { if ($cents === null) { return '—'; } return number_format($cents / 100, 2, ',', ' '); } if (strpos($apiKey, 'prc_') !== 0) { exit('<p>' . h('Brakuje klucza API: otwórz plik i wklej swój klucz w zaznaczonym wierszu.') . '</p>'); } $ch = curl_init('https://4pricing.net/api/v1/pricing/groups/'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_USERAGENT => 'my-shop-integration/1.0', CURLOPT_TIMEOUT => 30, CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Accept: application/json'], ]); $body = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); if ($body === false) { exit('<p>' . h('Nie udało się połączyć z 4pricing:' . ' ' . curl_error($ch)) . '</p>'); } if ($status !== 200) { $hints = [401 => 'klucz API jest błędny lub został unieważniony', 402 => 'konto nie ma aktywnej subskrypcji 4pricing (albo brakuje kredytów)', 429 => 'zbyt wiele zapytań w ciągu minuty, spróbuj ponownie za minutę']; $hint = isset($hints[$status]) ? ' — ' . $hints[$status] : ''; exit('<p>' . h('4pricing odpowiedział błędem' . ' ' . $status . $hint) . '</p><pre>' . h($body) . '</pre>'); } $groups = json_decode($body, true); $verdicts = [ 'cheapest' => 'Jesteś najtańszy', 'too_cheap' => 'Dużo taniej niż rynek — możesz podnieść cenę', 'competitive' => 'Konkurencyjna cena', 'too_expensive' => 'Drożej niż większość konkurentów', 'no_competitors' => 'Brak jeszcze ceny konkurencji', 'no_own_price' => 'Brakuje Twojej ceny', ]; echo '<!doctype html><html><head><meta charset="utf-8"><title>' . h('Moje monitorowane produkty') . '</title>'; echo '<style>body{font-family:Arial,sans-serif;margin:24px}table{border-collapse:collapse}'; echo 'th,td{border:1px solid #ddd;padding:6px 10px;text-align:left}th{background:#f5f5f5}</style>'; echo '</head><body><h1>' . h('Moje monitorowane produkty') . '</h1>'; if (!$groups) { echo '<p>' . h('Nie masz jeszcze żadnych porównywanych produktów.') . '</p>'; } else { echo '<table><tr><th>' . h('Produkt') . '</th><th>' . h('SKU') . '</th>'; echo '<th>' . h('Twoja cena') . '</th><th>' . h('Najtańsza konkurencja') . '</th>'; echo '<th>' . h('Różnica') . '</th><th>' . h('Werdykt') . '</th></tr>'; foreach ($groups as $g) { $yours = $g['own_price_cents_effective']; $cheapest = $g['min_competitor_cents']; $diff = '—'; if ($yours !== null && $cheapest !== null) { $diff = ($yours > $cheapest ? '+' : '') . money($yours - $cheapest); } $verdict = isset($verdicts[$g['verdict']]) ? $verdicts[$g['verdict']] : $g['verdict']; echo '<tr><td>' . h($g['name']) . '</td><td>' . h($g['sku']) . '</td>'; echo '<td>' . money($yours) . '</td><td>' . money($cheapest) . '</td>'; echo '<td>' . $diff . '</td><td>' . h($verdict) . '</td></tr>'; } echo '</table>'; } echo '</body></html>';Zastąp WKLEJ_TUTAJ_KLUCZ swoim kluczem, zachowując cudzysłowy wokół niego. To jedyny wiersz, który musisz zmienić.Wgraj plik na stronę i otwórz go w przeglądarce
Wgraj 4pricing-prices.php na swoją stronę za pomocą menedżera plików lub FTP, do tego samego folderu co strona (zwykle public_html).
Otwórz w przeglądarce https://twoja-strona/4pricing-prices.php. Zobaczysz tabelę z każdym porównywanym produktem: Twoja cena, najniższa cena konkurencji, różnica i werdykt.
Plik: lista Twoich produktów z najniższą ceną konkurencji
Plik pobiera z 4pricing Twoje porównywane produkty (GET /groups/) i pokazuje je w tabeli. Odczyt danych jest bezpłatny i nie zużywa kredytów.
- „Twoja cena” to cena, którą 4pricing zna dla Ciebie; „Najtańsza konkurencja” to najniższa cena znaleziona u konkurentów, których monitorujesz.
- Ceny są pokazane w pełnych jednostkach (12999 groszy to 129,99); kreska (—) oznacza, że 4pricing nie ma jeszcze ceny, a nie że cena wynosi zero.
- Jeśli zamiast tabeli widzisz błąd, komunikat podpowiada, co poprawić: błędny klucz, nieaktywna subskrypcja albo zbyt wiele zapytań w ciągu minuty.
Drugi przepis: wyślij swoją cenę
Użyj go, gdy 4pricing ma poznać Twoją cenę bez odczytywania jej ze strony sklepu — na przykład prosto z bazy danych sklepu. Wysyłanie własnych cen jest bezpłatne (bez kredytów) i nie wlicza się do limitu monitorowanych adresów URL w Twoim planie.
| Pole | Znaczenie |
|---|---|
| sku | Twój kod produktu; 4pricing po nim znajduje produkt albo tworzy go przy pierwszym wysłaniu |
| name | nazwa produktu, taka, jaką chcesz widzieć w 4pricing |
| url | opcjonalnie: adres strony produktu w Twoim sklepie (nigdy nie jest odczytywany) |
| price_cents | cena pomnożona przez 100, bez ułamków: 49,99 → 4999 |
| currency | kod waluty, na przykład EUR, GBP lub RON (RON, jeśli go brakuje) |
php4pricing-send-price.php
<?php
// =====================================================================
// WKLEJ TUTAJ SWÓJ KLUCZ API (panel.4pricing.net → API i klucze), między cudzysłowami
$apiKey = 'WKLEJ_TUTAJ_KLUCZ';
// =====================================================================
// TWOJE PRODUKTY: jeden blok na produkt. price_cents = cena x 100 (49,99 daje 4999).
$products = [
[
'sku' => 'BIALA-KOSZULKA-M',
'name' => 'Biała koszulka, rozmiar M',
'url' => 'https://twoj-sklep.example/biala-koszulka-m',
'price_cents' => 4999,
'currency' => 'EUR',
],
];
header('Content-Type: text/html; charset=utf-8');
function h($text) { return htmlspecialchars((string) $text, ENT_QUOTES, 'UTF-8'); }
if (strpos($apiKey, 'prc_') !== 0) {
exit('<p>' . h('Brakuje klucza API: otwórz plik i wklej swój klucz w zaznaczonym wierszu.') . '</p>');
}
$ch = curl_init('https://4pricing.net/api/v1/pricing/own-products/bulk');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERAGENT => 'my-shop-integration/1.0',
CURLOPT_TIMEOUT => 60,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Content-Type: application/json', 'Accept: application/json'],
CURLOPT_POSTFIELDS => json_encode(['items' => $products], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
]);
$body = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($body === false) {
exit('<p>' . h('Nie udało się połączyć z 4pricing:' . ' ' . curl_error($ch)) . '</p>');
}
if ($status !== 200) {
$hints = [401 => 'klucz API jest błędny lub został unieważniony', 402 => 'konto nie ma aktywnej subskrypcji 4pricing (albo brakuje kredytów)', 429 => 'zbyt wiele zapytań w ciągu minuty, spróbuj ponownie za minutę'];
$hint = isset($hints[$status]) ? ' — ' . $hints[$status] : '';
exit('<p>' . h('4pricing odpowiedział błędem' . ' ' . $status . $hint) . '</p><pre>' . h($body) . '</pre>');
}
$result = json_decode($body, true);
echo '<h1>' . h('Ceny wysłane do 4pricing') . '</h1>';
echo '<p>' . h('Utworzone nowe produkty:') . ' ' . (int) $result['created'] . '</p>';
echo '<p>' . h('Zaktualizowane produkty:') . ' ' . (int) $result['updated'] . '</p>';
if (!empty($result['errors'])) {
echo '<p>' . h('Wiersze z problemami:') . '</p><ul>';
foreach ($result['errors'] as $error) {
echo '<li>' . h($error) . '</li>';
}
echo '</ul>';
}
- Zapisz go jako 4pricing-send-price.php, wklej swój klucz, uzupełnij wiersze z produktami, wgraj plik i otwórz go jednorazowo: strona pokaże, ile produktów zostało utworzonych, a ile zaktualizowanych.
- Jedno zapytanie może zawierać do 2000 produktów; błędny wiersz zostanie zgłoszony, a pozostałe i tak się zapiszą. Ponowne wysłanie tego samego sku aktualizuje cenę.
Te same wywołania w curl
Jeśli wolisz terminal, te dwa polecenia robią dokładnie to samo co dwa pliki PHP:
bash
curl https://4pricing.net/api/v1/pricing/groups/ \
-H "X-API-Key: WKLEJ_TUTAJ_KLUCZ"bash
curl -X POST https://4pricing.net/api/v1/pricing/own-products/bulk \
-H "X-API-Key: WKLEJ_TUTAJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{"items":[{"sku":"BIALA-KOSZULKA-M","name":"Biała koszulka, rozmiar M","url":"https://twoj-sklep.example/biala-koszulka-m","price_cents":4999,"currency":"EUR"}]}'- Klucz daje dostęp do całego Twojego konta 4pricing. Trzymaj go wyłącznie w plikach po stronie serwera (PHP, Twój backend) — nigdy w JavaScripcie w przeglądarce ani w publicznym repozytorium.
- Każdy, kto zna adres wgranej strony, zobaczy tabelę z cenami (klucza nie zobaczy). Nadaj plikowi nazwę trudną do odgadnięcia, zabezpiecz folder hasłem albo usuń plik, gdy już go nie potrzebujesz.
- Jeśli klucz wycieknie, unieważnij go w API i klucze — przestaje działać natychmiast — i utwórz nowy.
Uwierzytelnianie
- Każde zapytanie przekazuje Twój klucz w nagłówku X-API-Key. Działa też Authorization: Bearer prc_live_… (tylko dla kluczy zaczynających się od prc_).
- Nie ma zakresów uprawnień: klucz może odczytywać i zmieniać wszystko na Twoim koncie 4pricing. Utwórz osobny klucz dla każdej integracji (sklep, ERP, raport), aby móc unieważnić jeden bez zatrzymywania pozostałych.
- Brakujący, błędny lub unieważniony klucz kończy się odpowiedzią 401. Unieważnienie działa natychmiast. Zbyt wiele nieprawidłowych kluczy w ciągu minuty kończy się odpowiedzią 429.
- Klucze tworzy się, przegląda i unieważnia wyłącznie w panelu (Monitoring cen → API i klucze); samo API nie zarządza kluczami. Utworzenie klucza wymaga aktywnej subskrypcji 4pricing.
http
X-API-Key: prc_live_…
# or
Authorization: Bearer prc_live_…Bazowy adres URL i konwencje
- Każda ścieżka w tej dokumentacji jest względna wobec bazowego adresu URL — /groups/ oznacza https://4pricing.net/api/v1/pricing/groups/.
- Zapytania i odpowiedzi mają format JSON (Content-Type: application/json).
- Kwoty to liczby całkowite w najmniejszych jednostkach waluty (groszach, centach): price_cents = 12999 oznacza 129,99. Wartości procentowe podawane są w punktach bazowych (100 = 1%).
- Nieznana wartość to null, nigdy 0 — pokaż ją jako „—”.
- Kolekcje wywołuje się z ukośnikiem na końcu (/products/, /groups/, /insights/).
- Listy podzielone na strony zwracają łączną liczbę w nagłówku X-Total-Count (na przykład GET /products/?skip=0&limit=200).
- Wysyłaj nagłówek User-Agent z nazwą Twojej integracji (na przykład my-shop-integration/1.0). Zapora przed API odpowiada kodem 403 na niektóre ogólne ustawienia domyślne bibliotek, takie jak Python-urllib.
Limit zapytań, subskrypcja i limity planu
Limit zapytań
- 60 zapytań na minutę na KONTO, w oknie przesuwnym — wszystkie klucze tego samego konta dzielą te same 60.
- Każda odpowiedź zawiera X-RateLimit-Limit, X-RateLimit-Remaining i X-RateLimit-Reset (długość okna, w sekundach).
- Po przekroczeniu limitu dostajesz 429 z nagłówkiem Retry-After (w sekundach). Poczekaj tyle czasu i ponów zapytanie — poniższe przykłady robią to za Ciebie.
http
HTTP/2 200
x-ratelimit-limit: 60
x-ratelimit-remaining: 59
x-ratelimit-reset: 60
HTTP/2 429
retry-after: 60
{"detail":"Rate limit exceeded"}Subskrypcja i limity planu
- API wymaga aktywnej subskrypcji 4pricing; bez niej każde zapytanie kończy się kodem 402 z kodem błędu plan_required.
- Każdy plan zawiera API z tymi samymi limitami co panel. Przekroczenie limitu przy dodawaniu lub ponownym włączaniu czegoś kończy się kodem 402 z kodem błędu plan_limit i komunikatem, którego limitu dotyczy.
| Limit | Znaczenie |
|---|---|
| Monitorowane adresy URL | Każdy adres strony produktu, który 4pricing odczytuje, liczy się osobno: Twoja własna strona i każda strona konkurenta. Ceny wysyłane przez /own-products nie są wliczane. |
| Sklepy konkurencji | Liczba różnych witryn konkurencji (domen), które monitorujesz. |
| Minimalny odstęp między sprawdzeniami | Jak często ten sam adres URL może być sprawdzany. Krótsze odstępy są automatycznie podnoszone do minimum Twojego planu; zbyt wczesne odświeżenie adresu URL kończy się kodem 402 plan_limit. |
Które wywołania zużywają kredyty
Odczyt strony sklepu kosztuje kredyty z portfela Twojego konta. Poniższe wywołania sprawdzają saldo PRZED odczytem czegokolwiek: jeśli nie wystarcza, dostajesz 402 insufficient_credits, a jeśli portfela nie można teraz odczytać, dostajesz 503 credit_wallet_unavailable (spróbuj później). W obu przypadkach nic nie jest odczytywane i nic nie jest pobierane.
Wywołania zużywające kredyty
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| POST | /groups/{group_id}/members | Dodaj adres URL do grupy, przez product_id lub z nowego adresu (nowy adres jest odczytywany od razu) | Kredyty |
| POST | /groups/{group_id}/refresh | Sprawdź teraz każdy adres URL w grupie | Kredyty |
| POST | /groups/{group_id}/suggestions | „Znajdź konkurentów”: proponowane adresy URL konkurentów z oceną i uzasadnieniem | Kredyty |
| POST | /products/ | Zacznij monitorować adres URL (url, name, rola own lub competitor) — jest odczytywany od razu | Kredyty |
| POST | /products/{product_id}/refresh | Sprawdź teraz cenę pod tym adresem URL (z poszanowaniem minimalnego odstępu Twojego planu) | Kredyty |
| POST | /search/ | Wyszukaj aktualną cenę produktu u sprzedawców (kredyty za każdego przeszukanego sprzedawcę) | Kredyty |
Wywołania bezpłatne
- Każdy odczyt (GET): produkty, grupy, porównania, historia, alerty, wnioski, podsumowania dnia, kredyty, statystyki.
- POST /own-products/ i POST /own-products/bulk — Twoje własne ceny.
- POST /sales/bulk — Twoja sprzedaż.
- Alerty, ustawienia, potwierdzenia i wszystko inne, co nie odczytuje strony sklepu.
- Zaplanowane monitorowanie śledzonych adresów URL także zużywa kredyty, przy każdym sprawdzeniu; GET /credits/ pokazuje Twoje saldo i prognozę.
- POST /products/import dodaje wiele adresów URL naraz bez natychmiastowego odczytu: są one sprawdzane — i rozliczane — przez zaplanowane monitorowanie.
Opis endpointów
Wszystkie operacje dostępne dla klucza, pogrupowane według przeznaczenia. Specyfikacja OpenAPI opisuje każdą z nich pole po polu, wraz z dokładną treścią zapytania i odpowiedzi.
Twoje własne ceny
Podaj 4pricing swoją cenę bezpośrednio, bez odczytywania strony sklepu. Bezpłatnie.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| POST | /own-products/ | Wyślij swoją aktualną cenę jednego produktu (wyszukiwanego po sku, a w razie braku po nazwie; tworzony, jeśli nie istnieje) | Bezpłatnie |
| POST | /own-products/bulk | Wyślij swoje ceny do 2000 produktów w jednym zapytaniu; błędne wiersze są zgłaszane, reszta zostaje zapisana | Bezpłatnie |
Porównywane produkty (grupy)
Grupa to jeden Twój produkt razem ze stronami konkurentów, którzy sprzedają to samo. Przechowuje werdykt i rekomendowaną cenę.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /groups/ | Wszystkie porównywane produkty, każdy z Twoją ceną, najniższą ceną konkurencji, werdyktem i rekomendowaną ceną | Bezpłatnie |
| POST | /groups/ | Utwórz porównywany produkt (nazwa, sku, Twoja cena, koszt, strategia cenowa) | Bezpłatnie |
| GET | /groups/{group_id} | Jeden porównywany produkt | Bezpłatnie |
| PUT | /groups/{group_id} | Zaktualizuj cenę, koszt, strategię, odstęp sprawdzeń lub powiązanie ze sklepem (store_kind) | Bezpłatnie |
| DELETE | /groups/{group_id} | Usuń grupę; jej adresy URL pozostają monitorowane | Bezpłatnie |
| GET | /groups/{group_id}/comparison | Twoja pozycja wobec każdego konkurenta, z rekomendowaną ceną i sposobem jej obliczenia | Bezpłatnie |
| GET | /groups/{group_id}/history | Historia cen każdego adresu URL w grupie | Bezpłatnie |
| GET | /groups/series | Twoja pozycja na rynku, dzień po dniu, dla wszystkich produktów | Bezpłatnie |
| POST | /groups/{group_id}/members | Dodaj adres URL do grupy, przez product_id lub z nowego adresu (nowy adres jest odczytywany od razu) | Kredyty |
| DELETE | /groups/{group_id}/members/{product_id} | Usuń adres URL z grupy (pozostaje monitorowany) | Bezpłatnie |
| POST | /groups/{group_id}/refresh | Sprawdź teraz każdy adres URL w grupie | Kredyty |
| POST | /groups/{group_id}/apply-recommended | Ustaw rekomendowaną cenę jako swoją cenę w 4pricing (wyślij cenę widzianą wcześniej; 409, jeśli się zmieniła) | Bezpłatnie |
| GET | /groups/suggestions/sources | Źródła dostępne dla funkcji „Znajdź konkurentów” | Bezpłatnie |
| POST | /groups/{group_id}/suggestions | „Znajdź konkurentów”: proponowane adresy URL konkurentów z oceną i uzasadnieniem | Kredyty |
Monitorowane adresy URL
Jeden wiersz na adres strony odczytywany przez 4pricing, Twój lub konkurenta (rola own lub competitor).
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /products/ | Monitorowane adresy URL, podzielone na strony (skip, limit do 500; łączna liczba w X-Total-Count) | Bezpłatnie |
| POST | /products/ | Zacznij monitorować adres URL (url, name, rola own lub competitor) — jest odczytywany od razu | Kredyty |
| POST | /products/import | Dodaj wiele adresów URL naraz; zostaną odczytane później przez zaplanowane monitorowanie | Bezpłatnie |
| GET | /products/export | Eksport aktywnych adresów URL do CSV | Bezpłatnie |
| GET | /products/{product_id} | Jeden monitorowany adres URL z ostatnią ceną | Bezpłatnie |
| PATCH | /products/{product_id} | Zmień nazwę, kategorię lub odstęp między sprawdzeniami | Bezpłatnie |
| DELETE | /products/{product_id} | Przestań monitorować adres URL (pozostaje jako nieaktywny) | Bezpłatnie |
| POST | /products/{product_id}/refresh | Sprawdź teraz cenę pod tym adresem URL (z poszanowaniem minimalnego odstępu Twojego planu) | Kredyty |
| GET | /products/{product_id}/history | Historia cen jednego adresu URL | Bezpłatnie |
| GET | /products/sparklines | Krótka historia cen dla kilku adresów URL naraz | Bezpłatnie |
| GET | /products/price-changes | Adresy URL, których cena zmieniła się przy ostatnim sprawdzeniu | Bezpłatnie |
Wyszukiwanie na żywo
Sprawdź cenę produktu u sprzedawców od razu.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| POST | /search/ | Wyszukaj aktualną cenę produktu u sprzedawców (kredyty za każdego przeszukanego sprzedawcę) | Kredyty |
Alerty
Reguły cenowe i skrzynka alertów, które się uruchomiły.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /alerts/ | Twoje alerty cenowe | Bezpłatnie |
| POST | /alerts/ | Utwórz alert (cena poniżej/powyżej, spadek w %, ponowna dostępność, podcięcie przez konkurenta…) | Bezpłatnie |
| DELETE | /alerts/{alert_id} | Usuń alert | Bezpłatnie |
| POST | /alerts/{alert_id}/rearm | Uzbrój ponownie alert, który się uruchomił | Bezpłatnie |
| GET | /alerts/events | Skrzynka alertów, które się uruchomiły | Bezpłatnie |
| GET | /alerts/events/unread-count | Ile masz nieprzeczytanych alertów | Bezpłatnie |
| POST | /alerts/events/read-all | Oznacz alerty jako przeczytane (opcjonalnie tylko te sprzed wskazanego momentu) | Bezpłatnie |
| POST | /alerts/events/{event_id}/read | Oznacz jeden alert jako przeczytany | Bezpłatnie |
Kredyty
Saldo, prognoza i każdy ruch kredytów.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /credits/ | Saldo kredytów i prognoza zużycia | Bezpłatnie |
| GET | /credits/ledger | Każdy ruch kredytów | Bezpłatnie |
| GET | /credits/by-product | Kredyty zużyte na adres URL (łącznie i w ostatnich 30 dniach) | Bezpłatnie |
Twoja sprzedaż
Dzienna sprzedaż na produkt, służąca do obliczania wartości każdej okazji. Bezpłatnie.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| POST | /sales/bulk | Dzienna sprzedaż na produkt (sku lub group_id, day, units_sold, revenue_cents; opcjonalnie stock_units, cost_cents), do 2000 wierszy; ponowne wysłanie tego samego produktu i dnia nadpisuje wpis | Bezpłatnie |
Wnioski, okazje i podsumowania dnia
Co się zmieniło, dlaczego to ważne i co zrobić — oraz dzienne podsumowanie.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /insights/ | Wnioski z dowodami i propozycjami cen (X-Total-Count) | Bezpłatnie |
| GET | /insights/{insight_id} | Jeden wniosek ze szczegółami | Bezpłatnie |
| POST | /insights/{insight_id}/dismiss | Odrzuć wniosek | Bezpłatnie |
| GET | /opportunities/ | Podsumowanie okazji: wartość wyliczona z Twojej sprzedaży, oddzielona od szacunku rynkowego | Bezpłatnie |
| GET | /briefings/ | Historia dziennych podsumowań | Bezpłatnie |
| GET | /briefings/latest | Ostatnie dzienne podsumowanie | Bezpłatnie |
| POST | /briefings/generate | Zbuduj ponownie dzisiejsze podsumowanie z otwartych wniosków (nic nie wysyła) | Bezpłatnie |
Akcje cenowe
Zmiany cen, które zostały zaproponowane, wysłane, zastosowane lub cofnięte. Przez API możesz je odczytywać i potwierdzać te otrzymane przez webhook.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /actions/ | Akcje cenowe: zaproponowane, wysłane, zastosowane, cofnięte | Bezpłatnie |
| POST | /actions/{action_id}/confirm | Potwierdź, że Twój system zastosował cenę otrzymaną przez price.apply lub price.revert | Bezpłatnie |
Ustawienia
Ustawienia konta, w tym adres i sekret webhooka.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /settings/ | Ustawienia konta (sprzedawcy, strategia repricingu, dostarczanie alertów, webhook) | Bezpłatnie |
| PUT | /settings/ | Zmień ustawienia, w tym alert_webhook_url, alert_webhook_secret i alert_webhook_enabled | Bezpłatnie |
Statystyki
Przegląd konta i kondycja sprawdzeń cen.
| Metoda | Endpoint | Co robi | Koszt |
|---|---|---|---|
| GET | /stats/ | Statystyki konta: adresy URL, alerty, zmiany z 24 godzin, grupy | Bezpłatnie |
| GET | /stats/scrapers | Kondycja sprawdzeń cen w podziale na sprzedawców | Bezpłatnie |
53 endpoints · https://4pricing.net/api/v1/pricing/openapi.json
Czego API nie potrafi
Niektóre rzeczy celowo zostają w panelu, gdzie decyduje zalogowana osoba. Przez API te ścieżki odpowiadają kodem 404:
| Ścieżka | Dlaczego |
|---|---|
| /keys/… | Tworzenie i unieważnianie kluczy — klucz nie może tworzyć kolejnych kluczy. |
| /autopilot/… | Polityka autopilota, która decyduje, co może się zmienić bez udziału człowieka; wyciekły klucz nie może jej rozszerzyć. |
| /watchlists/… | Listy obserwowanych, które zużywają kredyty przy każdym uruchomieniu. |
| /actions/{action_id}/approve · reject · revert | Zatwierdzenie, odrzucenie lub cofnięcie zmiany ceny to decyzja, którą człowiek podejmuje w panelu. API może tylko odczytywać akcje i je potwierdzać. |
| /insights/{insight_id}/act · /insights/run | Działanie na podstawie wniosku (zapisuje ceny) i ponowne przeliczanie wniosków. |
| /admin/… · /internal/… | Wewnętrzna administracja platformy. |
Webhooki
4pricing może wysyłać zdarzenia na Twój adres (webhook): wykonuje POST z JSON-em za każdym razem, gdy coś się wydarzy, więc Twój system nie musi ciągle pytać.
Na konto przypada jeden adres webhooka; trafiają na niego wszystkie typy zdarzeń. Rozróżnisz je po polu event — alert jest jedynym zdarzeniem bez tego pola.
Włączanie
- Ustaw alert_webhook_url (publiczny adres http/https na porcie 80 lub 443), alert_webhook_secret oraz alert_webhook_enabled: true — w panelu lub przez PUT /settings/.
- Adres wskazujący na sieć prywatną lub wewnętrzną jest odrzucany kodem 422. Przekierowania nie są śledzone.
- Dla briefing.ready włącz także briefing_webhook_enabled. price.apply i price.revert są wysyłane tylko dla grup powiązanych z Twoim sklepem przez store_kind = webhook i tylko wtedy, gdy ustawiono sekret.
bash
curl -X PUT https://4pricing.net/api/v1/pricing/settings/ \
-H "X-API-Key: WKLEJ_TUTAJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{"alert_webhook_url":"https://your-shop.example/4pricing-webhook.php","alert_webhook_secret":"TWOJ_SEKRET_WEBHOOKA","alert_webhook_enabled":true,"briefing_webhook_enabled":true}'Zdarzenia
| Zdarzenie | Znaczenie |
|---|---|
| alert | Uruchomił się alert cenowy. Treść: event_id (deduplikuj po nim), alert_id, alert_type, message, message_code, message_params, price_cents, currency, triggered_at, product, group, panel_url. Bez pola event. |
| price.apply | Zatwierdzona cena do zastosowania w Twoim sklepie: action_id, group (id, name, sku, ean, store_product_id), old_price_cents, new_price_cents, currency, reason_code, mode i confirm.path. |
| price.revert | Powrót do poprzedniej ceny. Ta sama treść co w price.apply; zastosuj ją i potwierdź w ten sam sposób. |
| briefing.ready | Dzienne podsumowanie jest gotowe: day, headline, summary, sections, total_value_cents (tylko z Twojej sprzedaży), currency, panel_url. |
Nagłówki każdego zapytania webhooka
| Nagłówek | Znaczenie |
|---|---|
| X-4Pricing-Timestamp | Kiedy zapytanie zostało wysłane, w sekundach Unix. Odrzucaj zapytania, które są zbyt stare (na przykład ponad 5 minut). |
| X-4Pricing-Signature | sha256= a po nim szesnastkowy HMAC-SHA256 tekstu „<timestamp>.<raw body>”, obliczony z Twoim sekretem webhooka. Obecny, gdy ustawiono sekret (zawsze przy price.apply i price.revert). |
| Content-Type | application/json; charset=utf-8 |
| User-Agent | 4pricing-alerts/1.0 |
Weryfikacja podpisu
Oblicz podpis ponownie z nagłówka timestamp i SUROWEJ treści, która dotarła, porównaj go w stałym czasie i sprawdź znacznik czasu. Dopiero wtedy przetwarzaj zdarzenie. Gdy je przyjmiesz, odpowiedz dowolnym kodem 2xx.
php
<?php
$secret = 'TWOJ_SEKRET_WEBHOOKA';
// Użyj SUROWEJ treści, dokładnie takiej, jaka dotarła — nie ponownie zakodowanego JSON-a
$body = file_get_contents('php://input');
$timestamp = isset($_SERVER['HTTP_X_4PRICING_TIMESTAMP']) ? $_SERVER['HTTP_X_4PRICING_TIMESTAMP'] : '';
$signature = isset($_SERVER['HTTP_X_4PRICING_SIGNATURE']) ? $_SERVER['HTTP_X_4PRICING_SIGNATURE'] : '';
$expected = 'sha256=' . hash_hmac('sha256', $timestamp . '.' . $body, $secret);
// Błędny podpis lub starszy niż 5 minut: odrzuć
if (!hash_equals($expected, $signature) || abs(time() - (int) $timestamp) > 300) {
http_response_code(401);
exit;
}
$event = json_decode($body, true);
if (isset($event['event']) && ($event['event'] === 'price.apply' || $event['event'] === 'price.revert')) {
// Zastosuj new_price_cents w produkcie w swoim sklepie (dopasuj go po sku lub store_product_id)
// $event['group']['sku'], $event['new_price_cents'], $event['currency']
// Potem potwierdź: POST /actions/{action_id}/confirm ze swoim kluczem API
}
http_response_code(200);
echo 'ok';
javascript
// Node.js, no package needed
const http = require("http");
const crypto = require("crypto");
const SECRET = process.env.WEBHOOK_SECRET; // TWOJ_SEKRET_WEBHOOKA
http.createServer((req, res) => {
const chunks = [];
req.on("data", (c) => chunks.push(c));
req.on("end", () => {
// Użyj SUROWEJ treści, dokładnie takiej, jaka dotarła — nie ponownie zakodowanego JSON-a
const body = Buffer.concat(chunks);
const ts = String(req.headers["x-4pricing-timestamp"] || "");
const sig = String(req.headers["x-4pricing-signature"] || "");
const expected = "sha256=" + crypto.createHmac("sha256", SECRET).update(ts + ".").update(body).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
const same = sig.length === expected.length && crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected));
if (!same || !fresh) {
// Błędny podpis lub starszy niż 5 minut: odrzuć
res.writeHead(401);
return res.end();
}
const event = JSON.parse(body.toString("utf8"));
if (event.event === "price.apply" || event.event === "price.revert") {
// Zastosuj new_price_cents w produkcie w swoim sklepie (dopasuj go po sku lub store_product_id)
// Potem potwierdź: POST /actions/{action_id}/confirm ze swoim kluczem API
}
res.writeHead(200);
res.end("ok");
});
}).listen(3000);
python
# Python 3, standard library only
import hashlib
import hmac
import time
SECRET = "TWOJ_SEKRET_WEBHOOKA"
def is_valid(raw_body: bytes, timestamp: str, signature: str) -> bool:
# Użyj SUROWEJ treści, dokładnie takiej, jaka dotarła — nie ponownie zakodowanego JSON-a
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
return False
expected = "sha256=" + hmac.new(
SECRET.encode(), timestamp.encode() + b"." + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
Dostarczanie i ponowienia
- Alerty: przy błędzie połączenia lub DNS, kodzie 429 albo 5xx zapytanie jest ponawiane z wykładniczym odstępem, maksymalnie 5 prób. Każdy inny kod 4xx jest ostateczny.
- Jeśli zapytanie wyszło, ale odpowiedź nie nadeszła w ciągu 10 sekund, nie jest wysyłane ponownie (by uniknąć duplikatu) — rzadko możesz mimo to dostać duplikat, więc deduplikuj po event_id.
- price.apply i price.revert są wysyłane jeden raz: jeśli dostarczenie się nie powiedzie, akcja otrzymuje status failed. Deduplikuj po action_id.
- briefing.ready jest wysyłane raz dziennie.
Potwierdzanie zastosowanej ceny
- price.apply nie jest uznawane za zastosowane, dopóki Twój system tego nie potwierdzi. Po zmianie ceny w sklepie wywołaj POST /actions/{action_id}/confirm ze swoim kluczem API: akcja przechodzi z „wysłana” do „zastosowana”.
- Podwójne potwierdzenie jest nieszkodliwe — akcja, która jest już zastosowana, zostanie zwrócona bez zmian.
bash
curl -X POST https://4pricing.net/api/v1/pricing/actions/ACTION_ID/confirm \
-H "X-API-Key: WKLEJ_TUTAJ_KLUCZ"Przykładowa treść
json · price.apply
{
"event": "price.apply",
"action_id": "8d0f2c4e-0b7a-4f7e-9d61-3c2b1a0e9f55",
"tenant_id": "…",
"group": {
"id": "2fa912df-440c-4ddf-896a-5d558d56085f",
"name": "Canon RF 50mm F1.8 STM",
"sku": "4515C005AA",
"ean": "4549292181623",
"store_product_id": "1042"
},
"old_price_cents": 124900,
"new_price_cents": 118699,
"currency": "RON",
"reason_code": "undercut",
"mode": "manual",
"confirm": { "method": "POST", "path": "/api/v1/pricing/actions/8d0f2c4e-0b7a-4f7e-9d61-3c2b1a0e9f55/confirm" }
}json · alert
{
"event_id": 18342,
"alert_id": "c1b5…",
"alert_type": "competitor_undercut",
"tenant_id": "…",
"message_code": "competitor_undercut",
"message": "…",
"message_params": { "…": "…" },
"price_cents": 118990,
"currency": "RON",
"triggered_at": "2026-10-13T07:05:12",
"product": { "id": "…", "name": "…", "url": "https://…", "retailer": "…", "current_price_cents": 118990, "in_stock": true },
"group": { "id": "…", "name": "Canon RF 50mm F1.8 STM", "sku": "4515C005AA" },
"panel_url": "https://panel.4pricing.net/…"
}Błędy
Błędy przychodzą z właściwym statusem HTTP i treścią JSON {"detail": …}. detail to tekst, obiekt z kodem, który możesz obsłużyć w swoim programie, albo — przy 422 — lista nieprawidłowych pól.
| Status | Kod | Znaczenie |
|---|---|---|
| 401 | — | Klucz API jest nieobecny, błędny lub unieważniony. |
| 402 | plan_required | Konto nie ma aktywnej subskrypcji 4pricing. |
| 402 | plan_limit | Zostałby przekroczony limit planu (monitorowane adresy URL, sklepy konkurencji) albo odświeżenie nastąpiło przed minimalnym odstępem. Komunikat wskazuje, który to limit. |
| 402 | insufficient_credits | Za mało kredytów na wywołanie, które odczytuje stronę sklepu. Nic nie zostało odczytane ani pobrane. |
| 403 | — | Zablokowane przez zaporę, zanim żądanie dotarło do API — zwykle z powodu ogólnego User-Agent, takiego jak Python-urllib. Wyślij własny User-Agent. |
| 404 | — | Zasób nie istnieje, nie należy do Twojego konta albo ścieżka nie jest dostępna przez API. |
| 409 | recommendation_moved · own_from_listing · invalid_state · webhook_secret_required | Konflikt: rekomendowana cena się zmieniła (recommendation_moved), akcja jest w innym stanie (invalid_state), wymagany jest sekret webhooka albo operacja nie dotyczy tego elementu. |
| 422 | — | Nieprawidłowe dane, na przykład adres URL bez http/https albo brakujące wymagane pole. detail wymienia pola. |
| 429 | — | Ponad 60 zapytań na minutę dla konta (albo zbyt wiele nieprawidłowych kluczy). Poczekaj tyle sekund, ile wskazuje Retry-After. |
| 503 | credit_wallet_unavailable | Portfela kredytów nie można teraz odczytać. Spróbuj później; nic nie zostało pobrane. |
Treść błędów
json
{"detail": "Invalid or missing API key"}
{"detail": {"code": "plan_required", "message": "…"}}
{"detail": {"code": "insufficient_credits", "message": "…"}}
{"detail": [{"loc": ["body", "price_cents"], "msg": "…", "type": "…"}]}Przykłady kodu
Ten sam odczyt w czterech językach, z pauzą i ponowieniem po kodzie 429, a następnie wysyłanie sprzedaży. Zastąp WKLEJ_TUTAJ_KLUCZ swoim kluczem (albo ustaw zmienną środowiskową PRICING_API_KEY).
Node.js: odczytaj porównywane produkty
javascript
// Node.js 18+ (fetch is built in, no package needed)
const API_KEY = process.env.PRICING_API_KEY; // WKLEJ_TUTAJ_KLUCZ
const BASE = "https://4pricing.net/api/v1/pricing";
async function call(path, options = {}) {
for (let attempt = 0; attempt < 3; attempt++) {
const res = await fetch(BASE + path, {
...options,
headers: { "X-API-Key": API_KEY, "Content-Type": "application/json", ...(options.headers || {}) },
});
if (res.status === 429) {
// Ponad 60 zapytań na minutę: poczekaj tyle sekund, ile wskazuje Retry-After, i spróbuj ponownie
const wait = Number(res.headers.get("Retry-After") || 60);
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
if (!res.ok) throw new Error(res.status + " " + (await res.text()));
return res.status === 204 ? null : res.json();
}
throw new Error("rate limited");
}
async function main() {
// Twoje porównywane produkty: Twoja cena, najtańsza konkurencja, werdykt
const groups = await call("/groups/");
for (const g of groups) {
console.log(g.name, g.own_price_cents_effective, g.min_competitor_cents, g.verdict);
}
}
main().catch((err) => {
console.error(err.message);
process.exit(1);
});
Python: odczytaj porównywane produkty
python
# Python 3, standard library only
import json
import os
import time
import urllib.error
import urllib.request
API_KEY = os.environ["PRICING_API_KEY"] # WKLEJ_TUTAJ_KLUCZ
BASE = "https://4pricing.net/api/v1/pricing"
def call(path, method="GET", payload=None):
data = json.dumps(payload).encode() if payload is not None else None
for _ in range(3):
req = urllib.request.Request(BASE + path, data=data, method=method, headers={
"X-API-Key": API_KEY, "Content-Type": "application/json", "Accept": "application/json",
# Ustaw własny User-Agent: domyślny w niektórych bibliotekach HTTP (Python urllib) jest blokowany kodem 403
"User-Agent": "my-shop-integration/1.0",
})
try:
with urllib.request.urlopen(req, timeout=30) as res:
body = res.read()
return json.loads(body) if body else None
except urllib.error.HTTPError as err:
if err.code == 429:
# Ponad 60 zapytań na minutę: poczekaj tyle sekund, ile wskazuje Retry-After, i spróbuj ponownie
time.sleep(int(err.headers.get("Retry-After", "60")))
continue
raise
raise RuntimeError("rate limited")
# Twoje porównywane produkty: Twoja cena, najtańsza konkurencja, werdykt
for g in call("/groups/"):
print(g["name"], g["own_price_cents_effective"], g["min_competitor_cents"], g["verdict"])
curl: wyślij dzienną sprzedaż
bash
curl -X POST https://4pricing.net/api/v1/pricing/sales/bulk \
-H "X-API-Key: WKLEJ_TUTAJ_KLUCZ" \
-H "Content-Type: application/json" \
-d '{"rows":[{"sku":"BIALA-KOSZULKA-M","day":"2026-10-12","units_sold":7,"revenue_cents":34993}]}'PHP: wyślij dzienną sprzedaż
php
<?php
$apiKey = 'WKLEJ_TUTAJ_KLUCZ';
// Dzienna sprzedaż na produkt (według sku lub group_id), do 2000 wierszy na zapytanie
$rows = [
['sku' => 'BIALA-KOSZULKA-M', 'day' => '2026-10-12', 'units_sold' => 7, 'revenue_cents' => 34993],
];
$ch = curl_init('https://4pricing.net/api/v1/pricing/sales/bulk');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_USERAGENT => 'my-shop-integration/1.0',
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['X-API-Key: ' . $apiKey, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['rows' => $rows]),
]);
$body = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE) . ' ' . $body; // 200 {"upserted": 1, "skipped": []}
Najczęściej zadawane pytania
Czy muszę być programistą, żeby używać API 4pricing?
Nie. Szybki start daje Ci gotowy plik PHP: tworzysz klucz w panelu, wklejasz go do pliku, wgrywasz plik na hosting i otwierasz go w przeglądarce. Przy bardziej zaawansowanych integracjach (ERP, platforma sklepowa) programista może skorzystać z opisu endpointów i specyfikacji OpenAPI.
Ile kosztuje API 4pricing?
API jest wliczone w każdą subskrypcję 4pricing, z tymi samymi limitami co panel. Odczyt Twoich danych jest bezpłatny; kredyty zużywają tylko wywołania, które odczytują stronę sklepu (dodanie nowego adresu URL, odświeżenie, wyszukiwanie na żywo, znajdowanie konkurentów) — tak samo jak w panelu.
Które wywołania zużywają kredyty?
POST /products/, POST /products/{product_id}/refresh, POST /groups/{group_id}/refresh, POST /groups/{group_id}/members z nowym adresem URL, POST /groups/{group_id}/suggestions oraz POST /search/. Jeśli saldo nie wystarcza, dostajesz 402 insufficient_credits i nic nie jest odczytywane.
Czy ceny wysyłane przez API wliczają się do limitu adresów URL w moim planie?
Nie. Liczą się tylko adresy, które 4pricing odczytuje ze strony — Twoje własne strony produktów i każda strona konkurenta osobno. Ceny wysłane przez POST /own-products/ lub /own-products/bulk nie liczą się ani jako adresy URL, ani jako kredyty.
Czy mogę ograniczyć, do czego ma dostęp klucz?
Nie, nie ma zakresów uprawnień: klucz widzi i może zmieniać całe Twoje konto 4pricing. Używaj osobnego klucza dla każdej integracji i unieważniaj każdy klucz, którego już nie używasz; unieważnienie działa natychmiast.
Co się stanie, jeśli wyślę więcej niż 60 zapytań na minutę?
Dostaniesz 429 z nagłówkiem Retry-After. Limit dotyczy konta i jest wspólny dla wszystkich Twoich kluczy. Poczekaj liczbę sekund podaną w Retry-After i spróbuj ponownie; przy wielu produktach używaj wywołań zbiorczych (do 2000 wierszy na zapytanie).
Jak sprawić, by rekomendowana cena trafiała do mojego sklepu automatycznie?
Powiąż grupę ze sklepem przez store_kind = webhook, ustaw adres i sekret webhooka i obsłuż zdarzenie price.apply: zmień cenę w sklepie, a następnie wywołaj POST /actions/{action_id}/confirm. Zatwierdzanie zmiany ceny pozostaje decyzją podejmowaną w panelu.
Czy istnieje SDK albo gotowa wtyczka?
Nie ma oficjalnego SDK ani wtyczki. API to zwykły REST z JSON-em, więc działa każdy język, który potrafi wykonywać zapytania HTTPS; specyfikację OpenAPI można załadować do narzędzi, które automatycznie generują klienta.