Skip to main content
All monetary values in the Admin API are canonical decimal strings (e.g., "29.99", "0.0" — period decimal, no thousands grouping), both in responses and in request bodies. Strings preserve decimal precision and avoid the floating-point rounding issues common with JSON numbers — critical when an admin is setting prices and costs. The format is the same regardless of locale; localized formatting is a presentation concern handled by the client (see Canonical format).

Reading

Every monetary field is returned as a string, alongside a display_ companion that includes currency formatting:
{
  "amount": "29.99",
  "display_amount": "$29.99",
  "compare_at_amount": "39.99",
  "display_compare_at_amount": "$39.99"
}
Use display_* for rendering and the raw string fields for calculations (parseFloat(price.amount)).

Writing

Send amounts back as strings too, so a value reads and writes in the same type:
await client.products.create({
  name: 'Classic Tee',
  prices: [{ currency: 'USD', amount: '29.99', compare_at_amount: '39.99' }],
})
JSON numbers are also accepted ("amount": 29.99), but strings are recommended: they round-trip with what the API returns and sidestep float precision. A null clears the value.

Canonical format — not localized

Amounts are canonical: a period decimal separator and no thousands grouping ("1234.56"), independent of any locale. The Admin API does not parse locale-specific formats — do not send "1.234,56" or "1,234.56". If you are taking input from a merchant in a localized format, normalize it to canonical form before sending. The Spree dashboard does this in the browser: a EUR price typed as 1.234,56 becomes 1234.56 on the wire. This matches how other commerce APIs handle money (canonical decimal or minor-unit integers; localization is a presentation concern).

Affected types

This convention applies to all monetary fields across all resources, including back-office-only fields the Store API never exposes:
ResourceMonetary Fields
Product / Variantprice, compare_at_amount, cost_price
Priceamount, compare_at_amount
Ordertotal, item_total, ship_total, tax_total, adjustment_total, promo_total, included_tax_total, additional_tax_total
Line Itemprice, total, adjustment_total, promo_total, pre_tax_amount, discounted_amount, compare_at_amount
Paymentamount
Refundamount
Shipmentcost
Gift Cardamount, amount_used, amount_authorized, amount_remaining
Store Creditamount, amount_used, amount_remaining

Amounts inside preferences

Calculators and some promotion rules carry their amounts inside an untyped preferences object (Record<string, unknown>) rather than as top-level fields. The same canonical-string convention applies to the money-valued keys within that hash — they read back as strings and accept canonical decimal strings on write. The keys that are money:
ResourceMoney keys in preferences
Calculator flat_rate, per_item, digital_deliveryamount
Calculator flexi_ratefirst_item, additional_item
Calculator price_sackminimal_amount, normal_amount, discount_amount
Calculator tiered_flat_ratebase_amount, and the values of tiers
Calculator flat_rate (shipping)amount, minimum_item_total, maximum_item_total
Promotion Rule item_totalamount_min, amount_max
Percentage keys (percent, flat_percent, base_percent), weights (minimum_weight, maximum_weight), and quantities (max_items, min_quantity) are not money — those stay numbers. Promotion actions (create_adjustment, create_item_adjustments) hold their money inside a nested calculator.preferences, per the calculator rows above.