Querying Prices
This guide covers everything you need to make price queries against the Quantaprice engine: choosing between the GET and POST endpoints, constructing pricelist filters, handling currency and VAT, quantity breaks, historical lookups, and reading the response.
All requests require a bearer token obtained from the auth server. See Authentication for details.
Base URL: https://api.quantaprice.com
The two query shapes
Single-SKU GET — point lookups
GET /price/{sku}/{pricelist}
GET /price/{sku}/{pricelist}/{tax_area}
Use these for point lookups where you already know the exact SKU and pricelist code. The engine skips filter resolution and returns a single price record directly, making this the fastest path available.
# Price without VAT
curl -X GET "https://api.quantaprice.com/price/SKU-CHAIR-001/retail-sek" \
-H "Authorization: Bearer $TOKEN"
# Price with VAT for a specific tax area
curl -X GET "https://api.quantaprice.com/price/SKU-CHAIR-001/retail-sek/SE" \
-H "Authorization: Bearer $TOKEN"
When to use the GET endpoints:
- Server-side rendering of a single product detail page where the pricelist is already known
- Admin tooling and internal dashboards
- Audit scripts that walk a known SKU/pricelist matrix
When not to use the GET endpoints: any time you need to match a customer to the right pricelist, query multiple SKUs in one round-trip, or apply best-price selection across several pricelists. Use POST /price/query for all of those.
Batch POST — product pages and carts
POST /price/query accepts multiple SKUs and a pricelist filter, and returns one result per matched (SKU, pricelist) combination. This is the correct endpoint for storefront product listings, cart pricing, and any scenario where you need the engine to resolve which pricelists apply.
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001", "SKU-DESK-042", "SKU-LAMP-007"],
"pricelist_filter": {
"pricelists": ["customer-acme"]
},
"currency_code": "SEK",
"tax_area": "SE"
}'
The response is a JSON object with a results array ordered by SKU, then by pricelist. If the filter matches multiple pricelists for the same SKU, see Best-price selection below.
Building a pricelist filter
The pricelist_filter object controls which pricelists the engine considers. The three filter keys are pricelists, metadata, and currency_code. You can use any combination; the engine intersects all conditions.
By pricelist code
The most explicit option. Pass an array of pricelist codes:
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001"],
"pricelist_filter": {
"pricelists": ["customer-acme", "wholesale-tier-1"]
},
"currency_code": "SEK"
}'
A single code in the array bypasses best-price selection and returns exactly that pricelist's price. Useful when the customer's pricelist assignment is resolved upstream (for example, by your CRM or the Shopify adapter) before the query is made.
By metadata
Pricelists carry arbitrary key-value metadata you define in the dashboard. Query by metadata to resolve pricing by customer segment, region, or any other classification without hardcoding pricelist codes in your application:
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001", "SKU-DESK-042"],
"pricelist_filter": {
"metadata": {
"segment": "wholesale",
"region": "nordics"
}
},
"currency_code": "EUR"
}'
All metadata conditions must match (AND semantics). The engine returns prices from every pricelist that carries all the specified key-value pairs. If multiple pricelists match, best-price selection applies automatically.
By currency
Filter to pricelists denominated in a specific currency. Combine with metadata or pricelist codes to narrow further:
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001"],
"pricelist_filter": {
"metadata": {"segment": "retail"},
"currency_code": "EUR"
},
"currency_code": "EUR"
}'
Note the distinction: pricelist_filter.currency_code filters which pricelists to consider; the top-level currency_code sets the output currency of the response. They can differ — see Currency and VAT.
Currency and VAT
Requesting an output currency
Set the top-level currency_code to the currency you want prices returned in. If the matched pricelist is denominated in a different currency, the engine applies FX conversion using its internal exchange rates.
# Pricelist is in EUR, response requested in SEK
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001"],
"pricelist_filter": {
"pricelists": ["wholesale-eur"]
},
"currency_code": "SEK"
}'
Exchange rates are updated daily. For financial reconciliation, use as_of to pin the query to a specific date and ensure reproducible results.
VAT calculation
Include tax_area to receive VAT-inclusive prices alongside the NET price. The engine applies the VAT rate configured for that tax area.
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001"],
"pricelist_filter": {
"pricelists": ["retail-sek"]
},
"currency_code": "SEK",
"tax_area": "SE"
}'
Response fields when tax_area is provided:
| Field | Description |
|---|---|
sales_price_ex_vat | NET price (excluding VAT) in the requested currency |
sales_price_inc_vat | GROSS price (including VAT) in the requested currency |
vat_rate | Decimal VAT rate applied, e.g. 0.25 for 25% |
When tax_area is omitted, sales_price_inc_vat and vat_rate are null. sales_price_ex_vat is always returned.
Quantity breaks
Pass qty to activate quantity-break pricing. The engine selects the tier whose minimum quantity is less than or equal to qty and returns that tier's price.
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001"],
"pricelist_filter": {
"pricelists": ["wholesale-tier-1"]
},
"currency_code": "SEK",
"tax_area": "SE",
"qty": 25
}'
If qty is below all configured break thresholds, the engine returns the base price. If qty is omitted, base pricing applies.
Historical queries
Use as_of to query prices as they stood at a past point in time. Pass an ISO 8601 UTC timestamp:
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001"],
"pricelist_filter": {
"pricelists": ["customer-acme"]
},
"currency_code": "SEK",
"as_of": "2025-01-15T00:00:00Z"
}'
Use cases for as_of:
- Audit trails: Reproduce the exact price a customer was quoted on a given date.
- Dispute resolution: Confirm what price was active at the time of an order without relying on order snapshots.
- Debugging price changes: Identify exactly when a price changed by stepping through dates.
Use as_of together with count and took in the response to verify reproducible results.
Best-price selection
When pricelist_filter matches more than one pricelist for the same SKU, the engine does not return all candidates. Instead it:
- Normalises every candidate price to NET in the target currency (applying FX conversion as needed).
- Returns the single lowest-price candidate.
This is the intended behaviour for B2B scenarios where a customer qualifies for multiple discounts or price tiers and should always receive their best available price.
# Customer qualifies for both wholesale-tier-1 and promo-summer-2025.
# Engine returns whichever is cheaper.
curl -X POST "https://api.quantaprice.com/price/query" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"skus": ["SKU-CHAIR-001"],
"pricelist_filter": {
"pricelists": ["wholesale-tier-1", "promo-summer-2025"]
},
"currency_code": "SEK"
}'
To opt out of best-price selection, pass exactly one pricelist code. The engine returns that pricelist's price directly without comparison:
"pricelist_filter": {
"pricelists": ["wholesale-tier-1"]
}
Response fields
The response is a JSON object with a results array. Each element corresponds to one (SKU, pricelist) result returned by the engine.
{
"results": [
{
"sku": "SKU-CHAIR-001",
"is_bundle": false,
"sales_price_inc_vat": 389.00,
"sales_price_ex_vat": 311.20,
"vat_rate": 0.25,
"vat_amount": 77.80
}
],
"count": 1,
"took": 3
}
Top-level fields
| Field | Type | Description |
|---|---|---|
sku | string | The SKU as supplied in the request. |
is_bundle | boolean | true if the SKU is a bundle article. |
sales_price_ex_vat | number | Price excluding VAT in the requested currency. Always present. |
sales_price_inc_vat | number | null | Price including VAT. Present only when tax_area was supplied. |
vat_rate | number | null | Decimal VAT rate, e.g. 0.25. Present only when tax_area was supplied. |
vat_amount | number | null | VAT amount in currency. Present only when tax_area was supplied. |
See also
- VAT — how tax areas and VAT classes are configured.
- Bundles — how bundle prices appear in query responses.
- Rounding — rounding profiles and the
rounding_profilequery parameter. - OpenAPI Reference — full machine-readable spec.