Multi-currency pricing via API
- Type
- Applies to
- Medusa v2.x
- Updated
Short answer
Medusa handles multi-currency pricing through its Pricing Module: each variant has a price set with one price per currency, plus optional prices scoped by region, customer group or quantity. The Store API returns a calculated price for the shopper's region and country, with and without tax. Medusa does not convert currencies automatically.
How Medusa's price model handles multiple currencies
Medusa's Pricing Module links every product variant and shipping option to a price set: a collection of prices for one resource. Each price has a Copy to clipboardcurrency_code and an Copy to clipboardamount stored in major units, so Copy to clipboard20 means 20.00 (Pricing Concepts).
Prices can carry rules and quantity tiers:
- Price rules limit a price to a context, such as a Copy to clipboard
region_id, a customer group or a postal code. - Tiers use Copy to clipboard
min_quantityand Copy to clipboardmax_quantityto change the price by quantity. - Price lists group prices of type Copy to clipboard
saleor Copy to clipboardoverridethat apply only when their rules match, optionally within a date range.
How to set multi-currency prices with the Admin API
You send prices per currency when you create or update a variant. A price with Copy to clipboardrules applies only in that context. This body for Copy to clipboardPOST /admin/products/{id}/variants follows the Admin API schema:
12345678{"title": "Large","prices": [{ "currency_code": "usd", "amount": 40 },{ "currency_code": "eur", "amount": 39 },{ "currency_code": "eur", "amount": 42, "rules": { "region_id": "reg_123" } }]}
Customers in region Copy to clipboardreg_123 see 42 EUR. Other euro customers see 39 EUR.
How the Store API returns calculated prices
The Store API resolves one price per variant for the shopper's context. Add Copy to clipboard*variants.calculated_price to Copy to clipboardfields on the List Products or Get Product routes, and pass at least one context parameter (Retrieve Product Variant's Prices):
- Copy to clipboard
region_id: the region, which also sets the currency. - Copy to clipboard
country_codeand Copy to clipboardprovince: needed for prices with tax. - Copy to clipboard
cart_id: uses the cart's region and shipping address instead.
12345const { products } = await sdk.store.product.list({fields: "*variants.calculated_price",region_id: region.id,country_code: "fr",})
Each variant gets Copy to clipboardcalculated_amount, Copy to clipboardoriginal_amount, Copy to clipboardcurrency_code, and, when both region and country are passed, Copy to clipboardcalculated_amount_with_tax and Copy to clipboardcalculated_amount_without_tax. If the request is authenticated, the customer's groups are used to select group prices automatically.
How to calculate prices in backend code
In custom API routes or workflows, you pass the currency and region directly with Query (Get Variant Prices):
12345678910const { data: products } = await query.graph({entity: "product",fields: ["variants.calculated_price.*"],filters: { id: "prod_123" },context: {variants: {calculated_price: QueryContext({ region_id: "reg_123", currency_code: "eur" }),},},})
How Medusa selects a price
Medusa's Copy to clipboardcalculatePrices returns a calculated price and an original price for each price set (Prices Calculation):
- With no rules in the context, it selects the default price for the currency.
- If a price matches every rule in the context, it selects that price.
- Otherwise, it selects the price that matches the most rules.
A matching price list price becomes the calculated price. For sale price lists, the original price stays the regular price so the storefront can show a strike-through; for override price lists, the original price equals the calculated price.
Medusa compared with commercetools price selection
Both platforms store explicit prices per currency and pick one per request. They differ in which dimensions a price can be scoped by.
commercetools facts come from its pricing overview and Product Projections docs.
When Medusa is not the right choice
- You want prices converted from one base currency automatically. Medusa needs a price per currency or a custom sync job. Shopify converts prices at the current market exchange rate by default, and BigCommerce auto-converts prices using an exchange rate the merchant sets or updates through its API.
- You need a search index sorted by price in many currencies with no setup. Medusa's Pricing Module calculates a price for one currency per query, so a search index needs a price field per currency (Index Prices for Multiple Currencies).
Common questions
How do I get a product's price in a specific currency from the Medusa Store API?
Pass the region_id of a region that uses that currency, with fields=*variants.calculated_price. Medusa returns each variant's calculated_price in the region's currency.
Can Medusa have two prices in the same currency for different regions?
Yes. Add a price with a region_id rule. Medusa selects it for that region and uses the default price for the currency elsewhere.
Does Medusa support customer-specific prices in multiple currencies?
Yes. Medusa price lists hold prices per currency with rules such as customer group, and the Store API applies them when the customer is authenticated.
How is Medusa's price selection different from commercetools?
commercetools scopes prices by currency, country, customer group and channel and walks a fixed fallback order. Medusa scopes prices by currency plus any rules and picks the price that matches the most rules.