Cross-Border & Markets
Sell internationally with per-market currencies, locales, translated content, and automatic GeoIP market routing.
How Markets Work
A Market groups countries that share a currency and language. Each market defines: countries (ISO-3166-1 alpha-2), base currency, default locale, available locales, tax-inclusive flag, and duty mode (DDU or DDP).
Pricing Hierarchy
When resolving a product price for a market: (1) check for a per-variant MarketPrice override, (2) fall back to the variant base price. All prices are stored as int64 cents.
GeoIP Routing
The Markets middleware automatically resolves the visitor country from CF-IPCountry (Cloudflare) and selects the matching market. Falls back to the primary market if no match.
/admin/marketsadmin-jwtCreate a new market. If is_primary is true, the existing primary market is unset.
Request Body
{
"active": true,
"available_locales": [
"de",
"fr"
],
"base_currency": "EUR",
"countries": [
"DE",
"FR",
"AT"
],
"default_locale": "de",
"duty_mode": "DDP",
"name": "Europe",
"tax_inclusive": true
}/admin/markets/:id/pricesadmin-jwtBulk set per-variant price overrides for a market.
Request Body
[
{
"amount": 2499,
"compare_at": 2999,
"variant_id": "uuid"
}
]/admin/translations/:type/:entityId/:localeadmin-jwtSet translations for an entity in a locale. type: product | collection | page | article
Request Body
{
"description": "Weiches Baumwoll-T-Shirt.",
"title": "Klassisches T-Shirt"
}Storefront API: Market-Aware Routes
The Headless Storefront API v1 supports locale-prefixed routes: /storefront/v1/de/products returns EUR prices with German translations. GET /storefront/v1/seo/hreflang returns all locale alternate links.
Code Examples
curl -X POST https://cartly.pro/admin/markets \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name":"Europe","countries":["DE","FR"],"base_currency":"EUR","default_locale":"de","available_locales":["de","fr"],"tax_inclusive":true,"duty_mode":"DDP","active":true}'