How to publish a daily FX rate sheet from live quotes without promising a price you can't honor

Build a daily indicative rate sheet for customers or sales from live quote data: which endpoint to pull, timestamps, disclaimer wording, refresh cadence.

A daily FX rate sheet is an indicative price list built from live quote data: one row per currency pair, priced at a stated reference amount, stamped with the snapshot time, and labeled as an estimate. Customers use it to plan. Every real conversion still runs on a binding quote created at the moment of the transaction.

Sales teams want a number to send. Customers want to budget before they log in. A rate sheet serves both, as long as nobody mistakes it for a price. The API-level difference between previews and quotes is in indicative rate vs binding quote. This page is the publishing workflow on top of it.

Key takeaways

  • A rate sheet is information, never a commitment. Say "indicative" on every row.
  • Price every row at a stated reference amount and snapshot time, so differences have an explanation.
  • At BlindPay, POST /payin-quotes/fx previews payin rates without creating or reserving anything.
  • The preview excludes your own partner fee. Add your markup to the sheet yourself.
  • Publish at a fixed time, refresh volatile corridors intraday, and archive every version.

What belongs on a rate sheet?

Five columns per row and one paragraph of terms. Anything less invites the "but your sheet said" email.

ColumnExampleWhy it's there
Pair and directionBRL to USDC (payin)Buy and sell rates differ, so direction is part of the price
Indicative rate5.46 BRL per USDCThe number people came for, in the quoting convention they expect
Reference amountR$ 10,000Rates can vary by size, so state the size you priced
Snapshot time2026-10-13 12:05 America/Mexico_CityTells the reader how stale the number is
StatusIndicativeRepeated per row, because rows get copied out of context

Rates read as units of local currency per one stablecoin, the way BlindPay quotes return them. How to read an FX quote explains the convention, and getting it wrong on a sheet sent to 200 customers is a bad morning.

Which data should feed the sheet?

Live quote data from the provider you actually execute with, pulled at one moment. Screen rates from a search engine or a market data feed tell you where the market is, not what your customers will get.

Payin rows. BlindPay's POST /payin-quotes/fx endpoint "returns an indicative rate and amount without creating a payin quote," per the payin quotes docs. It previews a payin direction only: from is a fiat currency and to is a stablecoin. The response carries commercial_quotation (the market rate), blindpay_quotation (the rate including BlindPay's fee), result_amount, instance_flat_fee, and instance_percentage_fee. The minimum request_amount is 500 minor units.

Payout rows. Take them from a payout quote created at snapshot time against a reference bank account for each currency. The payout quote returns the same two rate fields, holds for 5 minutes, and expires unused. Nothing moves until a payout executes against it.

Your markup. The /fx preview takes no partner_fee_id, so its fee fields never include your own margin. If you add a partner fee, apply it to the sheet's numbers yourself. Partner fees on live quotes shows the math.

Pull every row inside the same few minutes. A sheet whose BRL row is from 09:00 and MXN row from 11:00 can't be compared line by line.

How should the disclaimer read?

Short, specific, and on the sheet itself. The FX Global Code, Principle 9, says market participants should "make clear whether the prices they are providing are firm or merely indicative." Central banks follow the same habit for their own reference rates: the ECB says its euro reference rates are "published for information purposes only" and that "using the rates for transaction purposes is strongly discouraged."

A template that covers the four questions people ask:

Indicative rates as of 12:05 America/Mexico_City on October 13, 2026, priced at the reference amount shown per row. These rates are for information only and are not an offer. Every conversion executes at the rate and fees in the binding quote shown before you confirm. Quotes are valid for a limited time, usually 5 minutes.

Have legal review it once. Then never edit it per email.

How often should the sheet refresh?

Daily at a fixed time, with intraday updates where the corridor moves faster than your readers can tolerate. Pick the time your audience already watches.

AudienceGood snapshot timeReason
Mexico-facing salesAfter 12:00 Mexico CityBanxico releases the FIX "a partir de las 12:00 horas" on banking business days (Banxico)
Euro-facing customersAfter 16:00 CETThe ECB updates reference rates "at around 16:00 CET every working day"
Brazil-facing customersLate morning São PauloYour readers are at work and can act on it the same day
Volatile corridors (ARS, COP)Twice a day, or a 2-hour validity noteRates can move more inside a day

Mind your request budget when you automate it. A sheet with 6 corridors in 2 directions is 12 calls per refresh, which is nothing. A sheet that refreshes every minute on a development instance runs into the roughly 100 requests per minute cap, which returns a 429 with a Retry-After header (sandbox vs production).

How do you stop the sheet from becoming a promise?

By making the binding quote the only path to a transaction. Three rules:

  1. Sales never quotes from the sheet in writing as a price. They quote it as an estimate and send the customer to the confirm screen.
  2. The product never prefills a payment with a sheet rate. The confirm screen always shows the binding quote's blindpay_quotation, fees, and received amount, plus a countdown from expires_at.
  3. Support answers differences with the sheet's own metadata. Snapshot time and reference amount explain most gaps in one sentence.

Illustrative example. The sheet at 12:05 shows BRL to USDC at 5.46 for R$ 10,000, so about 10,000 / 5.46 = 1,831.50 USDC. A customer opens the app at 15:40 and converts R$ 10,000. The binding quote comes back at 5.49: 10,000 / 5.49 = 1,821.49 USDC. The gap is 10.01 USDC, or 10.01 / 1,831.50 x 10,000 = about 55 bps, from three and a half hours of market movement. The sheet did its job: the customer budgeted within 0.6% and paid the quoted price, not a stale one.

If the customer wants certainty on a future date, a sheet is the wrong tool. Live quote vs forward contract vs fixed rate covers the instruments that lock longer, and how on/off-ramp providers make money explains why holding a price for hours costs someone money.

What should you archive?

Every version, with the raw responses. Store the request time, the request body, and the full response for each row, plus the published file. When a customer disputes a conversion months later, you can show what the sheet said, when, and that the transaction ran on a separate binding quote with its own ID. Keep the archive next to your quote records so finance has one place to look.

What to do next

Script the sheet on a development instance first: call /payin-quotes/fx for each payin pair, add your markup, stamp the rows, and render the disclaimer. The payin quotes reference documents every field the script reads.

FAQ