REST API v1

Integracja przez REST API

Pobieraj produkty, stany magazynowe i swoje indywidualne ceny La Grana bezpośrednio do swojego systemu. Pełna automatyzacja z uwierzytelnianiem JWT.

🔐
JWT Auth
Bezpieczne uwierzytelnianie tokenami JWT z automatycznym odświeżaniem.
REST + JSON
Standardowe endpointy REST z odpowiedziami JSON. Prosto i przewidywalnie.
💰
Indywidualne ceny
Każde konto ma przypisane indywidualne ceny i rabat — widoczne bezpośrednio w odpowiedziach API.
Uwierzytelnianie
API La Grana używa JWT (JSON Web Tokens). Najpierw uzyskaj token, a następnie dołącz go do każdego żądania w nagłówku Authorization.
POST /api/token/ Uzyskaj token dostępu
PoleTypOpis
usernamewymaganeTwój login do systemu La Grana
passwordwymaganeTwoje hasło
import requests

resp = requests.post("https://b2b.lagrana.pl/api/token/",
    json={"username": "twoj_login", "password": "haslo"})
token = resp.json()["access"]
headers = {"Authorization": f"Bearer {token}"}
$res = file_get_contents('https://b2b.lagrana.pl/api/token/', false,
    stream_context_create(['http' => [
        'method'  => 'POST',
        'header'  => 'Content-Type: application/json',
        'content' => json_encode(['username'=>'twoj_login', 'password'=>'haslo'])
    ]])
);
$token = json_decode($res)->access;
$authHeader = "Authorization: Bearer $token";
curl -X POST https://b2b.lagrana.pl/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username": "twoj_login", "password": "haslo"}'
Odpowiedź
{
  "access":  "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "refresh": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
}

Token access ważny przez 60 minut. Odśwież przez POST /api/token/refresh/ podając token refresh.

Dołącz token do każdego żądania:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Base URL i format
Wszystkie żądania kieruj na poniższy adres. API zwraca i przyjmuje dane w formacie JSON (UTF-8). Wszystkie endpointy listowania są paginowane — używaj pola next do iterowania stron.
https://b2b.lagrana.pl/api/
Odpowiedź z paginacją
{
  "count": 1248,        // łączna liczba wyników
  "next":  "https://b2b.lagrana.pl/api/products/?page=2",
  "previous": null,
  "results": [ /* ... */ ]
}
Produkty
Pełny katalog produktów z opisami, zdjęciami, atrybutami i Twoimi indywidualnymi cenami. client_price to cena końcowa po rabacie; client_price_without_discount to cena bazowa przed rabatem.
GET /api/products/ Lista produktów (paginacja)
ParametrTypOpis
pageopcjonalnyNumer strony (domyślnie 1)
brandopcjonalnyFiltruj po nazwie marki
categoriesopcjonalnyFiltruj po ID kategorii
statusopcjonalnyFiltruj po ID statusu
activeopcjonalnytrue / false
searchopcjonalnySzukaj po SKU lub statusie

Pola odpowiedzi

id sku name brand ean active vat_rate for_who advantages stock_warehouse stock_supplier added_at updated_at client_price client_price_without_discount categories [ ] status { } prod_image [ ] description { } additional [ ]

pola cenowe   zagnieżdżone obiekty/tablice

url = "https://b2b.lagrana.pl/api/products/"
while url:
    data = requests.get(url, headers=headers).json()
    for p in data["results"]:
        print(p["sku"], p["client_price"], p["stock_warehouse"])
    url = data["next"]
$url = 'https://b2b.lagrana.pl/api/products/';
while ($url) {
    $data = json_decode(file_get_contents($url, false,
        stream_context_create(['http' => ['header' => $authHeader]])
    ));
    foreach ($data->results as $p) {
        echo $p->sku . ' ' . $p->client_price . "\n";
    }
    $url = $data->next;
}
curl "https://b2b.lagrana.pl/api/products/" \
  -H "Authorization: Bearer $TOKEN"

# kolejna strona
curl "https://b2b.lagrana.pl/api/products/?page=2" \
  -H "Authorization: Bearer $TOKEN"
Odpowiedź
{
  "count": 1248,
  "next": "https://b2b.lagrana.pl/api/products/?page=2",
  "previous": null,
  "results": [
    {
      "id": 42,
      "sku": "LG-12345",
      "name": "Nazwa produktu",
      "brand": "BrandName",
      "ean": "1234567890123",
      "active": true,
      "vat_rate": 23,
      "for_who": "Dla niej",
      "advantages": ["Zaletą jest...", "..."],
      "stock_warehouse": 24,
      "stock_supplier": 10,
      "added_at": "2025-01-15T08:00:00Z",
      "updated_at": "2026-03-01T12:00:00Z",
      "client_price": "84.15",               // cena po rabacie
      "client_price_without_discount": "99.00", // cena bazowa
      "categories": [
        { "id": 3, "name_pl": "Wibratory", "name_en": "Vibrators", "parent": null }
      ],
      "status": { "id": 1, "name": "Nowość" },
      "prod_image": [
        { "image": "https://cdn.lagrana.pl/img/lg-12345-1.jpg", "main_image": true, "hoover_image": false },
        { "image": "https://cdn.lagrana.pl/img/lg-12345-2.jpg", "main_image": false, "hoover_image": true }
      ],
      "description": {
        "id": 5, "product": 42,
        "lang_pl": "Opis po polsku...",
        "lang_en": "Description in English...",
        "lang_de": "Beschreibung auf Deutsch...",
        // oraz: lang_nl, lang_fr, lang_es, lang_it, lang_gr, lang_cz, lang_sk, lang_ua, lang_ru
      },
      "additional": [
        { "id": 101, "product": 42, "item": "Kolor", "value": "Czarny" },
        { "id": 102, "product": 42, "item": "Długość", "value": "18 cm" }
      ]
    }
  ]
}
GET /api/products/{id}/ Pojedynczy produkt po ID

Zwraca jeden obiekt produktu z tymi samymi polami co lista. Użyj numerycznego ID z pola id, lub użyj endpointu products-sku do wyszukiwania po SKU.

Produkt po SKU
Pobierz pojedynczy produkt bezpośrednio po kodzie SKU — przydatne przy synchronizacji z zewnętrznymi systemami, gdzie wewnętrzne ID nie jest znane.
GET /api/products-sku/{sku}/ Produkt po kodzie SKU
p = requests.get(
    "https://b2b.lagrana.pl/api/products-sku/LG-12345/",
    headers=headers
).json()
print(p["name"], p["client_price"], p["client_price_without_discount"])
$p = json_decode(file_get_contents(
    'https://b2b.lagrana.pl/api/products-sku/LG-12345/', false,
    stream_context_create(['http' => ['header' => $authHeader]])
));
echo $p->name . ' ' . $p->client_price;
curl "https://b2b.lagrana.pl/api/products-sku/LG-12345/" \
  -H "Authorization: Bearer $TOKEN"

Zwraca te same pola co GET /api/products/{id}/. Zwraca 404 jeśli SKU nie istnieje.

GET /api/products-sku/ Lista z filtrowaniem

Te same parametry filtrowania co /api/products/ (brand, categories, status, active, search). Endpoint szczegółowy używa SKU jako klucza zamiast numerycznego ID.

Kategorie
Pełna lista kategorii z wielojęzycznymi nazwami. Użyj pola parent do budowania drzewa kategorii.
GET /api/categories/ Lista kategorii
cats = requests.get("https://b2b.lagrana.pl/api/categories/",
    headers=headers).json()
for c in cats:
    print(c["id"], c["name_pl"])
$cats = json_decode(file_get_contents(
    'https://b2b.lagrana.pl/api/categories/', false,
    stream_context_create(['http' => ['header' => $authHeader]])
));
foreach ($cats as $c) echo $c->id . ' ' . $c->name_pl . "\n";
curl "https://b2b.lagrana.pl/api/categories/" \
  -H "Authorization: Bearer $TOKEN"
Odpowiedź
[
  {
    "id": 1,
    "name_pl": "Wibratory",
    "name_en": "Vibrators",
    "name_de": "Vibratoren",
    "name_nl": null,
    "name_fr": null,
    "name_es": null,
    "name_it": null,
    "name_gr": null,
    "name_cz": null,
    "name_sk": null,
    "name_ua": null,
    "name_ru": null,
    "parent": null   // ID kategorii nadrzędnej, null = najwyższy poziom
  }
]
Stany magazynowe
Lekki endpoint do sprawdzania dostępności bez pobierania pełnych danych produktu. Obsługuje te same filtry co /api/products/. Domyślny rozmiar strony to 1 000 — przy synchronizacji całego katalogu użyj ?all=1.
GET /api/stock/ Stany aktywnych produktów
ParametrTypOpis
allopcjonalnyUstaw na 1 aby wyłączyć paginację i pobrać wszystkie produkty w jednym zapytaniu. Zalecane przy pełnej synchronizacji katalogu.
page_sizeopcjonalnyLiczba wyników na stronę (domyślnie: 1000, maks: 10000). Ignorowany przy all=1.
brandopcjonalnyFiltruj po marce
categoriesopcjonalnyFiltruj po ID kategorii
searchopcjonalnySzukaj po SKU
# wszystkie stany w jednym zapytaniu
stock = requests.get("https://b2b.lagrana.pl/api/stock/?all=1",
    headers=headers).json()
for item in stock:
    print(item["sku"], item["stock_warehouse"], item["stock_supplier"])
$stock = json_decode(file_get_contents(
    'https://b2b.lagrana.pl/api/stock/?all=1', false,
    stream_context_create(['http' => ['header' => $authHeader]])
));
foreach ($stock as $item) {
    echo $item->sku . ' ' . $item->stock_warehouse . "\n";
}
# wszystkie stany naraz
curl "https://b2b.lagrana.pl/api/stock/?all=1" \
  -H "Authorization: Bearer $TOKEN"
Z paginacją (domyślnie)
GET /api/stock/
GET /api/stock/?page=2
GET /api/stock/?page_size=500

{
  "count":    4432,   // łączna liczba produktów
  "next":     "https://.../api/stock/?page=2",
  "previous": null,
  "results": [
    { "sku": "LG-12345", "stock_warehouse": 24, "stock_supplier": 10 },
    ...
  ]
}
Wszystkie produkty naraz (?all=1)
GET /api/stock/?all=1

[
  { "sku": "LG-12345", "stock_warehouse": 24, "stock_supplier": 10 },
  { "sku": "LG-12346", "stock_warehouse": 0,  "stock_supplier": 50 },
  ...
]  // płaska tablica, bez wrappera count/next/previous

stock_warehouse — ilość w magazynie La Grana
stock_supplier — ilość dostępna u dostawcy

Zamówienia
Twórz i listuj swoje zamówienia. Każde konto widzi tylko swoje zamówienia. Produkty możesz podawać po wewnętrznym ID lub SKU. Tworzenie zamówienia wymaga adresu dostawy i pliku listu przewozowego — użyj multipart/form-data (nie JSON).
GET /api/orders/ Lista Twoich zamówień
orders = requests.get("https://b2b.lagrana.pl/api/orders/",
    headers=headers).json()
for o in orders["results"]:
    print(o["id"], o["status"], o["total_gross"])
$orders = json_decode(file_get_contents(
    'https://b2b.lagrana.pl/api/orders/', false,
    stream_context_create(['http' => ['header' => $authHeader]])
));
foreach ($orders->results as $o) {
    echo $o->id . ' ' . $o->status . ' ' . $o->total_gross . "\n";
}
curl "https://b2b.lagrana.pl/api/orders/" \
  -H "Authorization: Bearer $TOKEN"
Odpowiedź
{
  "count": 3,
  "results": [
    {
      "id": 7,
      "external_id": "WC-1234",
      "status": "new",        // new | confirmed | rejected
      "total_gross": "267.00",
      "notes": "Uwagi do zamówienia",
      "first_name": "Jan",
      "last_name": "Kowalski",
      "phone": "500100200",
      "delivery_email": "jan@sklep.pl",
      "address": "ul. Przykładowa 1",
      "postal": "00-001",
      "city": "Warszawa",
      "country_code": "PL",
      "shipping_label": "https://b2b.lagrana.pl/media/shipping_labels/etykieta.pdf",
      "dropshipping": false,
      "created_at": "2026-03-01T12:00:00Z",
      "updated_at": "2026-03-01T12:05:00Z",
      "items_read": [
        {
          "product": 42, "sku": "LG-12345", "name": "Nazwa produktu",
          "qty": 3, "unit_price_gross": "89.00",
          "vat_rate": 23, "line_total_gross": "267.00"
        }
      ]
    }
  ]
}
POST /api/orders/ Utwórz zamówienie
Content-Type: multipart/form-data

Adres dostawy (wymagane)

PoleTypOpis
first_namewymaganeImię odbiorcy
last_namewymaganeNazwisko odbiorcy
addresswymaganeUlica i numer
postalwymaganeKod pocztowy
citywymaganeMiasto
shipping_labelopcjonalny Plik listu przewozowego (PDF/ZPL)
country_codeopcjonalny2-literowy kod kraju (domyślnie: PL)
phoneopcjonalnyTelefon odbiorcy
delivery_emailopcjonalnyE-mail odbiorcy

Pola zamówienia

PoleTypOpis
itemswymaganeCiąg JSON — tablica pozycji (patrz niżej)
dropshippingopcjonalnyZamówienie dropshipping — true / false (domyślnie: false). Przy false do ZO trafia pozycja TRANSPORT-PL, przy true — DROPSHIPPING.
external_idopcjonalnyTwój numer referencyjny
notesopcjonalnyUwagi do zamówienia

Każda pozycja w tablicy JSON items:

PoleTypOpis
productproduct lub skuWewnętrzne ID produktu
skuproduct lub skuSKU produktu (alternatywa dla product)
qtywymaganeIlość (min. 1)

Ceny, stawki VAT i wartości pozycji są naliczane serwerowo z cennika przypisanego do konta — nie przesyłaj danych cenowych.

Walidacja — co zwraca 400

Produkt nie istnieje
Jeśli którekolwiek SKU lub ID produktu nie istnieje w katalogu, całe zamówienie jest odrzucane.
Niewystarczający stan magazynowy
Jeśli zamówiona ilość przekracza stan magazynowy którejkolwiek pozycji, zamówienie jest odrzucane. Wszystkie błędy są zwracane jednocześnie.
{
  "non_field_errors": [
    "Produkt o SKU 'LG-99999' nie istnieje.",
    "Niewystarczający stan dla 'LG-12345': zamówiono 10, dostępne 3."
  ]
}
Wszystko albo nic
Zamówienie trafia do bazy dopiero po przejściu walidacji wszystkich pozycji — nie powstają zamówienia częściowe.
import requests, json

with open("etykieta.pdf", "rb") as label_file:
    order = requests.post(
        "https://b2b.lagrana.pl/api/orders/",
        headers=headers,
        data={
            "first_name":   "Jan",
            "last_name":    "Kowalski",
            "address":      "ul. Przykładowa 1",
            "postal":       "00-001",
            "city":         "Warszawa",
            "external_id":  "WC-5678",
            "notes":        "Pilne",
            "items": json.dumps([
                {"sku": "LG-12345", "qty": 3},
                {"sku": "LG-12346", "qty": 1},
            ]),
        },
        files={"shipping_label": ("etykieta.pdf", label_file, "application/pdf")},
    )
print(order.json())
$ch = curl_init('https://b2b.lagrana.pl/api/orders/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ["Authorization: Bearer $token"],
    CURLOPT_POSTFIELDS     => [
        'first_name'     => 'Jan',
        'last_name'      => 'Kowalski',
        'address'        => 'ul. Przykładowa 1',
        'postal'         => '00-001',
        'city'           => 'Warszawa',
        'external_id'    => 'WC-5678',
        'notes'          => 'Pilne',
        'items'          => json_encode([
            ['sku' => 'LG-12345', 'qty' => 3],
            ['sku' => 'LG-12346', 'qty' => 1],
        ]),
        'shipping_label' => new CURLFile('/path/to/etykieta.pdf', 'application/pdf', 'etykieta.pdf'),
    ],
]);
var_dump(json_decode(curl_exec($ch)));
curl_close($ch);
curl -X POST "https://b2b.lagrana.pl/api/orders/" \
  -H "Authorization: Bearer $TOKEN" \
  -F "first_name=Jan" \
  -F "last_name=Kowalski" \
  -F "address=ul. Przykładowa 1" \
  -F "postal=00-001" \
  -F "city=Warszawa" \
  -F "external_id=WC-5678" \
  -F "notes=Pilne" \
  -F 'items=[{"sku":"LG-12345","qty":3},{"sku":"LG-12346","qty":1}]' \
  -F "shipping_label=@/path/to/etykieta.pdf"
Reklamacje i zwroty
Zgłaszaj i śledź reklamacje (wady produktu) oraz zwroty pozycji z Twoich zamówień. Funkcja jest opt-in — jeśli Twoje konto otrzymuje 403 Forbidden, skontaktuj się z nami. Każde konto widzi tylko swoje zgłoszenia; status i rozstrzygnięcie ustawia zespół La Grana po rozpatrzeniu.
GET /api/complaints/ Lista Twoich reklamacji/zwrotów
ParametrTypOpis
statusopcjonalnyFiltruj po statusie — new, in_review, accepted, rejected, resolved
kindopcjonalnycomplaint lub return
orderopcjonalnyFiltruj po ID zamówienia
tickets = requests.get("https://b2b.lagrana.pl/api/complaints/?status=new",
    headers=headers).json()
for t in tickets["results"]:
    print(t["id"], t["kind"], t["status"])
$tickets = json_decode(file_get_contents(
    'https://b2b.lagrana.pl/api/complaints/?status=new', false,
    stream_context_create(['http' => ['header' => $authHeader]])
));
foreach ($tickets->results as $t) {
    echo $t->id . ' ' . $t->kind . ' ' . $t->status . "\n";
}
curl "https://b2b.lagrana.pl/api/complaints/?status=new" \
  -H "Authorization: Bearer $TOKEN"
Odpowiedź
{
  "count": 1,
  "results": [
    {
      "id": 12,
      "order": 7,
      "item": 18,
      "kind": "complaint",             // complaint | return
      "qty": 1,
      "reason": "Urządzenie nie ładuje się.",
      "return_tracking_number": "",
      "status": "in_review",           // new | in_review | accepted | rejected | resolved
      "resolution_type": "",            // replacement | refund | correction | repair
      "resolution_note": "",
      "photos": [
        { "id": 4, "photo": "https://b2b.lagrana.pl/media/complaint_photos/img1.jpg", "uploaded_at": "2026-07-20T09:12:00Z" }
      ],
      "created_at": "2026-07-20T09:12:00Z",
      "updated_at": "2026-07-21T11:00:00Z",
      "resolved_at": null
    }
  ]
}
POST /api/complaints/ Zgłoś reklamację lub zwrot
Content-Type: multipart/form-data
PoleTypOpis
orderwymaganeID jednego z Twoich zamówień
reasonwymaganeOpis zgłoszenia
kindopcjonalnycomplaint lub return (domyślnie: complaint)
itemopcjonalnyID konkretnej pozycji zamówienia, której dotyczy zgłoszenie
qtyopcjonalnyIlość, której dotyczy zgłoszenie (domyślnie: 1)
return_tracking_numberopcjonalnyNr przesyłki zwrotnej
photosopcjonalnyMaks. 6 plików zdjęć, powtórz pole dla każdego pliku

Pola status, resolution_type i resolution_note są tylko do odczytu — ustawia je zespół La Grana po rozpatrzeniu. Zasubskrybuj webhook complaint.status_changed, aby otrzymać powiadomienie o rozstrzygnięciu.

import requests

with open("usterka.jpg", "rb") as photo:
    resp = requests.post(
        "https://b2b.lagrana.pl/api/complaints/",
        headers=headers,
        data={
            "order": 7,
            "kind": "complaint",
            "item": 18,
            "qty": 1,
            "reason": "Urządzenie nie ładuje się.",
        },
        files={"photos": ("usterka.jpg", photo, "image/jpeg")},
    )
print(resp.json())
$ch = curl_init('https://b2b.lagrana.pl/api/complaints/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ["Authorization: Bearer $token"],
    CURLOPT_POSTFIELDS     => [
        'order'  => 7,
        'kind'   => 'complaint',
        'item'   => 18,
        'qty'    => 1,
        'reason' => 'Urządzenie nie ładuje się.',
        'photos' => new CURLFile('/path/to/usterka.jpg', 'image/jpeg', 'usterka.jpg'),
    ],
]);
var_dump(json_decode(curl_exec($ch)));
curl_close($ch);
curl -X POST "https://b2b.lagrana.pl/api/complaints/" \
  -H "Authorization: Bearer $TOKEN" \
  -F "order=7" \
  -F "kind=complaint" \
  -F "item=18" \
  -F "qty=1" \
  -F "reason=Urządzenie nie ładuje się." \
  -F "photos=@/path/to/usterka.jpg"
Wycofane produkty
Lista produktów wycofanych ze sprzedaży. Użyj jej, aby usunąć te produkty ze swojego sklepu. Każdy wpis zawiera SKU produktu oraz datę wycofania.
GET /api/discontinued-products/ Lista wycofanych produktów
ParametrTypOpis
skuopcjonalnyFiltruj po dokładnym SKU
searchopcjonalnySzukaj po SKU
discontinued = requests.get("https://b2b.lagrana.pl/api/discontinued-products/",
    headers=headers).json()
for item in discontinued["results"]:
    print(item["sku"], item["discontinued_at"])
$discontinued = json_decode(file_get_contents(
    'https://b2b.lagrana.pl/api/discontinued-products/', false,
    stream_context_create(['http' => ['header' => $authHeader]])
));
foreach ($discontinued->results as $item) {
    echo $item->sku . ' ' . $item->discontinued_at . "\n";
}
curl "https://b2b.lagrana.pl/api/discontinued-products/" \
  -H "Authorization: Bearer $TOKEN"
Odpowiedź
{
  "count":    2,
  "next":     null,
  "previous": null,
  "results": [
    { "id": 1, "sku": "LG-12345", "discontinued_at": "2026-06-11T10:32:00Z" },
    { "id": 2, "sku": "LG-12346", "discontinued_at": "2026-06-10T08:15:00Z" }
  ]
}
POST /api/discontinued-products/ Oznacz produkt jako wycofany

Tylko konta administratorów. Data discontinued_at jest ustawiana automatycznie na bieżącą datę i godzinę. Jeśli dane SKU jest już na liście, zwracany jest istniejący wpis bez zmian.

PoleTypOpis
skuwymaganySKU wycofanego produktu
resp = requests.post("https://b2b.lagrana.pl/api/discontinued-products/",
    headers=headers, json={"sku": "LG-12345"})
print(resp.json())
$ch = curl_init('https://b2b.lagrana.pl/api/discontinued-products/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_HTTPHEADER     => ["Authorization: Bearer $token", "Content-Type: application/json"],
    CURLOPT_POSTFIELDS     => json_encode(['sku' => 'LG-12345']),
]);
var_dump(json_decode(curl_exec($ch)));
curl_close($ch);
curl -X POST "https://b2b.lagrana.pl/api/discontinued-products/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"sku": "LG-12345"}'
Odpowiedź
{ "id": 3, "sku": "LG-12345", "discontinued_at": "2026-06-11T16:00:00Z" }
Webhooki
Otrzymuj powiadomienie w momencie zmiany ceny lub stanu magazynowego produktu — bez czekania na kolejną dzienną paczkę XML/CSV. Skontaktuj się z nami, aby skonfigurować subskrypcję pod Twój endpoint.
POST /api/webhooks/ Zarejestruj subskrypcję

Tylko konta administratorów — subskrypcje są konfigurowane przez La Grana na Twoją prośbę.

PoleTypOpis
event_typewymaganyJedno z: stock.changed, price.changed, product.updated, order.cancelled, complaint.status_changed
target_urlwymaganyTwój endpoint HTTPS, który odbierze POST
Payload wysyłany na Twój target_url
JSON
{
  "event_id":   "3f9c2e6a-1b2d-4e5f-8a9b-0c1d2e3f4a5b",
  "event_type": "price.changed",
  "sku":        "LG-12345",
  "changes": {
    "price_hurt_pln": { "old": "12.50", "new": "13.00" }
  },
  "timestamp": "2026-07-11T14:32:05.123456+00:00"
}

Każde żądanie ma podpis HMAC-SHA256, dzięki czemu możesz zweryfikować, że naprawdę pochodzi od nas:

NagłówekOpis
X-LaGrana-Signaturesha256=<hex-digest>
X-LaGrana-TimestampUnix seconds — odrzuć jeśli starsze niż 300s (ochrona przed replay)
X-LaGrana-Eventevent_type
X-LaGrana-Deliveryevent_id
Python
import hmac, hashlib

expected = hmac.new(
    SECRET.encode(),
    f"{timestamp}.".encode() + raw_body,
    hashlib.sha256,
).hexdigest()

is_valid = hmac.compare_digest(f"sha256={expected}", request.headers["X-LaGrana-Signature"])
Feed XML
Alternatywa dla API JSON — feed produktów w formacie XML, dostępny przez unikalny token (bez JWT). Przydatne dla platform obsługujących import XML (np. PrestaShop, IdoSell). Skontaktuj się z nami, aby otrzymać swój token.
GET /api/xml/{token}/ Feed XML produktów
XML
<?xml version="1.0" encoding="UTF-8"?>
<products>
  <product>
    <id>42</id>
    <sku>LG-12345</sku>
    <name>Nazwa produktu</name>
    <description>Opis po polsku...</description>
    <category>Wibratory</category>
    <brand>BrandName</brand>
    <price>84.15</price>   <!-- Twoja cena -->
    <stock>24</stock>
    <image>https://cdn.lagrana.pl/img/lg-12345-1.jpg</image>
    <active>true</active>
  </product>
</products>

Feed nie wymaga nagłówka uwierzytelniającego — token w URL stanowi dostęp. Ceny odzwierciedlają Twoje indywidualne stawki.

Kody błędów
API używa standardowych kodów HTTP. Odpowiedź błędu zawiera pole detail z opisem.
KodZnaczenie
200Sukces
201Zasób utworzony (np. nowe zamówienie)
400Błąd w żądaniu — sprawdź parametry lub wymagane pola
401Brak uwierzytelnienia lub wygasły token
403Brak uprawnień do zasobu
404Zasób nie istnieje
429Przekroczono limit zapytań (rate limiting)
Gotowy na integrację?

Zaloguj się i zacznij korzystać z API

Dostęp do API przyznajemy na wniosek — skontaktuj się z nami lub zaloguj, jeśli masz już konto.

Zaloguj się Zapytaj o dostęp