Quote
Price a BRL to asset conversion, with the full fee breakdown, before you run an on-ramp or off-ramp.
POST /api/quote
Returns a price + itemised fee breakdown for a pair, so you can show the user what they will get before they commit.
A quote is pricing, not a rate lock. For a PIX payout there is now one that is:
POST /api/wallet/payout/beneficiaryresolves the payee and returns aquoteIdthat/api/wallet/payoutsettles against. This endpoint prices any pair and locks nothing. No other endpoint accepts aquoteTokentoday —/api/deposit/assetand/api/wallet/payoutboth price at execution time. TreatoutputAmountas indicative, quote immediately before executing, and show the user the amount the operation response returns as the final one.
expiresAtis set 2 minutes after creation. It marks when the displayed price is stale enough that you should re-quote — it does not reserve anything.
Request
curl --request POST \
--url https://api.hodle.com.br/api/quote \
--header "Authorization: Bearer $API_KEY" \
--header "Content-Type: application/json" \
--data '{
"inputCurrency": "BRL",
"inputPaymentMethod": "PIX",
"outputCurrency": "USDC",
"outputPaymentMethod": "BASE",
"inputAmount": "100.00"
}'const res = await fetch('https://api.hodle.com.br/api/quote', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.HODLE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
inputCurrency: 'BRL',
inputPaymentMethod: 'PIX',
outputCurrency: 'USDC',
outputPaymentMethod: 'BASE',
inputAmount: '100.00',
}),
})
const data = await res.json()import os, requests
res = requests.post(
"https://api.hodle.com.br/api/quote",
headers={
"Authorization": f"Bearer {os.environ['HODLE_API_KEY']}",
"Content-Type": "application/json",
},
json={
"inputCurrency": "BRL",
"inputPaymentMethod": "PIX",
"outputCurrency": "USDC",
"outputPaymentMethod": "BASE",
"inputAmount": "100.00",
},
)
data = res.json()Parameters
| Field | Type | Required | Description |
|---|---|---|---|
inputCurrency | string | Yes | BRL, USDT, USDC, USDB, BTC, BRS. |
inputPaymentMethod | string | Yes | PIX, POLYGON, BASE, TRON, LIGHTNING, SOLANA, SPARK. |
outputCurrency | string | Yes | Same set as inputCurrency. |
outputPaymentMethod | string | Yes | Same set as inputPaymentMethod. |
inputAmount | string | Yes (or outputAmount) | Amount in inputCurrency. Decimal string. |
outputAmount | string | Yes (or inputAmount) | Amount in outputCurrency. Use this for exact-out quotes. |
outputBrCode | string | No | PIX BR Code, when the output goes to a known PIX recipient. Used to route and price this quote. It carries no third-party permission — third-party operations are authorised per account, see Third-party operations. |
address | string | No | BRL → BRS only. The Solana address the BRS will be delivered to. An address outside the account adds the deliveryFee block (external delivery must be enabled on the account — ask support). |
Pass exactly one of inputAmount or outputAmount.
Response
{
"success": true,
"data": {
"quoteToken": "qt_8f3a4b...",
"pairName": "BRL-USDC",
"basePrice": "0.18634",
"inputCurrency": "BRL",
"outputCurrency": "USDC",
"inputAmount": "100.00",
"outputAmount": "18.41",
"appliedFees": [
{ "type": "MARKUP", "amount": "0.20", "currency": "USDC" },
{ "type": "INPUT_FIXED", "amount": "0.50", "currency": "BRL" },
{ "type": "OUTPUT_FIXED", "amount": "0.05", "currency": "USDC" }
],
"expiresAt": "2026-05-09T22:02:00.000Z"
}
}| Field | Type | Description |
|---|---|---|
data.quoteToken | string | Provider-side reference, returned for traceability. No Hodle endpoint accepts it as input — it does not lock the rate. |
data.basePrice | string | Mid-market outputCurrency per 1 unit of inputCurrency before fees. |
data.outputAmount | string | What the user actually receives after fees are applied. |
data.inputAmountSats | integer | BTC sats required for a BTC → BRL exact-out quote. Present only for this route. |
data.outputAmountSats | integer | BTC sats delivered by a BRL → BTC quote. Present only for this route. |
data.feeInCents | integer | Total fee charged to the authenticated API-key owner, after its custom fee override and floor. |
data.appliedFees | array | Itemised — your UI can render each line. |
data.expiresAt | string | ISO timestamp; after this, re-quote. |
data.settlement | object | Present for composed Solana and Flashnet routes; identifies the priced settlement path. |
Solana quotes
SOLANA is routable with USDT, USDC and BRS; any other currency on that
payment method answers 400 "No route ... at this time". BRL on Solana is
BRS — send outputCurrency: "BRS", not BRL.
BRS
BRS is quoted against BRL over Pix in both directions, at basePrice 1:
| Pair | Amount field | Meaning |
|---|---|---|
BRL/PIX → BRS/SOLANA | inputAmount | The Pix charge; outputAmount is the BRS minted after fees. |
BRS/SOLANA → BRL/PIX | outputAmount | The Pix payout; inputAmount is the BRS debited, fee included. |
The account needs BRS enabled (403 otherwise) and the minimum is R$5.00. BRS settles on the
account's own Hodle Solana wallet unless you send address on a BRL → BRS quote: when
external delivery is enabled on the account, an external Solana address is priced with its
delivery fee (the total in BRS, paid from the account's own Hodle wallet), without signing or
debiting anything. Otherwise an external address answers 403 — ask support to enable it. See
BRS to an external Solana wallet.
{
"success": true,
"data": {
"pairName": "BRLBRS",
"inputAmount": "100.00",
"outputAmount": "100.00",
"feeInCents": 0,
"settlement": {
"deliveryNetwork": "SOLANA",
"destination": "EXTERNAL_SOLANA_WALLET"
},
"deliveryFee": {
"feeCents": 144,
"recipientHasTokenAccount": false,
"paidIn": "BRS",
"paidFrom": "OWN_HODLE_SOLANA_WALLET"
}
}
}A BRS → BRL quote prices from where the wallet's BRS came from, which is read in
the background. The request waits up to ~5 seconds for that read and normally
answers 200. Only if it takes longer does it answer 202 {"success": true, "status": "PENDING", "pricingPending": true}; retry the same request after a
second or two.
{
"success": true,
"data": {
"pairName": "BRLBRS",
"basePrice": "1",
"inputAmount": "100.00",
"outputAmount": "99.00",
"feeInCents": 100,
"appliedFees": [{ "type": "MARKUP_FEE", "amount": "1.00", "currency": "BRL" }],
"settlement": {
"provider": "NORA",
"deliveryNetwork": "SOLANA",
"destination": "OWN_HODLE_SOLANA_WALLET"
}
}
}A Solana quote is priced on the Base leg, because that is where the money actually lands before it moves: a Pix on-ramp settles USDC on Base and Relay then bridges it to the destination Solana address. The response says so explicitly:
{
"success": true,
"data": {
"outputAmount": "18.41",
"settlement": {
"quotedNetwork": "BASE",
"deliveryNetwork": "SOLANA",
"bridge": "relay",
"bridgeFeeIncluded": false
}
}
}bridgeFeeIncluded: false is the part to plan around — the Relay bridge cost is
charged on delivery, so the amount that lands on Solana is marginally below
outputAmount. Do not treat a Solana quote as an exact-out guarantee.
BRL ↔ BTC quotes
BTC quotes use the same Flashnet/Orchestra swap route as settlement and price
the fee of the user that owns the API key. This matters for accounts with a
custom LIGHTNING percentage, fixed fee, or minimum fee.
- BRL → BTC is an exact-input quote: send
inputAmountin BRL withPIX→LIGHTNINGorSPARK. The response returnsoutputAmountSats. - BTC → BRL is an exact-output quote: send
outputAmountin BRL withLIGHTNINGorSPARK→PIX. The response returnsinputAmountSats, the exact invoice amount to request from the payer.
{
"inputCurrency": "BRL",
"inputPaymentMethod": "PIX",
"outputCurrency": "BTC",
"outputPaymentMethod": "LIGHTNING",
"inputAmount": "21.00"
}inputAmount is the gross Pix amount, matching deposit/asset.value. The
quoted sats are calculated from the amount left after feeInCents; for the
Passbit fee of 1.5% with a R$ 1 minimum, R$ 20 is converted on a R$ 21 charge.
A R$ 10 Passbit simulation is rejected with
400 "Valor mínimo para compra de BTC é R$ 20". This is intentional: the
matching BTC/Lightning deposit rail has the same minimum, so the quote endpoint
does not promise sats for a charge that cannot settle.
{
"inputCurrency": "BTC",
"inputPaymentMethod": "LIGHTNING",
"outputCurrency": "BRL",
"outputPaymentMethod": "PIX",
"outputAmount": "100.00"
}Errors
{ "success": false, "error": "No route for BRL-USDC at this time" }{ "success": false, "error": "inputAmount must be at least 1.00 BRL" }There is no "expired quote" error, because nothing consumes the quote. Once expiresAt has passed, call /api/quote again.
Common quote shapes
| Use case | Input | Output |
|---|---|---|
| BRL → USDC (Base) on-ramp | BRL / PIX | USDC / BASE |
| BRL → USDT (Polygon) on-ramp | BRL / PIX | USDT / POLYGON |
| BRL → USDC (Solana) on-ramp | BRL / PIX | USDC / SOLANA |
| USDC (Solana) → BRL off-ramp | USDC / SOLANA | BRL / PIX |
| USDT (Tron) → BRL off-ramp | USDT / TRON | BRL / PIX |
| BRL → BTC (Lightning) on-ramp | BRL / PIX | BTC / LIGHTNING |
| Lightning → BRL off-ramp | BTC / LIGHTNING | BRL / PIX |
| Lightning → USDB (Spark via Flashnet AMM) | BTC / LIGHTNING | USDB / SPARK |