Quotes
Soft and firm quotes: request fields, responses, safe retries and common failures.
A quote prices one trade: token_in to token_out, for amount of token_in, on chain_id. Ask for a soft quote to show a price, and a firm quote when the user is ready to trade.
Request fields
| Field | Required | Meaning |
|---|---|---|
token_in | Yes | Address of the token the user sends |
token_out | Yes | Address of the token the user receives |
amount | Yes | Raw base-unit quantity of token_in, as a string. Convert with token_in.decimals from discovery |
chain_id | Yes | The chain both token addresses live on |
slippage_bps | No | Slippage tolerance in basis points. When omitted, Rave applies the route default and echoes it. Set 0 only for an exact binary fill |
recipient | Firm only | Your wallet address: the output tokens settle there |
quote_attempt_id | No, firm only | Your own UUID for safe retries |
source | No | Application label, such as a wallet or dapp name. Send a string of at most 64 characters; labels are trimmed and case-insensitive |
end_user | No | Address of the wallet that will trade, which can differ from the output recipient |
We recommend sending source and end_user with every request. They help us serve your users' real trades reliably when demand is high, which means more firm quotes and better execution for your flow.
source and end_user are optional metadata on both soft and firm requests. They are not price inputs, are not echoed in the quote, and are excluded from the quote_attempt_id identity. Adding or changing them on an otherwise identical retry does not create a conflict. Non-string values are ignored; omitting either field is safe. end_user never changes where tokens settle: the output still goes to recipient.
There is no side field: the direction is token_in to token_out. Do not send V1 fields (asset, side, quote_asset); they return 422.
Soft quote
curl -s "$RAVE_API/v2/quotes/soft" \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"token_in": "0x55d398326f99059ff775485246999027b3197955",
"token_out": "0x390a684ef9cade28a7ad0dfa61ab1eb3842618c4",
"amount": "100000000000000000000",
"chain_id": 56,
"slippage_bps": 50
}
JSON{
"type": "soft_quote",
"schema_version": 2,
"quote_id": "64ec0a6f-8d98-499f-a1b2-090dd2967dc5",
"token_in": { "symbol": "USDT", "address": "0x55d398326f99059ff775485246999027b3197955", "decimals": 18 },
"token_out": { "symbol": "AAPLon", "address": "0x390a684ef9cade28a7ad0dfa61ab1eb3842618c4", "decimals": 18 },
"amount_in": "100000000000000000000",
"amount_out": "292558761888100307",
"price": "341.81167350663256234402428564",
"expires": "2026-09-26T11:09:47.855097475Z",
"valid_for_secs": 60,
"slippage_bps": 50,
"chain_id": 56
}The pair is an example; take token_in, token_out and chain_id from discovery. amount_in and amount_out are raw base units; price is a decimal string. Soft-quote IDs are not stored, so GET /v2/quotes/{quote_id} returns 404 for them.
Firm quote
Same body, plus recipient. Complete the approval first. The API cannot tell a typo or a burn address from a real wallet, and tokens sent to the wrong address cannot be recovered.
curl -s "$RAVE_API/v2/quotes/firm" \
-H "Authorization: Bearer ***" \
-H "Content-Type: application/json" \
--data @- <<'JSON'
{
"token_in": "0x55d398326f99059ff775485246999027b3197955",
"token_out": "0x390a684ef9cade28a7ad0dfa61ab1eb3842618c4",
"amount": "100000000000000000000",
"chain_id": 56,
"slippage_bps": 50,
"recipient": "YOUR_WALLET_ADDRESS"
}
JSONThe response adds execution.transaction:
{
"type": "firm_quote",
"schema_version": 2,
"quote_id": "550e8400-e29b-41d4-a716-446655440000",
"token_in": { "symbol": "USDT", "address": "0x55d398326f99059ff775485246999027b3197955", "decimals": 18 },
"token_out": { "symbol": "AAPLon", "address": "0x390a684ef9cade28a7ad0dfa61ab1eb3842618c4", "decimals": 18 },
"amount_in": "100000000000000000000",
"amount_out": "292558761888100307",
"price": "341.81167350663256234402428564",
"expires": "2026-09-26T11:10:17Z",
"valid_for_secs": 30,
"slippage_bps": 50,
"chain_id": 56,
"execution": {
"safety_label": "execution-preparatory",
"transaction": { "to": "0xa53B869C883B5036dDf8D7a12B7618e4612C503e", "data": "0x...", "value": "0", "chain_id": 56 }
}
}Submit execution.transaction unchanged before expires. If the user changes token_in, amount, recipient, chain or slippage, request a new firm quote. Firm quotes can also carry input_capability; treat it as optional (see Variable Input).
Expiry
Read valid_for_secs and expires on every quote instead of assuming a fixed window. Firm quotes are short-lived, typically 30 seconds. A packet submitted after expires reverts.
Safe retries with quote_attempt_id
quote_attempt_idSend your own UUID as quote_attempt_id on a firm request. An exact retry with the same body and ID returns the same unexpired quote instead of a new one, provided current eligibility still permits it. A later eligibility change or temporary control outage can withhold that replay; it does not cancel a packet already issued. See temporary quoting-control outages and reconcile any possible broadcast before requesting another trade. The same ID with a changed body returns 409. Use a new UUID for each new attempt.
Common failures
| Status | Example body | What to do |
|---|---|---|
400 | { "error": "amount must be positive" } | Fix the value |
400 | { "error": "recipient required for firm quotes" } | Add recipient |
404 | { "error": "asset not found for pair 0x.../0x..." } | The pair is not published on that chain. Re-read levels |
409 | Reused quote_attempt_id, or capacity exhausted. New ID, or back off | |
422 | { "error": "invalid quote request" } | Body does not match the schema, for example a number instead of a string for amount |
503 | { "error": "price-stream depth does not cover requested amount for 0x.../0x... on chain 56" } | Size is above current depth, or quoting is paused. Re-read limits and levels, reduce size |
The full table is in Errors.
Updated 14 days ago