Live — Production

Asgard API
Comprehensive Documentation

Products, your prices, marking, and orders in one place. A REST API ready for integration.

GET  /api/products-index/ GET  /api/client-products/ GET  /api/marking-data/ POST /api/orders/ 🔒 JWT Bearer
🔒  Log in to the panel Swagger Documentation 🌐  Polski
⚡

Getting Started

Every request to protected endpoints requires a valid JWT token in the header. Below is a three-step quick start.

1
Get a JWT token
Send POST /api/token/ with your username and password. You'll receive an access token (valid for 5 min) and a refresh token.
2
Add the header to every request
Authorization: Bearer <access_token>
3
Refresh the token before it expires
Use POST /api/token/refresh/ with the refresh field to get a new access token without logging in again.
👥
API access is granted by your account manager. A Swagger UI with a full overview of the endpoints is available at /docs/.
🔒

JWT Authorization

The API uses JWT (JSON Web Token). Pass the token in the Authorization header with every request.

POST /api/token/
POST /api/token/
Content-Type: application/json

{
  "username": "your_username",
  "password": "your_password"
}
Response 200 OK
{
  "access":  "eyJhbGci...",   // <-- use this one, expires after 5 min
  "refresh": "eyJhbGci..."    // <-- for refreshing, valid for 1 day
}
Header in every request
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
⏱
The access token expires after 5 minutes. Refresh it via POST /api/token/refresh/ with the body {"refresh": "..."}.
■

Products — Overview

The API provides several product endpoints optimized for different use cases: full data, your prices, stock levels, and updates.

GET/api/products-index/full data, lookup by index
GET/api/products-index/{index}/single product by index
GET/api/client-products/full data + catalogue price and your purchase price
GET/api/my-prices/your product prices
GET/api/promo-prices/current promotional prices
GET/api/promo-prices/{index}/product promotion by index
GET/api/products/list of active products (lookup by id)
GET/api/stock-info/index + stock level only
GET/api/last-update/updated in the last 45 min
✨
For daily synchronization we recommend /api/products-index/ — data is returned by product index, which simplifies mapping to your own systems.
📄

Full Product List

The /api/products-index/ endpoint returns active products with all related data nested in a single response.

Get all products
GET  /api/products-index/             // list of all
GET  /api/products-index/ABC-123/      // single product by index
Authorization: Bearer <token>

What a single product contains:

FieldTypeDescription
indexstringUnique product code (URL key)
quantityintegerCurrent stock level
activebooleanWhether the product is active
names[]arrayNames by language: name_pl, name_en, name_de, name_fr, name_cz
descriptions[]arrayMulti-language descriptions
prices[]arrayPrices: pln, eur, chf, czk
madeof[]arrayMaterial composition (multi-language)
image[]arrayProduct image URLs
additional[]arrayAdditional attributes (key–value)
future_delivery[]arrayPlanned deliveries: date + quantity
marking_data[]arrayMarking data (nested: places, options)
category / subcategoryintegerCategory and subcategory ID
new_product, promotion, salebooleanProduct status flags
💰

Products with Your Prices

The /api/client-products/ endpoint returns the same data as /api/products-index/ — all fields, filters and search — extended with the catalogue price and your purchase price. Handy when building an offer or a cart and you need product data together with the price in a single request.

GET/api/client-products/products with prices (100 per page)
GET/api/client-products/{index}/single product by index
?qty=100 ?search=AP8000 ?index=AP800001-01 ?category=12 ?new_product=true ?page=2
Product with prices for 100 pcs
GET  /api/client-products/AP800001-01/?qty=100
Authorization: Bearer <token>

// Response:
{
  "id":                 142,
  "index":              "AP800001-01",
  "quantity":           4520,
  ... // all fields as in products-index
  "currency":           "PLN",
  "catalogue_price":    "10.00",   // net catalogue price
  "purchase_price":     "7.90",   // your net purchase price
  "price_source":       "individual",
  "no_discount":        true,
  "discount_percent": "10.00"
}

Price fields (in addition to products-index):

FieldTypeDescription
currencystringYour account currency (PLN / EUR / CHF / CZK).
catalogue_pricedecimal | nullNet catalogue unit price, before discount.
purchase_pricedecimal | nullYour net unit purchase price — what you pay in an order.
price_sourcestringbase / discount / individual / promo — where the purchase price comes from.
no_discountbooleantrue — individual or promotional price, no discount applied.
discount_percentdecimalYour discount for this product (0 when the product is not discounted).
↓
The purchase price is the lowest of: the catalogue price after your discount, your individual price and the promotional price. Tiered prices depend on quantity — pass ?qty= equal to the quantity you order. Details and price tiers: Your prices.
📈

Stock Levels

A lightweight endpoint returning only the index and quantity — ideal for frequent stock synchronization.

GET /api/stock-info/
GET  /api/stock-info/
Authorization: Bearer <token>

// Response:
{
  "count":    1250,
  "next":     "/api/stock-info/?page=2",
  "previous": null,
  "results": [
    { "index": "ABC-123", "quantity": 450 },
    { "index": "XYZ-007", "quantity": 0 },
    ...
  ]
}
↻
The /api/last-update/ endpoint returns products changed in the last 45 minutes — use it for incremental synchronization instead of fetching the entire catalogue.
⍂

Filtering and Search

All product endpoints support filtering via GET parameters and pagination (100 results per page).

Parameters for /api/products-index/:

?search=ABC ?index=ABC-123 ?category=5 ?new_product=true ?discount_prices=true ?promotion=true ?sale=true ?page=2
Filtering examples
// Products from category ID=5 with a discount
GET  /api/products-index/?category=5&discount_prices=true

// New arrivals - page 2
GET  /api/products-index/?new_product=true&page=2

// Search by partial index
GET  /api/products-index/?search=ABC

// Categories and subcategories
GET  /api/categories/
GET  /api/subcategories/
{

Product Response Structure

Full JSON structure of a single product from /api/products-index/{index}/.

Example response
{
  "id":              142,
  "index":         "ABC-123",
  "quantity":      450,
  "active":        true,
  "new_product":   false,
  "promotion":     false,
  "sale":          false,
  "discount_prices": true,
  "category":      5,
  "subcategory":   12,

  "names": [
    { "id": 1, "language": "name_pl", "title": "Kubek termiczny 350ml" },
    { "id": 2, "language": "name_en", "title": "Thermal mug 350ml" }
  ],

  "prices": [
    { "pln": "24.90", "eur": "5.80", "chf": "5.50", "czk": "138.00" }
  ],

  "image": [
    { "url": "https://cdn.example.com/products/abc-123-1.jpg" },
    { "url": "https://cdn.example.com/products/abc-123-2.jpg" }
  ],

  "future_delivery": [
    { "quantity": 200, "date": "2026-03-15" }
  ],

  "additional": [
    { "item": "dimensions", "value": "8.5 x 8.5 x 12 cm" },
    { "item": "weight",    "value": "210g" }
  ],

  "marking_data": [ /* see the Marking section */ ],

  // ... names, descriptions, madeof, marking (text)
}
☍

Marking — Data Architecture

Marking data has a hierarchical structure. Each product can have multiple marking places, and each place can have several options with different prices.

Product
Product
→
Marking Data
MarkingData
→
Place
MarkingPlace
→
Option / Method
MarkingOption
→
Price Tiers
PriceCode

One product → multiple places (e.g. “Front”, “Back”) → multiple options (e.g. “Screen printing”, “Engraving”) → the price depends on the ordered quantity.

Key concepts:

ObjectDescriptionKey Fields
MarkingDataThe main marking node for a product. Contains the default method.product, default_marking_code
MarkingPlaceA place on the product where a marking can be applied.code, name_pl
MarkingOptionA specific marking technique at a given place.marking_code, option_code, price_code, max_colors
MarkingVariantA variant of the option (e.g. marking area size).variant_code, variant_label
MarkingPriceCodePrice key — links the option to price tiers.code
PriceCodePrice tier for a given ordered quantity.from_qty, to_qty, price_pln
AdditionalServiceAdditional marking services (e.g. setup, artwork shipping).service_id, service_price_pln
☷

Marking Endpoints

Two access modes: nested (full hierarchy in a single request) or flat (solo — each object separately, lighter responses).

Nested mode:

GET/api/marking-data/full hierarchy in a single request
GET/api/marking-data/?product=IDmarking for a specific product
GET/api/marking-default/default method per product (search by product__index)

Flat mode (solo — for detailed queries):

GET/api/marking-solodata/MarkingData, search: ?search=<index>
GET/api/marking-soloplace/MarkingPlace, filter: ?marking_data=ID
GET/api/marking-solooption/MarkingOption, filter: ?marking_place=ID
GET/api/marking-additional/AdditionalService, filter: ?marking_data=ID
GET/api/marking-name/dictionary of marking technique labels
Get marking for product ABC-123 (nested)
// Step 1 — find the product ID
GET  /api/products-index/ABC-123/
// → the response contains "id": 142 and nested "marking_data"

// Step 2 — or directly via marking-data
GET  /api/marking-data/?product=142

// Response (nested):
{
  "id":                    18,
  "product":              142,
  "default_marking_code": "TS-F1",
  "marking_place": [
    {
      "id":      55,
      "code":   "F1",
      "name_pl": "Przód",
      "marking_option": [
        {
          "id":            201,
          "marking_code":  "TS-F1",
          "option_code":   "TS",          // technique code
          "price_code":    7,           // ID → /api/marking-price/
          "max_colors":    4,
          "realisation_time_id": 3,
          "marking_variant": []
        }
      ]
    }
  ],
  "additional_service": [
    { "service_id": "SETUP", "service_price_pln": "35.00" }
  ]
}
💳

Marking Price Tiers

The price per marked unit depends on the ordered quantity. Fetch the tiers via the price_code from the marking option.

GET/api/marking-price/price codes with tiers (nested)
GET/api/marking-tierprice/?marking_option=IDtiers for a specific option
Price tiers for code “P3”
GET  /api/marking-price/?code=P3

// Response:
{
  "id":   7,
  "code": "P3",
  "main_marking_price": [
    { "from_qty": 25,  "to_qty": 49,  "price_pln": "2.80" },
    { "from_qty": 50,  "to_qty": 99,  "price_pln": "2.20" },
    { "from_qty": 100, "to_qty": 249, "price_pln": "1.60" },
    { "from_qty": 250, "to_qty": 499, "price_pln": "1.10" }
  ]
}
▶

Typical Marking Integration Flow

How to build a marking configurator step by step.

1
Fetch the product
GET /api/products-index/ABC-123/ — the response includes nested marking_data with places and options.
2
Choose a marking place and option
From marking_place[] choose a place (e.g. “Front”), from marking_option[] choose a technique (e.g. “Screen printing”). Note the price_code and marking_code.
3
Fetch prices for the chosen option
GET /api/marking-price/?code=P3 (or /api/marking-tierprice/?marking_option=ID) — display the price tiers for the customer's quantity.
4
Fetch additional services
GET /api/marking-additional/?marking_data=ID — add setup costs, artwork shipping, etc.
5
Place the order
POST /api/orders/ with items — including marking (marking_place_code, marking_option_code, marking_colors). See Order with Marking.
✓
Orders via /api/orders/ support marking — you pass the place and technique codes in the order items and upload design files separately.
💰

Your prices

The /api/my-prices/ endpoint returns the prices that apply to your account — in your currency and for the given quantity. These are the same prices applied when you place an order.

↓
The lowest price always applies out of: the catalogue price after your discount, your individual price and the promotional price. No discount is applied to individual and promotional prices — they are final prices.
sourceMeaning
baseCatalogue price (product without discount or 0% discount).
discountCatalogue price after your percentage discount.
individualYour individual price — final price, no discount.
promoPromotional price — final price, no discount.
GET/api/my-prices/product prices (100 per page)
GET/api/my-prices/{index}/price of one product + quantity tiers
GET/api/client-products/{index}/products as in products-index + your prices
📦
Full product data together with prices is returned by /api/client-products/ — see Products with Your Prices.
?qty=100 ?index=AP800001-01,AP800002-10 ?special=1 ?page=2
Product price for 100 pcs
GET  /api/my-prices/AP800001-01/?qty=100
Authorization: Bearer <token>

// Response:
{
  "index":               "AP800001-01",
  "currency":            "PLN",
  "qty":                 100,
  "price":               "7.90",   // applicable net unit price
  "source":              "individual",   // base | discount | individual | promo
  "no_discount":         true,   // final price
  "base_price":          "10.00",
  "discount_percent":    "10.00",
  "discounted_price":    "9.00",
  "individual_price":    "7.90",
  "promo_price":         null,
  "individual_tiers": [
    { "from_qty": 1,   "to_qty": 99,   "price": "8.40" },
    { "from_qty": 100, "to_qty": null, "price": "7.90" }
  ],
  "promo_tiers": []
}

Response fields:

FieldTypeDescription
pricedecimal | nullNet unit price you will pay. null — no price for this product.
sourcestringWhere the price comes from (table above).
no_discountbooleantrue — individual or promotional price, no discount applied.
base_price / discounted_pricedecimal | nullCatalogue price before and after your discount.
individual_price / promo_pricedecimal | nullIndividual / promotional price for the given quantity, if any.
individual_tiers[] / promo_tiers[]arrayQuantity tiers (only /api/my-prices/{index}/).
✓
?special=1 returns only products with an individual price or an ongoing promotion. Tiered prices depend on quantity — pass ?qty= equal to the quantity you intend to order.
🏷

Promotional prices

The /api/promo-prices/ endpoint returns promotional product prices. Promotions are addressed by product index — just like /api/products-index/. Prices are tiered: each tier defines a quantity range and a unit price in PLN/EUR/CHF/CZK.

⚠
No customer discount applies to a promotional price. Every response contains "no_discount": true — do not subtract your percentage discount from this price.
GET/api/promo-prices/list of current promotions
GET/api/promo-prices/{index}/single product promotion by index
GET/api/promo-prices/{index}/price/unit price for a given quantity
Get promotional prices
GET  /api/promo-prices/                  // all current promotions
GET  /api/promo-prices/ABC-123/          // one promotion by index
GET  /api/promo-prices/?index=ABC-123,XYZ-007
Authorization: Bearer <token>

// Response:
{
  "index":       "ABC-123",
  "active":      true,
  "date_from":   "2026-10-01",
  "date_to":     "2026-12-31",
  "is_current":  true,
  "no_discount": true,          // no discount on this price
  "tiers": [
    { "from_qty": 1,   "to_qty": 99,   "qty_label": "1–99",    "pln": "10.00", "eur": "2.40", "chf": null, "czk": null },
    { "from_qty": 100, "to_qty": null, "qty_label": "100+",    "pln": "8.50",  "eur": "2.05", "chf": null, "czk": null }
  ],
  "updated_at":  "2026-10-01T10:15:00+02:00"
}

Promotion fields:

FieldTypeDescription
indexstringProduct index (URL key)
activebooleanWhether the promotion is enabled
date_fromdate | nullStart date; null = effective immediately
date_todate | nullEnd date (inclusive); null = no end date
is_currentbooleanWhether the promotion is valid today
no_discountbooleanAlways true — no discount on promotional prices
tiers[]arrayPrice tiers, sorted ascending by from_qty
tiers[].from_qty / to_qtyinteger | nullQuantity range; to_qty: null = “and more”
tiers[].qty_labelstringReadable range label, e.g. "1–99", "100+"
tiers[].pln / eur / chf / czkdecimal | nullUnit price in currency; null = no price in this currency
updated_atdatetimeLast modification date
?index=ABC-123,XYZ-007 ?search=ABC ?page=2
ℹ
The list contains only promotions valid today (active and within their dates). An expired or future promotion returns 404. A single price “from 1 pc” is one tier 1+. Promotions are automatically included in your purchase price — see Your prices.
💳

Promotional price for quantity

Instead of looking up the tier yourself, you can ask for the unit price for a specific quantity and currency.

?qty=150 ?currency=PLN | EUR | CHF | CZK
Promotional price for 150 pcs
GET  /api/promo-prices/ABC-123/price/?qty=150&currency=PLN

// Response:
{
  "index":       "ABC-123",
  "qty":         150,
  "currency":    "PLN",
  "unit_price":  "8.50",
  "total":       "1275.00",
  "tier":        "100+",
  "no_discount": true
}
✓
If there is no promotional price for the given quantity or currency, the endpoint returns 404 — the regular product price (with customer discount) applies.
👤

Profile & Permissions

Before placing your first order, check your profile. Key fields are set by your account manager — without them the order will be blocked.

GET/api/me/JWT
Response
{
  "username":       "company_abc",
  "email":          "office@company.com",
  "can_order":        true,           // false = POST /api/orders/ will return 403
  "discount_percent": 10.00,          // % discount on products with discount_prices=true
  "currency":         "PLN",          // currency used in orders
  "payment_method":   "banktransfer", // payment method used in orders
  "language":         "pl"
}
FieldDescriptionSet By
can_orderWhen false — the API rejects POST /api/orders/ with a 403 error.Account manager
discount_percentPercentage discount applied automatically to products with discount_prices=true. Does not apply to individual and promotional prices. 0 = no discount.Account manager
currencyCurrency used for all orders on this account. Determines which price column is used (pln / eur / chf / czk).Account manager
payment_methodPayment method for orders. Defaults to banktransfer.Account manager
⚠
Profile fields are set by your account manager. Clients cannot change them on their own — contact your account manager if a change is needed.
+

Create an Order

Just provide a list of products. Currency and payment method are automatically retrieved from the logged-in user's profile. The order is passed on for processing immediately.

GET/api/me/check your profile and permissions
POST/api/orders/JWT
👤
Before placing an order, check your profile via GET /api/me/. The following must be set:
• can_order = true — permission to place orders
• currency and payment_method — retrieved from the profile, no need to provide them in the order
• discount_percent — discount on products with discount_prices=true; final prices are calculated automatically
💰
The price of each item is set automatically when the order is placed — the lowest of: the catalogue price after your discount, your individual price and the promotional price applies. You can check it beforehand in /api/client-products/ or /api/my-prices/ (for the same quantity). The applied price is returned in items[].unit_price.
Request — minimal (items only)
POST /api/orders/
Authorization: Bearer <access_token>
Content-Type:  application/json

{
  // Your own order number (optional)
  "client_reference": "MOJ-NR-2024/001",

  "notes": "Please issue a VAT invoice",  // optional

  // List of products — at least one item
  "items": [
    { "product_index": "ABC-123", "quantity": 50 },
    { "product_index": "XYZ-007", "quantity": 10 }
  ]
}

// Currency and payment method come from the user's profile — no need to provide them.
201 Created
{
  "id":                42,
  "client_reference": "MOJ-NR-2024/001",
  // ... orderer and address fields ...

  "items": [
    { "id": 99, "product_index": "ABC-123", "quantity": 50, "unit_price": "8.90", "price_source": "discount", "marking_place_code": "", "marking_option_code": "", "design_files": [] },
    { "id": 100, "product_index": "XYZ-007", "quantity": 10, "unit_price": "4.20", "price_source": "individual", "marking_place_code": "", "marking_option_code": "", "design_files": [] }
  ],
  "attachments":      [],

  "created_at":       "2026-06-25T10:35:00.123Z",

  // Processing status (filled in automatically)
  "status":           "processing",    // new | processing | confirmed | rejected
  "erp_order_number": "A/2026/0000042",  // order number in our system
  "erp_error":        ""                  // empty = success
}
400 Bad Request
{
  "items": [
    {
      "product_index": ["Product with index 'XYZ' does not exist or is inactive."]
    }
  ],
  "billing_city": ["This field is required."]
}
401 Unauthorized
{ "detail": "Authentication credentials were not provided." }
☷

Order Fields

Request fields (input):

FieldTypeDescription
items[]arrayList of order items. At least one required.required
items[].product_indexstringProduct index. Must exist and be active.required
items[].quantityintegerQuantity ≥ 1.required
items[].marking_place_codestringMarking place code, e.g. M02. Must exist for the product.optional
items[].marking_option_codestringMarking technique code, e.g. C1, S1.optional
items[].marking_colorsintegerNumber of colors. Required when max_colors > 0.conditional
items[].marking_infostringMarking description / instructions.optional
client_referencestringYour own order number — it will appear on documents.optional
notesstringOrder notes.optional

Fields retrieved automatically from the user's profile:

FieldDescription
currencyOrder currency: PLN / EUR / CHF / CZK. Set by your account manager in the profile.
payment_methodPayment method: banktransfer / prepayment / cod. Defaults to banktransfer.
discount_percentProfile discount applied automatically to products with discount_prices=true. Does not apply to individual and promotional prices.

Response fields (read-only):

FieldDescription
statusnew — received  |  processing — in progress  |  confirmed — confirmed  |  rejected — rejected
erp_order_numberOrder number assigned in our system, e.g. A/2026/0000042.
erp_errorError message when accepting the order. Empty = success.
items[].unit_priceNet unit price applied in the order (the lowest applicable).
items[].price_sourcePrice source: base / discount / individual / promo.
attachments[]General order attachments.
📌
Available product codes: GET /api/stock-info/. Profile and permissions: GET /api/me/.
☍

Order with Marking

Each order item can include marking. You provide the place and technique code from the product data returned by the API. The system verifies that such marking exists for the product before accepting the order.

🔍
You retrieve marking place and option codes from GET /api/marking-data/?product={id} or from the nested marking_data[] in the product response. Look for the code (place) and option_code (technique) fields.

Item fields with marking:

FieldTypeDescription
marking_place_codestringMarking place code, e.g. M02. Must exist for the product.optional
marking_option_codestringMarking technique code, e.g. C1, S1, F1. Must exist at the given place.optional
marking_colorsintegerNumber of colors. Required when the technique has max_colors > 0.conditional
marking_infostringMarking description / instructions for the designer.optional
design_files[]arrayList of design files for this item (read-only). Files are uploaded via a separate request.read-only
Order with two items — one with marking
POST /api/orders/
Authorization: Bearer <token>
Content-Type:  application/json

{
  // ... orderer and address fields ...

  "items": [
    {
      "product_index":      "20820-14",
      "quantity":           100,
      "marking_place_code": "M02",        // Back
      "marking_option_code":"C1",         // DTF 60x50mm
      "marking_info":       "Company logo, design attached"
    },
    {
      "product_index":      "20820-14",
      "quantity":           50,
      "marking_place_code": "M02",
      "marking_option_code":"S1",         // Screen printing 300x250mm
      "marking_colors":     2,            // max_colors=3
      "marking_info":       "2-color PMS logo"
    }
  ]
}

Uploading a design file for an item:

POST/api/orders/{id}/items/{item_id}/designJWT
cURL — upload design file
# Step 1 — place the order, note the order id and item id
# In the response items[0].id = 99 (item id)

# Step 2 — send the design file
curl -X POST https://developers.bluecollection.eu/api/orders/42/items/99/design \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@/path/to/logo.pdf"

// Response 201:
{
  "id":          7,
  "file":        "/media/order_item_designs/42/logo.pdf",
  "uploaded_at": "2026-06-25T11:30:00.000Z"
}
📎

Attachments

You can attach files (PDF, PNG, XLSX, etc.) to every order. Attachments are passed on for processing together with the order.

POST/api/orders/{id}/attachments/JWT
Add an attachment (multipart/form-data)
POST /api/orders/42/attachments/
Authorization: Bearer <token>
Content-Type:  multipart/form-data

// Form field: file

// cURL:
curl -X POST https://developers.bluecollection.eu/api/orders/42/attachments/ \
  -H "Authorization: Bearer $TOKEN" \
  -F "file=@/path/to/template.pdf"

// Response 201:
{
  "id":          1,
  "file":        "/media/order_attachments/2026/06/wzor.pdf",
  "uploaded_at": "2026-06-25T11:00:00.000Z"
}
⚠
Add attachments right after placing the order. If you add a file when the order is already being processed, let your account manager know.
↻

Order Status

Every order is automatically passed on for processing right after it's created. The status field shows what stage the order is at.

new
New

Order received, waiting to be passed on for processing.

processing
In progress

The order is being processed. The erp_order_number field contains its number.

confirmed
Confirmed

Order confirmed.

rejected
Rejected

Order rejected — see erp_error or contact your account manager.

☰

Order List

Fetch and search placed orders. The status filter lets you track their progress.

GET/api/orders/JWT
GET/api/orders/{id}/details of a single order
?status=new ?status=processing ?currency=EUR ?payment_method=cod ?search=Kowalski ?search=ABC-123 ?page=2
Orders awaiting processing
GET  /api/orders/?status=new
Authorization: Bearer <token>
⚠

Response Codes

200
OK

GET request completed successfully.

201
Created

Resource (order) created successfully.

400
Bad Request

Validation error — check the response body.

401
Unauthorized

Missing or invalid JWT token.

403
Forbidden

No permission to access the resource.

404
Not Found

Resource does not exist (e.g. incorrect index).

405
Method Not Allowed

Method not available for the endpoint (e.g. writing to a read-only endpoint).

429
Too Many Requests

Rate limit exceeded: 600 requests/min when logged in, 60/min anonymous. Wait and retry.

⌀

Code Examples

cURL — token + product data
# 1. Get token
TOKEN=$(curl -s -X POST https://developers.bluecollection.eu/api/token/ \
  -H "Content-Type: application/json" \
  -d '{"username":"username","password":"password"}' \
  | python3 -c "import sys,json; print(json.load(sys.stdin)['access'])")

# 2. List of products (paginated)
curl https://developers.bluecollection.eu/api/products/ \
  -H "Authorization: Bearer $TOKEN"

# 3. Product details by index
curl https://developers.bluecollection.eu/api/products-index/ABC-123/ \
  -H "Authorization: Bearer $TOKEN"

# 4. Stock levels
curl https://developers.bluecollection.eu/api/stock-info/ \
  -H "Authorization: Bearer $TOKEN"

# 5. Product with your prices (100 pcs)
curl "https://developers.bluecollection.eu/api/client-products/ABC-123/?qty=100" \
  -H "Authorization: Bearer $TOKEN"
Python (requests)
import requests

BASE = "https://developers.bluecollection.eu"

# 1. Token
token = requests.post(f"{BASE}/api/token/", json={
    "username": "username", "password": "password"
}).json()["access"]
headers = {"Authorization": f"Bearer {token}"}

# 2. List of products (first page)
products = requests.get(f"{BASE}/api/products/", headers=headers).json()
print(f"Total products: {products['count']}")

# 3. Product details by index
product = requests.get(f"{BASE}/api/products-index/ABC-123/", headers=headers).json()
print(f"Stock: {product['quantity']} pcs.")

# 4. Product marking
marking = requests.get(f"{BASE}/api/marking-data/?product={product['id']}", headers=headers).json()
print(marking)

# 5. Product with your prices (100 pcs)
priced = requests.get(f"{BASE}/api/client-products/ABC-123/", params={"qty": 100}, headers=headers).json()
print(f"Catalogue price: {priced['catalogue_price']}, Your price: {priced['purchase_price']} {priced['currency']}")
JavaScript (fetch)
const BASE = "https://developers.bluecollection.eu";
const post = (url, body, tok) => fetch(BASE + url, {
  method: "POST", headers: { "Content-Type": "application/json",
    ...(tok && { "Authorization": `Bearer ${tok}` }) },
  body: JSON.stringify(body) }).then(r => r.json());

// 1. Token
const { access } = await post("/api/token/", { username: "username", password: "password" });

// 2. List of products
const get = (url) => fetch(BASE + url,
  { headers: { "Authorization": `Bearer ${access}` } }).then(r => r.json());

const products = await get("/api/products/");
console.log(`Total products: ${products.count}`);

// 3. Product details by index
const product = await get("/api/products-index/ABC-123/");
console.log(`Stock: ${product.quantity} pcs.`);

// 4. Product marking
const marking = await get(`/api/marking-data/?product=${product.id}`);
console.log(marking);

// 5. Product with your prices (100 pcs)
const priced = await get("/api/client-products/ABC-123/?qty=100");
console.log(`Catalogue price: ${priced.catalogue_price}, Your price: ${priced.purchase_price} ${priced.currency}`);
PHP (cURL)
<?php
$base = "https://developers.bluecollection.eu";
function apiPost($url, $data, $token = null) {
    $ch = curl_init($url);
    $headers = ["Content-Type: application/json"];
    if ($token) $headers[] = "Authorization: Bearer $token";
    curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true, CURLOPT_HTTPHEADER => $headers,
        CURLOPT_POSTFIELDS => json_encode($data)]);
    return json_decode(curl_exec($ch));
}

// 1. Token
$auth  = apiPost("$base/api/token/", ["username"=>"username","password"=>"password"]);
$token = $auth->access;

// 2. List of products
$ch = curl_init("$base/api/products/");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]);
$products = json_decode(curl_exec($ch));
echo "Total products: {$products->count}\n";

// 3. Product details by index
$ch = curl_init("$base/api/products-index/ABC-123/");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]);
$product = json_decode(curl_exec($ch));
echo "Stock: {$product->quantity} pcs.\n";
print_r($product);

// 5. Product with your prices (100 pcs)
$ch = curl_init("$base/api/client-products/ABC-123/?qty=100");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => ["Authorization: Bearer $token"]]);
$priced = json_decode(curl_exec($ch));
echo "Your price: {$priced->purchase_price} {$priced->currency}\n";
🔗
API server address: developers.bluecollection.eu. Obtain login credentials from your account manager.