---
title: "Stablecoin API quotes explained: expiry, fee direction, and minor units"
seoTitle: "Stablecoin API quotes explained"
description: "A stablecoin API quote locks the rate, fees, and amounts for a few minutes. How expiry works, which side pays the fee, and the field that flips meaning."
date: "2026-08-10"
category: "payments"
author: "BlindPay Team"
faq:
  - q: "What is a quote in a stablecoin API?"
    a: "A short-lived price lock. It fixes the exchange rate, the fees, the amount the sender pays, and the amount the recipient gets, before any money moves. You execute the payment against the quote id while it is still valid, or request a new one."
  - q: "How long does a stablecoin API quote last?"
    a: "It varies by provider and product. At BlindPay, payin, payout, and transfer quotes expire 5 minutes after creation by default. OTC Pix payin quotes expire in 10 seconds, and SEPA payout quotes can be shorter than 5 minutes. Read the expires_at field on each response instead of hardcoding a window."
  - q: "What happens if a quote expires before I execute the payment?"
    a: "The payment is rejected and nothing moves. Request a new quote, show the user the new numbers, and execute against the new id. At BlindPay, a transfer against an expired quote returns a 400 quote_expired error."
  - q: "What does currency_type mean on a BlindPay quote?"
    a: "It tells the API which side of the payment request_amount is denominated in. On a payout quote, sender means the stablecoin being sent and receiver means the fiat the bank account gets. On a payin quote the meaning flips: sender means the fiat the payer sends."
  - q: "Why are amounts integers instead of decimals?"
    a: "Stablecoin APIs usually take amounts in minor units to avoid floating point errors. At BlindPay, request_amount is an integer and does not accept floats: 10000 means 100.00. For MXN, COP, and ARS payins quoted on the sender side, the amount must also be a whole peso, a multiple of 100."
---

A stablecoin API quote is a price lock. It fixes the exchange rate, the fees, the amount the sender pays, and the amount the recipient gets, for a short window. You execute the payment against the quote's id before it expires. If it expires, nothing moves, and you ask for a new one.

Simple on paper. In practice, three fields cause most of the bugs: when the quote expires, which side pays the fee, and which currency the amount is in.

## What does a quote actually lock?

Everything the user needs to say yes. A good quote response answers four questions with numbers, not estimates:

- What rate am I getting, and what was the market rate?
- What does this cost, fee by fee?
- How much leaves the sender?
- How much arrives at the recipient?

Here's a BlindPay payout quote response, straight from the [payout quotes docs](/docs/payout-quotes):

```json
{
  "id": "qu_000000000000",
  "expires_at": 1712958191000,
  "commercial_quotation": 1,
  "blindpay_quotation": 0.998,
  "sender_amount": 10000,
  "receiver_amount": 9980,
  "partner_fee_amount": 0,
  "flat_fee": 20,
  "billing_fee_amount": null
}
```

`commercial_quotation` is the raw market rate. `blindpay_quotation` is the rate net of the fee. The gap between them is the spread, visible on every quote. `sender_amount` and `receiver_amount` are the two numbers your UI shows. EVM payout quotes also carry a `contract` object: the ERC-20 `approve` payload for wallets that sign their own transactions.

If a provider's quote gives you one rate and one total, you can't tell spread from fee. Ask for the itemized version.

## How long does a quote last, and why so short?

Because FX moves. A quote that lasted an hour would either carry a fat buffer (you pay for it) or leave the provider exposed. Minutes is the normal range.

At BlindPay the defaults are:

| Quote type | Default expiry | Exception |
| --- | --- | --- |
| Payin quote | 5 minutes | OTC Pix payin quotes: 10 seconds |
| Payout quote | 5 minutes | SEPA payout quotes can be shorter |
| Transfer quote | 5 minutes | None |

Read `expires_at` from every response. Don't hardcode 5 minutes. And note the unit: `expires_at` is epoch milliseconds, not seconds. Divide by 1,000 before passing it to a library that expects seconds, or your countdown will say the quote expires in the year 56,000.

Every rail's window is in the [cut-off times reference](/docs/kb/cut-off-times).

## What happens when a quote expires?

The execute call fails and no money moves. That's the point: an expired quote protects both sides from a stale price.

Build the UX around it:

1. Show the quote with a visible countdown driven by `expires_at`.
2. When it hits zero, disable the confirm button and fetch a new quote.
3. Show the new numbers. If they moved, say so.
4. Execute against the new id.

On an external wallet flow, there's one extra trap. If the user signs an on-chain approval and the transaction is slow to mine, the quote can expire while they wait. Quote again rather than committing an expired one.

Also: a quote backs one payment. At BlindPay, a `quote_id` can only back one payout, and a second call with the same quote returns an error. That's a useful second layer against double-sends, but it's not a substitute for proper idempotency on your retries.

## Who pays the fee: sender or recipient?

The `cover_fees` flag decides it, and the choice changes which number stays fixed.

| `cover_fees` | Who pays | What happens to the amounts |
| --- | --- | --- |
| `false` | The recipient | The fee comes off what the recipient gets. The common case. |
| `true` | The sender | The fee is added on top, so the recipient gets the full amount. |

Payroll is the classic `true` case. A company paying a contractor in Mexico wants them to receive exactly 20,000 MXN, so the company absorbs the fee. A marketplace paying out sellers usually leaves it `false` and shows the net.

## Which currency is request_amount in?

This is the one that bites. `currency_type` says which side of the payment `request_amount` refers to, and on BlindPay it means opposite things on payins and payouts:

| Quote | `currency_type: "sender"` | `currency_type: "receiver"` |
| --- | --- | --- |
| Payout quote | The stablecoin being sent | The fiat the bank account receives |
| Payin quote | The fiat the payer sends | The stablecoin the wallet receives |

Same field, same values, flipped direction. If you share one "build quote" helper across both flows, test both. A payout helper that assumes `sender` means fiat will quote the wrong side of every payment.

## Why are amounts integers?

Floating point and money don't mix. `0.1 + 0.2` isn't `0.3` in JavaScript, and you don't want to find that out in a reconciliation report. So stablecoin APIs usually take minor units.

At BlindPay, `request_amount` is an integer and doesn't accept floats. `10000` is 100.00. `500000` is 5,000.00. Two more rules worth knowing:

- **Whole pesos.** For MXN, COP, and ARS payins quoted on the sender side, the amount must be a multiple of 100. `10050` fails with `request_amount_must_be_a_whole_currency_unit`, because those rails settle in whole units.
- **Minimums.** SWIFT payouts need at least 100 USD requested. SEPA needs 11 USDC on the sender side or 10 EUR on the receiver side.

Convert at the edge of your system, once. Store minor units everywhere else.

## What should you check in a provider's quote API?

- **Is the fee itemized?** Market rate, applied rate, flat fee, and partner fee as separate fields.
- **Is the expiry machine-readable?** A timestamp on every response, not a number in the docs.
- **Can you choose who pays?** A `cover_fees` style flag, not a support ticket.
- **Can you quote either side?** "Send exactly X" and "receive exactly Y" are different products.
- **Is a quote single-use?** It should back one payment and fail loudly on reuse.

The quote is where the price gets set, so it's also where hidden costs hide. Settlement speed matters too: see [how long a stablecoin payout takes](/resources/more/stablecoin-payout-settlement-times). For where the quote sits in the full flow, see [how a stablecoin payment moves](/resources/more/how-a-stablecoin-payment-works).

## Where does BlindPay fit?

[BlindPay](/global-payments) is a stablecoin API for cross-border payouts and collections: USDC or USDT in, local currency out over Pix, SPEI, ACH, RTP, SEPA, and SWIFT (POBO/COBO) to 100+ countries, with no pre-funding. Every quote itemizes the market rate, the applied rate, and each fee, and [virtual USD accounts](/virtual-accounts) turn ACH, wire, and SWIFT deposits into stablecoins. Pricing is on the [pricing page](/pricing).

## What to do next

Create a payout quote on a free development instance and read the response field by field. Then flip `cover_fees` and `currency_type` and watch which numbers move. Ten minutes of that beats an hour of docs. Start with the [payout quotes guide](/docs/payout-quotes), then the [payin quotes guide](/docs/payin-quotes) for the flipped direction.

*This article is for general information only and is not legal, tax, or financial advice.*
