Indicative rate vs binding quote: how to show a live price before the user commits

An indicative rate previews a price. A binding quote locks it. How to show a live price while the user types, then lock it before they pay.

An indicative rate is a price preview: the provider shows what a conversion would cost right now but does not commit to it. A binding quote is a price the provider will execute at, for a stated window, under a quote ID. Good payment UX uses both: the preview while the user types, the quote when they commit.

Most teams get this backwards. Some create a binding quote on every keystroke, which burns quotes and requests for nothing. Others show a cached rate and settle at a different one, which is slippage with extra steps. This guide covers the two-call pattern, what each BlindPay response contains, and the four reasons a preview and a quote disagree.

Key takeaways

  • An indicative rate is information. A binding quote is a commitment with an ID and an expiry.
  • Show the indicative rate while the user types. Create the binding quote when they reach the confirm screen.
  • At BlindPay, POST /payin-quotes/fx returns an indicative rate and creates nothing. POST /payin-quotes returns a quote that holds for 5 minutes.
  • A preview can differ from the quote one second later. The confirm screen shows the quote's numbers, never the preview's.
  • Locking a price has a cost, and the cost grows with the lock. That's why binding windows are measured in minutes.

What is the difference between an indicative rate and a binding quote?

An indicative rate tells you where the price is. A binding quote tells you what you will get. The glossary for this cluster defines both the same way: a firm (binding) quote is a price the provider commits to execute at, and an indicative rate is shown for information only.

FX markets treat the line between the two as a conduct issue. The FX Global Code (updated December 2024), Principle 9, says market participants handling orders should "make clear whether the prices they are providing are firm or merely indicative." Payment APIs should be just as clear.

Indicative rateBinding quote
Provider commits to it?NoYes, for the stated window
Has an ID you can execute against?NoYes
Has an expiry?NoYes (expires_at)
Stored by the provider?NoYes
Who carries a market move?You, at executionThe provider, inside the window
Right place in the UIAmount field, calculators, corridor pagesConfirm screen, approval lists
BlindPay payin endpointPOST /payin-quotes/fxPOST /payin-quotes

Why not create a binding quote on every keystroke?

Because a binding quote is a commitment, and commitments are not free. Each one asks the provider to stand behind a price for minutes, and almost all of them will expire unused if you create one per keystroke.

Three practical problems follow:

  1. Request volume. A user typing "25000" fires five requests. On BlindPay development instances, requests are capped at roughly 100 per minute per instance, and going over returns a 429 with a Retry-After header (sandbox vs production). A busy amount field gets there fast.
  2. Wasted windows. The quote's clock starts at creation. A quote created while the user is still typing has already spent part of its 5 minutes before anyone looks at it.
  3. Noisy metrics. If you track how many quotes expire before confirmation, keystroke quotes bury the real signal. You can't tell an abandoned checkout from a typo.

How does the two-call pattern work?

Preview while the amount changes, lock when the user commits. Five steps:

  1. Amount screen opens. Nothing is priced yet.
  2. User types. Debounce input by 300 to 500 ms, then call the indicative endpoint and show the result as an estimate ("about 1,834 USDC").
  3. User taps Continue. Create the binding quote. Show the quoted amounts and a countdown built from expires_at.
  4. User confirms. Create the payin (or payout) with the quote ID before it expires.
  5. Quote expires first. Request a new quote, update the numbers, and highlight the change only if the amount moved.

The on/off-ramp liquidity guide explains why the window length shapes which of these screens you can afford.

What does BlindPay's indicative endpoint return?

POST /instances/{instance_id}/payin-quotes/fx returns an indicative rate and amount without creating a payin quote. Per the payin quotes docs, it doesn't persist anything and doesn't reserve pricing.

Bash
curl https://api.blindpay.com/v1/instances/in_000000000000/payin-quotes/fx \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer YOUR_API_KEY' \
  --data '{
  "from": "BRL",
  "to": "USDC",
  "request_amount": 10000,
  "currency_type": "sender"
}'
FieldDirectionWhat it means
fromRequestThe fiat currency the payer sends, for example BRL
toRequestThe stablecoin the destination receives: USDC, USDT, or USDB on development
request_amountRequestMinor units, minimum 500 ($5.00). MXN, COP, and ARS sender amounts must be whole units
currency_typeRequestsender or receiver, same meaning as on a payin quote
commercial_quotationResponseThe raw market rate
blindpay_quotationResponseThe rate including BlindPay's fee
result_amountResponseThe other side of the conversion
instance_flat_feeResponseThe flat-fee component
instance_percentage_feeResponseThe percentage fee, in basis points

The preview covers the payin direction: from is a fiat currency and to is a stablecoin. On the payout side, the payout quote itself is what you show on the confirm screen.

What does the binding quote add?

An ID, an expiry, and every fee line. A payin quote response carries id, expires_at (epoch milliseconds), sender_amount, receiver_amount, commercial_quotation, blindpay_quotation, flat_fee, partner_fee_amount, billing_fee_amount, and is_otc.

It holds for 5 minutes by default. OTC payin quotes, BRL only and priced through BlindPay's OTC desk, hold for 10 seconds. Payout quotes also default to 5 minutes, and SEPA payout quotes can be shorter because the rail's own deadline can be tighter (cut-off times). An expired quote is rejected when you try to use it, and nothing moves. The field-by-field tour lives in stablecoin API quotes explained.

Why can the preview and the quote disagree?

Four reasons, and only the first one is the market.

  • Time. The preview reserved nothing. If BRL moves between the two calls, the quote reflects the new price.
  • Partner fees. The /fx endpoint takes no partner_fee_id, so instance_percentage_fee never includes your own markup. If your platform adds a partner fee, the preview looks better than the quote. Add your markup to the preview yourself.
  • Fee direction. The binding quote takes cover_fees, which decides whether the payer pays the fee on top or the recipient absorbs it. Make sure your preview math assumes the same direction you'll send on the quote.
  • Rounding. MXN, COP, and ARS payins settle in whole currency units, so small differences show up on peso amounts.

Illustrative example. A payer types R$10,000 at 09:00:00. The preview shows a blindpay_quotation of 5.45 BRL per USDC:

  • Preview: 10,000 / 5.45 = 1,834.86 USDC
  • The payer taps Continue at 09:00:40. The quote comes back at 5.46: 10,000 / 5.46 = 1,831.50 USDC
  • Difference: 1,834.86 - 1,831.50 = 3.36 USDC
  • In basis points: 3.36 / 1,834.86 x 10,000 = about 18 bps

That 18 bps is not slippage. The payer never agreed to 1,834.86. They agree to 1,831.50, and that is what lands. Label the preview as an estimate and nobody feels cheated.

What does locking a price cost?

Locking a price means someone carries the market risk for the length of the lock, so longer locks cost more. Stripe's FX Quotes API makes the trade-off visible: a lock_duration of none returns an informational rate at no charge, while 5-minute, 1-hour, and 24-hour locks add a duration premium. For BRL, MXN, and COP, which sit in Stripe's second currency group, that premium is 0.12% for 5 minutes, 0.15% for 1 hour, and 0.30% for 24 hours.

Illustrative example. On a R$50,000 conversion, a 0.12% lock costs R$60 and a 0.30% lock costs R$150. The 24-hour lock costs 2.5 times the 5-minute lock for the same money. Short windows are cheap because the provider carries little risk, which is why binding quotes across the industry last minutes, not days. If you want the treasury view of longer locks, see live quote vs forward contract vs fixed rate.

When is an indicative rate enough on its own?

Whenever nobody confirms a number they expect to pay or receive. That covers:

  • Marketing pages and calculators, such as the USDC to BRL corridor page.
  • Dashboards and internal treasury estimates.
  • Reporting, where a daily reference rate is the right tool. The ECB says its euro reference rates are published for information purposes only and strongly discourages using them for transactions.

Decision rule: if a person confirms an amount, require a binding quote. If a person is only browsing, an indicative rate is fine, as long as it is labeled as an estimate. Why buy and sell prices differ in the first place is covered in why on-ramp and off-ramp rates differ, and the on-ramp integration guide shows where the payin quote sits in the full flow.

What to do next

Wire the two calls into your amount screen on a free development instance: debounce /payin-quotes/fx for the typing phase, then create the binding quote on Continue. The payin quotes reference has both requests and every field.

FAQ