Selling internationally on Shopify sounds straightforward until you realize your customers in Germany see prices that are 3% higher than the mid-market rate, your Australian shoppers get stale conversions from yesterday, and your checkout abandonment rate for international orders is double your domestic rate.
Shopify Markets handles basic multi-currency display, but it has limitations that serious international sellers hit quickly. Integrating an exchange rate API Shopify solution gives you control over the rates your customers see, when those rates update, and how you handle rounding and pricing psychology. This guide covers the limitations of Shopify's built-in tools, three integration approaches (Liquid, Shopify Functions, and theme app extensions), and practical code you can deploy today.
What Shopify Markets Gets Right (and Wrong)
Shopify Markets, introduced in 2022, lets you assign markets to countries and display prices in local currencies. On the surface it works well. Under the hood, there are issues:
What works:
- Automatic currency detection based on customer IP
- Currency selector in the storefront
- Rounding rules (e.g., round to .99)
What does not:
- Rate freshness. Shopify updates its internal exchange rates once or twice per day. If EUR/USD moves 0.5% during trading hours, your European prices are stale until the next update.
- No rate source transparency. Shopify does not disclose where its exchange rates come from or what markup is applied.
- Manual price locks. Shopify allows you to fix prices per market, but this requires manual maintenance across hundreds of SKUs.
- Limited control over rounding. You can round to .99 or .00, but more nuanced psychological pricing rules (like rounding to the nearest 50 in JPY) require workarounds.
- No rate auditing. You cannot compare Shopify's applied rate against a reference rate to check for hidden markup.
For stores doing under $10,000/month in international sales, Shopify Markets is fine. For stores where international revenue is material, you need an exchange rate API Shopify integration to take control.
Three Integration Approaches
Approach 1: Server-Side Conversion with a Middleware App
This is the most robust approach. You build a small server (or use a serverless function) that sits between your Shopify admin and your storefront.
How it works:
- A scheduled job fetches current rates from the API every 60 seconds.
- Rates are stored in a cache (Redis, or even a simple JSON file on a CDN).
- Your Shopify theme reads the cached rates and converts prices client-side, or your middleware writes converted prices to Shopify metafields.
Fetching rates:
// rates-updater.js (runs on a cron or serverless schedule)
const API_KEY = process.env.ALLRATESTODAY_API_KEY;
async function updateRates() {
const response = await fetch(
"https://api.allratestoday.com/v1/latest?base=USD&symbols=EUR,GBP,JPY,AUD,CAD,CHF,SEK,NOK,DKK",
{ headers: { "Authorization": "Bearer " + API_KEY } }
);
const data = await response.json();
// Store in your cache layer
await redis.set("fx_rates", JSON.stringify({
rates: data.rates,
updated_at: data.timestamp
}), "EX", 120); // Expire after 120 seconds
}
Writing to Shopify metafields:
const shopify = new Shopify.Clients.Rest(shop, accessToken);
// Store rates as a shop-level metafield
await shopify.post({
path: "metafields",
data: {
metafield: {
namespace: "fx_rates",
key: "current",
value: JSON.stringify(rates),
type: "json"
}
}
});
This approach gives you full control and works with any Shopify plan.
Approach 2: Liquid Template Snippets
If you want a lighter solution without a separate server, you can use Shopify metafields populated via the Admin API and read them in Liquid templates.
First, populate the rates using the Admin API (you can do this from a simple script running on your machine or a GitHub Action):
# Fetch rates
RATES=$(curl -s "https://api.allratestoday.com/v1/latest?base=USD" \
-H "Authorization: Bearer YOUR_API_KEY" | jq '.rates')
# Write to Shopify metafield
curl -X POST "https://your-store.myshopify.com/admin/api/2026-04/metafields.json" \
-H "X-Shopify-Access-Token: YOUR_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"metafield\": {\"namespace\": \"fx\", \"key\": \"rates\", \"value\": \"$RATES\", \"type\": \"json\"}}"
Then in your Liquid template:
{% assign fx_rates = shop.metafields.fx.rates.value %}
{% assign customer_currency = localization.country.currency.iso_code %}
{% if customer_currency != shop.currency %}
{% assign rate = fx_rates[customer_currency] %}
{% if rate %}
{% assign converted_price = product.price | times: rate | round %}
<span class="local-price">
{{ converted_price | money_without_currency }} {{ customer_currency }}
</span>
<span class="base-price" style="font-size: 0.8em; color: #666;">
({{ product.price | money }})
</span>
{% endif %}
{% else %}
{{ product.price | money }}
{% endif %}
This approach requires no app installation and works within Shopify's existing infrastructure. The downside is that Liquid cannot fetch external data directly, so you still need an external process to update the metafield.
Approach 3: Theme App Extension with Client-Side Conversion
For app developers or stores comfortable with JavaScript, a theme app extension provides the cleanest user experience.
// assets/currency-converter.js
class CurrencyConverter {
constructor(apiKey) {
this.apiKey = apiKey;
this.rates = {};
this.baseCurrency = Shopify.currency.active;
}
async loadRates() {
// In production, proxy through your own server to protect the API key
const cached = sessionStorage.getItem("fx_rates");
if (cached) {
const parsed = JSON.parse(cached);
const age = Date.now() - parsed.timestamp;
if (age < 120000) { // 2 minutes
this.rates = parsed.rates;
return;
}
}
const res = await fetch("/apps/fx-proxy/rates?base=" + this.baseCurrency);
const data = await res.json();
this.rates = data.rates;
sessionStorage.setItem("fx_rates", JSON.stringify({
rates: data.rates,
timestamp: Date.now()
}));
}
convert(amount, targetCurrency) {
const rate = this.rates[targetCurrency];
if (!rate) return null;
return (amount * rate).toFixed(2);
}
applyToPage(targetCurrency) {
document.querySelectorAll("[data-price-amount]").forEach(el => {
const baseAmount = parseFloat(el.dataset.priceAmount);
const converted = this.convert(baseAmount, targetCurrency);
if (converted) {
el.textContent = converted + " " + targetCurrency;
}
});
}
}
The proxy endpoint (/apps/fx-proxy/rates) keeps your API key server-side and adds caching.
Handling Price Psychology Across Currencies
Raw mathematical conversion creates awkward prices. $49.99 USD converts to 45.7241 EUR, which looks wrong on a product page. You need rounding rules per currency.
Here is a rounding configuration you can apply after conversion:
const roundingRules = {
USD: { precision: 2, charm: 0.99 }, // $49.99
EUR: { precision: 2, charm: 0.99 }, // 45.99
GBP: { precision: 2, charm: 0.99 }, // 39.99
JPY: { precision: 0, charm: 0 }, // 7600 (no decimals)
SEK: { precision: 0, charm: 0 }, // 499
CHF: { precision: 1, charm: 0.90 }, // 44.90
INR: { precision: 0, charm: 0 }, // 4199
};
function applyPsychologicalRounding(amount, currency) {
const rule = roundingRules[currency] || { precision: 2, charm: 0 };
const factor = Math.pow(10, rule.precision);
let rounded = Math.ceil(amount * factor) / factor;
if (rule.charm > 0) {
rounded = Math.floor(rounded) + rule.charm;
}
return rounded;
}
This turns 45.7241 EUR into 45.99 EUR and 7648.47 JPY into 7648 JPY, which look natural to local shoppers.
Automatic Price Updates: How Often Is Enough?
For most Shopify stores, updating exchange rates every 15 to 60 minutes strikes the right balance between freshness and API usage. Here is a usage estimate:
| Update Interval | Requests/Day | Requests/Month | AllRatesToday Tier Needed |
|---|---|---|---|
| Every 60 seconds | 1,440 | 43,200 | Paid |
| Every 15 minutes | 96 | 2,880 | Paid (just above free) |
| Every 60 minutes | 24 | 720 | Free (1,500/month) |
| Every 4 hours | 6 | 180 | Free |
| Once daily | 1 | 30 | Free |
For an exchange rate API Shopify integration on the free tier, hourly updates use only 720 requests per month, leaving room for historical and conversion queries.
Dealing with Shopify Markets Conflicts
If you already have Shopify Markets enabled, adding your own exchange rate source can create conflicts. Here is how to handle it:
Option A: Disable Shopify Markets currency conversion. Set all markets to manual pricing, then use your API-driven prices. This gives you full control but means you manage everything.
Option B: Use Shopify Markets for checkout, API for display. Show your API-converted prices on product pages and collection pages, but let Shopify handle the actual checkout conversion. Add a disclaimer: "Final price may vary slightly at checkout."
Option C: Overwrite Shopify rates via the API. Use the Shopify Admin API to write your own exchange rates into the Markets configuration. This is the cleanest approach if you have Shopify Plus.
Most merchants choose Option B because it works on any Shopify plan and avoids disrupting the checkout flow.
Showing "Last Updated" for Trust
International shoppers appreciate knowing that prices are current. Add a small timestamp near your currency selector:
{% assign fx_updated = shop.metafields.fx.updated_at.value %}
<div class="fx-freshness">
Rates updated: {{ fx_updated | date: "%b %d, %Y %H:%M UTC" }}
<br>
<small>Source: Mid-market rates via AllRatesToday</small>
</div>
This transparency builds trust and differentiates your store from competitors who show stale or opaque conversions.
Measuring the Impact
After integrating an exchange rate API Shopify solution, track these metrics:
- International checkout completion rate -- should increase as price surprise at checkout decreases
- International revenue as % of total -- accurate local pricing typically increases international sales
- Average order value by market -- psychological rounding can increase or decrease AOV; monitor both directions
- Support tickets about pricing -- should decrease significantly
Give it 30 days and compare against your pre-integration baseline.
Step-by-Step Quick Start
- Sign up at allratestoday.com/pricing -- the free tier is enough to get started.
- Choose your integration approach from the three options above based on your Shopify plan and technical comfort.
- Set up rate fetching using the latest rates endpoint:
GET /v1/latest?base=USD. - Implement rounding rules for your target currencies.
- Deploy and monitor checkout completion rates for 30 days.
Full API documentation, including code examples in JavaScript, Python, Ruby, and PHP, is available at allratestoday.com/developers. The exchange rate API Shopify integration typically takes a developer half a day to implement and starts paying for itself within the first week through reduced checkout abandonment.