docs/developer/core-concepts/markets.mdx
Markets let you segment a single Store into distinct geographic regions, each with its own currency, locale, and set of countries. For example, an international store might define:
erDiagram
Store ||--o{ Market : "has many"
Market ||--o{ MarketCountry : "has many"
MarketCountry }o--|| Country : "belongs to"
Store {
string name
string url
}
Market {
string name
string currency
string default_locale
string supported_locales
boolean tax_inclusive
boolean default
}
Country {
string iso
string name
}
| Attribute | Description | Example |
|---|---|---|
name | Human-readable name, unique per store | North America |
currency | ISO 4217 currency code | USD |
default_locale | Default language for this market | en |
supported_locales | All locales available in this market | ["en", "es"] |
tax_inclusive | Whether prices include tax (affects display and checkout calculation) | false |
default | Whether this is the fallback market when no country match is found | true |
countries | List of countries in this market | [{ iso: "US" }, { iso: "CA" }] |
When a customer visits your store, their country determines which market applies. The market then sets the currency, locale, and tax behavior for that session.
Customer's Country → Market → Currency + Locale + Tax Zone
The resolution chain:
X-Spree-Country header, or manual selection)If no market matches the customer's country, the store's default market is used.
Fetch all markets for the current store, including their countries:
<CodeGroup>const { data: markets } = await client.markets.list()
// [
// {
// id: "mkt_k5nR8xLq",
// name: "North America",
// currency: "USD",
// default_locale: "en",
// supported_locales: ["en", "es"],
// tax_inclusive: false,
// default: true,
// countries: [
// { iso: "US", name: "United States", states_required: true, zipcode_required: true },
// { iso: "CA", name: "Canada", states_required: true, zipcode_required: true }
// ]
// },
// ...
// ]
curl 'https://api.mystore.com/api/v3/store/markets' \
-H 'Authorization: Bearer pk_xxx'
When you know a customer's country (e.g., from geolocation or a country picker), resolve which market applies:
<CodeGroup>const market = await client.markets.resolve('DE')
// { id: "mkt_gbHJdmfr", name: "Europe", currency: "EUR", tax_inclusive: true, ... }
curl 'https://api.mystore.com/api/v3/store/markets/resolve?country=DE' \
-H 'Authorization: Bearer pk_xxx'
Returns the market object on success, or 404 if no market contains that country.
This is useful for building a country switcher — resolve the market to show the customer what currency and language they'll get.
List countries belonging to a specific market. Useful for populating address form dropdowns during checkout:
<CodeGroup>const { data: countries } = await client.markets.countries.list('mkt_k5nR8xLq')
// [
// { iso: "CA", name: "Canada", states_required: true, zipcode_required: true },
// { iso: "US", name: "United States", states_required: true, zipcode_required: true }
// ]
// Get a country with its states (for address form dropdowns)
const usa = await client.markets.countries.get('mkt_k5nR8xLq', 'US', {
include: 'states',
})
# List countries in a market
curl 'https://api.mystore.com/api/v3/store/markets/mkt_k5nR8xLq/countries' \
-H 'Authorization: Bearer pk_xxx'
# Get a country with its states
curl 'https://api.mystore.com/api/v3/store/markets/mkt_k5nR8xLq/countries/US?include=states' \
-H 'Authorization: Bearer pk_xxx'
You can also fetch countries flat (across all markets) or include the market on a country:
<CodeGroup>// All countries across all markets
const { data: countries } = await client.countries.list()
// Get a country with its market details
const germany = await client.countries.get('DE', { include: 'market' })
// { iso: "DE", name: "Germany", market: { currency: "EUR", default_locale: "de", tax_inclusive: true } }
# All countries across all markets
curl 'https://api.mystore.com/api/v3/store/countries' \
-H 'Authorization: Bearer pk_xxx'
# Get a country with its market
curl 'https://api.mystore.com/api/v3/store/countries/DE?include=market' \
-H 'Authorization: Bearer pk_xxx'
Each market defines a currency and set of supported locales. When a market is resolved, its currency and locale become the defaults for the session.
You can discover all available currencies and locales (aggregated from all markets) via dedicated endpoints:
<CodeGroup>const { data: currencies } = await client.currencies.list()
// [{ iso_code: "USD", name: "US Dollar", symbol: "$" }, { iso_code: "EUR", name: "Euro", symbol: "€" }]
const { data: locales } = await client.locales.list()
// [{ code: "en", name: "English" }, { code: "de", name: "German" }]
curl 'https://api.mystore.com/api/v3/store/currencies' \
-H 'Authorization: Bearer pk_xxx'
curl 'https://api.mystore.com/api/v3/store/locales' \
-H 'Authorization: Bearer pk_xxx'
See Localization for details on how to pass locale, currency, and country headers in API requests.
The tax_inclusive flag on a market controls how prices are displayed and calculated:
tax_inclusive: true (common in Europe) — the price shown to the customer already includes taxtax_inclusive: false (common in the US) — tax is added at checkout on top of the displayed priceEach market also resolves a tax zone from its default country. This zone determines which tax rates apply when browsing products — before the customer enters a shipping address. Once the customer provides an address at checkout, the actual shipping address takes over for tax calculation.
See Taxes for details on tax zones and rates.
Markets integrate with the Pricing system, enabling market-specific pricing through Price Lists with a Market Rule. This lets you set different prices for the same product in different markets — beyond just currency conversion.
For example, you could price a product at $29.99 in North America and €24.99 in Europe, rather than relying on exchange rate conversion.
See Pricing — Price Rules for details on configuring market-specific price lists.
Markets are managed in the admin dashboard under Settings → Markets. When you run rails db:seed, Spree automatically creates a default market for each store.
To create markets programmatically:
# North America market
current_store.markets.create!(
name: 'North America',
currency: 'USD',
default_locale: 'en',
countries: [usa, canada],
default: true
)
# Europe market with tax-inclusive pricing
current_store.markets.create!(
name: 'Europe',
currency: 'EUR',
default_locale: 'de',
supported_locales: 'de,en,fr',
tax_inclusive: true,
countries: [germany, france, austria, netherlands]
)