Skip to main content

Overview

Geography is central to how Spree handles checkout, pricing, taxes, and shipping. The system works through a chain of related concepts:
Markets → Countries → Zones → Tax Rates & Shipping Methods
                  ↘ States
                  ↘ Addresses
  • Markets group countries into selling regions with their own currency and locale
  • Countries and States provide the geographic data for addresses
  • Zones group countries or states together to define where tax rates and shipping methods apply
  • Addresses tie a customer to a specific location, determining which zones — and therefore which taxes and shipping options — apply to their order

Addresses

An address represents a shipping or billing location. Every order has a shipping address and a billing address, and customers can save multiple addresses in their address book.
FieldDescription
first_name, last_nameContact name
address1, address2Street address
cityCity
postal_codePostal code (not required for all countries)
phonePhone number
companyCompany name (optional)
labelUser-facing label like “Home” or “Office” (optional, unique per customer)
country_isoCountry (ISO alpha-2 code, e.g., US)
state_abbrState/province abbreviation (required for some countries, e.g., CA)
Whether a state or zipcode is required depends on the country. For example, the US requires both, while Hong Kong requires neither. The API returns states_required and zipcode_required on each country so your frontend can adapt the form dynamically.

Customer Address Book

Customers can save multiple addresses and set a default for shipping and billing. When a customer completes checkout, the selected addresses are cloned onto the order — so editing an address later doesn’t change past orders.
// List addresses
const { data: addresses } = await client.customer.addresses.list()

// Create an address
const address = await client.customer.addresses.create({
  first_name: 'John',
  last_name: 'Doe',
  address1: '123 Main St',
  city: 'Los Angeles',
  country_iso: 'US',
  state_abbr: 'CA',
  postal_code: '90001',
  phone: '555-0100',
})

// Update an address
await client.customer.addresses.update('addr_xxx', { city: 'Brooklyn' })

// Delete an address
await client.customer.addresses.delete('addr_xxx')

// Set as default shipping or billing address
await client.customer.addresses.markAsDefault('addr_xxx', 'shipping')

Checkout Addresses

During checkout, you can either reference a saved address by ID or pass a new address inline:
// Use saved addresses
await client.carts.update(cartId, {
  shipping_address_id: 'addr_xxx',
  billing_address_id: 'addr_yyy',
})

// Or pass new addresses inline
await client.carts.update(cartId, {
  shipping_address: {
    first_name: 'John',
    last_name: 'Doe',
    address1: '123 Main St',
    city: 'Los Angeles',
    country_iso: 'US',
    state_abbr: 'CA',
    postal_code: '90001',
    phone: '555-0100',
  },
})

Countries

Countries are the foundation of Spree’s geographic system. They connect to Markets, contain states, and belong to zones. Each country includes metadata that drives address form behavior:
FieldDescriptionExample
isoISO 3166-1 alpha-2 codeUS
iso3ISO 3166-1 alpha-3 codeUSA
nameCountry nameUnited States
states_requiredWhether the address form should show a state/province pickertrue
zipcode_requiredWhether the address form should require a postal codetrue

Which Countries Are Available?

Only countries assigned to a Market are available during checkout. This lets you control exactly where you sell.
// List all countries available in this store
const { data: countries } = await client.countries.list()

// Get a country with its states (for address form dropdowns)
const usa = await client.countries.get('US', { include: 'states' })
// usa.states => [{ name: "Alabama", abbr: "AL" }, { name: "Alaska", abbr: "AK" }, ...]

// Get a country with its market (for currency/locale resolution)
const germany = await client.countries.get('DE', { include: 'market' })
// germany.market => { currency: "EUR", default_locale: "de", tax_inclusive: true }
Use the states_required and zipcode_required fields to build adaptive address forms — show a state picker only when needed, and skip the zipcode field for countries that don’t use them.

States

States (provinces, regions) belong to a country and are used for address validation and zone matching. Countries like the US, Canada, Australia, and India have predefined states — for these countries, customers must select a state from the list rather than typing a name. States are fetched via the country endpoint using ?include=states:
const usa = await client.countries.get('US', { include: 'states' })

// Build a state picker from the response
usa.states.forEach(state => {
  console.log(state.abbr, state.name) // "AL", "Alabama"
})
For countries without predefined states, addresses accept a free-text state_name field instead of state_abbr.

Zones

Zones group countries or states together for tax and shipping rules. A zone is either country-based or state-based. Examples:
  • EU VAT (country zone) — Germany, France, Italy, Spain, … → applies EU VAT rates
  • California (state zone) — CA → applies California sales tax
  • Domestic Shipping (country zone) — US, CA → enables domestic shipping methods
When a customer enters their address at checkout, Spree matches it against zones to determine:
  1. Which tax rates apply (see Taxes)
  2. Which shipping methods are available (see Shipments)
Zones are configured in the admin dashboard — storefront developers don’t interact with them directly via the API.

Tax Zones and Markets

Each Market resolves a tax zone from its default country. This means product prices display the correct tax treatment (inclusive or exclusive) before the customer enters an address — just from knowing their market.

How It All Fits Together

Here’s how geography flows through a typical checkout:
  1. Customer visits the store — their country is detected (from URL, geolocation, or selection)
  2. Market resolved — the country maps to a market, which sets the currency and locale
  3. Products displayed — prices use the market’s currency and tax zone for correct formatting
  4. Customer enters address — the address form adapts based on states_required and zipcode_required
  5. Zones matched — the shipping address determines which tax rates and shipping methods apply
  6. Order completed — the address is cloned onto the order for permanent record
  • Markets — Multi-region commerce with currency, locale, and country grouping
  • Taxes — How zones and addresses affect taxation
  • Shipments — How zones and addresses affect shipping availability
  • Orders — Order billing and shipping addresses