Skip to main content

API Conventions and Errors

The rules every endpoint follows. The API Reference documents each endpoint on its own.


Requests​

  • Base URL: https://YOUR_TENANT.quantaprice.com/api, with Authorization: Bearer <token> on every request; see Authentication.
  • JSON field names and parameters are snake_case. Two older parameters, asOf and afterSku, keep their camel case.
  • Identifiers are your business codes: SKU, pricelist code, currency code, tax area. Quantaprice has no ids of its own for you to keep.
  • Timestamps are RFC 3339 instants. Send them with an offset, Z or +01:00. A bare date, 2026-12-01, means midnight UTC. Responses are in UTC, with milliseconds when they are not zero.
  • Amounts are decimal JSON numbers. A numeric string, "12.50", is accepted too. Amounts are kept to four decimals, rounded half-up.

Time​

Most data has a history:

  • Reads take as_of (in a request body) or asOf (as a parameter) and answer as of that moment; the default is now.
  • Writes take a start date and can be scheduled ahead. A price written with a start date in the past takes effect now.
  • Deletes are soft. A deleted entity carries deleted_at, and its history stays readable with as_of.

See Historical Pricing and Scheduled Updates.


Writes​

Writes answer 202 Accepted. Single writes have an empty body. Batch writes say how many items were accepted, and list the ones that failed, each with its key and the reason:

{ "accepted": 1, "failed": [ { "sku": "KIT", "pricelist": "base-sek", "message": "Missing required fields" } ] }

A batch is not atomic: the valid items are written even when others fail. See Loading Data.


Paging​

Listings come in two kinds.

Numbered pages, for listings such as GET /article, GET /pricelist and GET /price: pass page (from 1) and size. The answer has items, page, size and total.

Cursors, for change logs and GET /price/skus-for-pricelist/{code}: pass the previous answer's next_cursor back until has_more is false. A cursor is opaque; send it back unchanged. See Change Events.


Errors​

A failed request answers with its HTTP status and this body:

{
"timestamp": "2026-10-13T09:50:39.798Z",
"status": 404,
"error": "Not Found",
"message": "Article 'SKU-NOPE' not found"
}

error is the status's name; message says what went wrong, for a person to read. Decide on the status code, not the message text.

StatusMeaningWhat to do
400The request is invalid: a missing or malformed field, an unknown currency, a rule that does not compileFix the request. message says what is wrong.
401No token, or one that is expired, malformed, or for another tenantFetch a new token and retry.
403The request is not allowed: a setting your plan does not include, or an action only a signed-in person can take in the consoleDo not retry.
404The entity does not exist at as_of
410A change-log position that the log no longer holdsResynchronise; see Change Events.
413The request is larger than one request may beSplit it; retrying will not help. See Rate Limits.
422Your plan's quota of articles, pricelists or prices is fullSee Plan and Limits.
429Your rate limit is used up for nowWait Retry-After seconds and retry.
500An error on our sideRetry later; contact support if it persists.
503The engine is starting, restoring a backup or changing over, or (for a write) its storage is nearly fullWait Retry-After seconds and retry.

Retry 429 and 503 with the same request. Article, pricelist and price writes replace what is there, so sending one twice does no harm.


Caching​

Do not cache prices. A price can change at any second: a scheduled change takes effect, a parent pricelist changes, an exchange rate is updated. A query is fast enough to make on every page view, and answers with the price in force now.

GET /price/{sku}/{pricelist}/{tax_area} answers with an ETag. Send it back in If-None-Match to get 304 Not Modified, without a body, while the price is unchanged.