Skip to content
Pricepulse

API reference

Send the changes. Ask for the number.

Every request is authenticated with a bearer API key. Prices are decimal strings, dates are ISO 8601, and the anchor endpoint is free because it belongs on a product page.

Authentication

Create keys in the dashboard. A key is shown once and stored only as a SHA-256 digest, so a lost key is replaced rather than recovered.

Authorization: Bearer pp_live_XXXXXXXXXXXXXXXXXXXXXXXX

Endpoints

  • POST/api/v1/prices

    Record one or more price changes.

    Accepts a single object or a batch of up to 1,000. An unchanged price writes nothing, so it is safe to send on every product save.

  • GET/api/v1/anchor/{externalId}

    The reference price and a ready-made badge.

    Free and unmetered — it is on your product page. Add ?locale=de|en|fr for the badge text.

  • POST/api/webhooks/shopify/{slug}

    Shopify product webhook.

    Verified over the raw body with your shop's signing secret. Redelivery is idempotent.

# Record a price change
curl -X POST https://pricepulse.altixcode.com/api/v1/prices \
  -H "Authorization: Bearer pp_live_..." \
  -H "Content-Type: application/json" \
  -d '{"externalId":"variant_9001","price":"70.00","title":"Winter coat"}'

# Ask for the badge
curl https://pricepulse.altixcode.com/api/v1/anchor/variant_9001?locale=de \
  -H "Authorization: Bearer pp_live_..."

The rules, precisely

RuleHow it is implemented
The windowThe 30 days before the reduction took effect — not the 30 days before now. The current price is excluded from its own reference, or nothing would ever be a reduction.
The far edgeInclusive. A price still in force when the window opened counts, even if it was set months earlier. This is the case a naive 'filter the last 30 days of rows' query gets wrong.
The percentageDivided by the reference price, per C-330/23 — never by the previous price. Rounded down.
New productsArt. 6a(2): a shorter reference period for goods offered for less than 30 days. The badge says so rather than implying a full month of history.
Staged salesArt. 6a(3): a progressively increasing reduction may keep the price from before the sequence began. Off by default — it is an exemption, and claiming it wrongly is worse than not claiming it.

Why your discount got smaller

This is the first thing every customer asks, so the answer is in the response. Alongside the lawful figure the anchor endpoint returns naivePercentFromPrevious — what a previous-price comparison would have advertised.

{
  "anchorPrice": "80.00",            // the 30-day low
  "reductionPercent": "12",          // lawful: (80 - 70) / 80
  "naivePercentFromPrevious": "30"   // what you would have shown: (100 - 70) / 100
}

It is returned so you can see the gap and decide what to do about it — run the sale deeper, or advertise the honest number. It is never for display: showing it is the practice the directive prohibits.

The badge text

Generated rather than left to your theme, because the wording is part of the obligation. A bare crossed-out number does not tell a consumer what it is, and “statt 100 €” implies the price was 100 until recently — which is the impression the rule exists to prevent.

"badge": {
  "show": true,
  "price": "70,00",
  "anchorPrice": "80,00",
  "anchorLabel": "Niedrigster Preis der letzten 30 Tage",
  "percentLabel": "12 % reduziert",
  "savingsLabel": "Sie sparen 10,00 EUR",
  "locale": "de"
}

Errors

StatusCodeMeaning
400invalid_payloadThe body failed validation. `details.issues` names each field.
400invalid_priceA price was zero, negative or not a decimal.
400future_priceA price cannot take effect in the future.
401missing_api_key / invalid_api_keyNo bearer token, or one we do not recognise.
404not_foundNo variant with that external id in your organisation.
409no_historyThe variant has no recorded price, so no reduction can be announced.