Preços multi-moeda no seu site

Show localized prices on your pricing page or checkout

Você define os preços em uma moeda-base (ex.: USD) e mostra ao visitante o valor aproximado na moeda local. Busque as taxas uma vez por sessão, guarde em localStorage com um TTL, formate com Intl.NumberFormat e tenha um fallback para o preço-base se a busca falhar.

You keep prices in one base currency (e.g. USD) and show the visitor the approximate amount in their local currency. Fetch the rates once per session, cache them in localStorage with a TTL, format with Intl.NumberFormat, and fall back to the base price if the fetch fails.

Base: https://moneta.api.insyde.one — aberta, sem autenticação / open, no auth.

1. O endpoint / The endpoint

Uma única chamada traz todas as moedas que você precisa. / A single call brings every currency you need.

GET /rates?base=USD&quotes=BRL,EUR,GBP,JPY
[
  { "date": "2026-06-03", "base": "USD", "quote": "BRL", "rate": 5.0203 },
  { "date": "2026-06-03", "base": "USD", "quote": "EUR", "rate": 0.9210 }
]

2. Cache por sessão / Cached fetch

As taxas mudam no máximo uma vez por dia útil, então um TTL de algumas horas é seguro. Guardamos o mapa quote → rate e o carimbo de tempo.

Rates change at most once per business day, so a TTL of a few hours is safe. We store the quote → rate map plus a timestamp.

const BASE = "USD";
const QUOTES = ["BRL", "EUR", "GBP", "JPY"];
const TTL = 6 * 60 * 60 * 1000;          // 6h em ms
const KEY = "moneta-rates-v1";

async function getRates() {
  const cached = JSON.parse(localStorage.getItem(KEY) || "null");
  if (cached && Date.now() - cached.ts < TTL) return cached.rates;

  const url = `https://moneta.api.insyde.one/rates?base=${BASE}&quotes=${QUOTES.join(",")}`;
  const res = await fetch(url);
  if (!res.ok) throw new Error("rates unavailable");

  const rows = await res.json();
  const rates = Object.fromEntries(rows.map(r => [r.quote, r.rate]));
  rates[BASE] = 1;                                // base → base
  localStorage.setItem(KEY, JSON.stringify({ ts: Date.now(), rates }));
  return rates;
}

3. Formatar com Intl / Format with Intl

Intl.NumberFormat cuida do símbolo, dos separadores e das casas decimais corretas para cada moeda — não monte isso à mão.

Intl.NumberFormat handles the symbol, separators and correct decimal places for each currency — do not hand-roll it.

function format(amount, currency, locale) {
  return new Intl.NumberFormat(locale, {
    style: "currency",
    currency,
  }).format(amount);
}

4. Juntando tudo / Putting it together

O preço-base é a fonte da verdade. Se a busca de taxas falhar, mostramos o preço-base sem prefixo de estimativa. Se der certo, mostramos o valor convertido com .

The base price is the source of truth. If the rate fetch fails, we show the base price with no estimate prefix. If it succeeds, we show the converted value with .

const BASE_PRICE = 29;                            // 29 USD

async function renderPrice(el, currency, locale) {
  const baseLabel = format(BASE_PRICE, BASE, "en-US");
  try {
    const rates = await getRates();
    const rate = rates[currency];
    if (!rate) throw new Error("no rate");
    el.textContent = `≈ ${format(BASE_PRICE * rate, currency, locale)}`;
    el.title = `Charged as ${baseLabel}`;
  } catch {
    el.textContent = baseLabel;                  // fallback: preço-base
  }
}

renderPrice(document.getElementById("price"), "BRL", "pt-BR");

Resultado típico / Typical output: ≈ R$ 145,59 com a dica "Charged as $29.00".

5. Detectar a moeda do visitante / Detecting the visitor currency

Use o locale do navegador como pista, mas deixe o usuário trocar manualmente. Nunca presuma a moeda só pelo idioma.

Use the browser locale as a hint, but let the user switch manually. Never assume currency from language alone.

const locale = navigator.language || "en-US";   // ex.: "pt-BR"
// mapeie locale → moeda com sua própria tabela, ou ofereça um seletor
Importante: as taxas da Moneta são taxas de referência oficiais de bancos centrais, não taxas transacionais. Mostre sempre como estimativa (), cobre na moeda-base e deixe o seu PSP aplicar a taxa real na liquidação.
Important: Moneta rates are official central-bank reference rates, not transactional rates. Always show them as an estimate (), charge in the base currency, and let your PSP apply the real rate at settlement.
Cache: as taxas mudam no máximo uma vez por dia útil; um TTL de 6 h no localStorage evita chamadas repetidas sem servir dados velhos.
Cache: rates change at most once per business day; a 6 h localStorage TTL avoids repeat calls without serving stale data.

Moneta docs · Live demo · Back to Insyde APIs