Authorization.| Pole | Typ | Opis |
|---|---|---|
| username | wymagane | Twój login do systemu La Grana |
| password | wymagane | Twoje 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"}'
{
"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...
next do iterowania stron.https://b2b.lagrana.pl/api/
{
"count": 1248, // łączna liczba wyników
"next": "https://b2b.lagrana.pl/api/products/?page=2",
"previous": null,
"results": [ /* ... */ ]
}
client_price to cena końcowa po rabacie; client_price_without_discount to cena bazowa przed rabatem.| Parametr | Typ | Opis |
|---|---|---|
| page | opcjonalny | Numer strony (domyślnie 1) |
| brand | opcjonalny | Filtruj po nazwie marki |
| categories | opcjonalny | Filtruj po ID kategorii |
| status | opcjonalny | Filtruj po ID statusu |
| active | opcjonalny | true / false |
| search | opcjonalny | Szukaj po SKU lub statusie |
Pola odpowiedzi
■ 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"
{
"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" }
]
}
]
}
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.
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.
Te same parametry filtrowania co /api/products/ (brand, categories, status, active, search). Endpoint szczegółowy używa SKU jako klucza zamiast numerycznego ID.
parent do budowania drzewa 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"
[
{
"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
}
]
/api/products/. Domyślny rozmiar strony to 1 000 — przy synchronizacji całego katalogu użyj ?all=1.| Parametr | Typ | Opis |
|---|---|---|
| all | opcjonalny | Ustaw na 1 aby wyłączyć paginację i pobrać wszystkie produkty w jednym zapytaniu. Zalecane przy pełnej synchronizacji katalogu. |
| page_size | opcjonalny | Liczba wyników na stronę (domyślnie: 1000, maks: 10000). Ignorowany przy all=1. |
| brand | opcjonalny | Filtruj po marce |
| categories | opcjonalny | Filtruj po ID kategorii |
| search | opcjonalny | Szukaj 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"
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 },
...
]
}
?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
multipart/form-data (nie JSON).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"
{
"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"
}
]
}
]
}
Adres dostawy (wymagane)
| Pole | Typ | Opis |
|---|---|---|
| first_name | wymagane | Imię odbiorcy |
| last_name | wymagane | Nazwisko odbiorcy |
| address | wymagane | Ulica i numer |
| postal | wymagane | Kod pocztowy |
| city | wymagane | Miasto |
| shipping_label | opcjonalny | Plik listu przewozowego (PDF/ZPL) |
| country_code | opcjonalny | 2-literowy kod kraju (domyślnie: PL) |
| phone | opcjonalny | Telefon odbiorcy |
| delivery_email | opcjonalny | E-mail odbiorcy |
Pola zamówienia
| Pole | Typ | Opis |
|---|---|---|
| items | wymagane | Ciąg JSON — tablica pozycji (patrz niżej) |
| dropshipping | opcjonalny | Zamówienie dropshipping — true / false (domyślnie: false). Przy false do ZO trafia pozycja TRANSPORT-PL, przy true — DROPSHIPPING. |
| external_id | opcjonalny | Twój numer referencyjny |
| notes | opcjonalny | Uwagi do zamówienia |
Każda pozycja w tablicy JSON items:
| Pole | Typ | Opis |
|---|---|---|
| product | product lub sku | Wewnętrzne ID produktu |
| sku | product lub sku | SKU produktu (alternatywa dla product) |
| qty | wymagane | Ilość (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
{
"non_field_errors": [
"Produkt o SKU 'LG-99999' nie istnieje.",
"Niewystarczający stan dla 'LG-12345': zamówiono 10, dostępne 3."
]
}
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"
<?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.
detail z opisem.| Kod | Znaczenie |
|---|---|
| 200 | Sukces |
| 201 | Zasób utworzony (np. nowe zamówienie) |
| 400 | Błąd w żądaniu — sprawdź parametry lub wymagane pola |
| 401 | Brak uwierzytelnienia lub wygasły token |
| 403 | Brak uprawnień do zasobu |
| 404 | Zasób nie istnieje |
| 429 | Przekroczono limit zapytań (rate limiting) |
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.