On this page
- Set up
- 1. Get a quote
- 2. Create the order
- 3. Send the deposit
- 4. Track the order
- Buy a stock token with ETH
- Sell a stock token for USDC
- Buy a stock token with Bitcoin
- Swap Bitcoin for USDC
- Buy Bitcoin with USDC
- Move USDC between chains
- Get gas on another chain
- Buy HYPE on Hyperliquid with ETH
- Withdraw USDC from Hyperliquid
- Deposit into a Morpho vault
- Move between vaults on different chains
- Things to know
Quickstart
Every trade is three requests:
POST /quoteprices the trade and returns a quoteid. A quote can be turned into an order for ten minutes, until itsexpires_at.POST /orderbinds the quote to your recipient address and returns a freshdeposit_address.GET /order/{id}tracks the order once you send the deposit.
You don't need an API key or a wallet connection. You send from_amount of the from asset to the order's deposit_address, and Rift delivers the output to your to_address.
Assets are named <chain>.<asset>, for example ethereum.eth, bitcoin.btc or base.steakhouseUSDC. The chain is always spelled out (ethereum, not eth). The token list has every registered name.
Replace the example addresses with your own before you run anything. Rift delivers to exactly the
to_addressyou set, and transfers can't be reversed.
Set up
The examples are JavaScript modules for Node.js 22 or later. They call the Rift API, https://api.rift.trade, through two helpers. Save them as rift.mjs next to the examples:
const RIFT_API = "https://api.rift.trade";
// POSTs `body` as JSON, or GETs without one, and returns the parsed response.
export async function rift(path, body) {
const response = await fetch(`${RIFT_API}${path}`, {
method: body ? "POST" : "GET",
headers: { "content-type": "application/json" },
body: body ? JSON.stringify(body) : undefined,
});
const text = await response.text();
if (!response.ok) throw new Error(`${path} failed (${response.status}): ${text}`);
return JSON.parse(text);
}
// Checks the order every 10 seconds until it settles, logging each new status.
export async function waitForOrder(id) {
const settled = ["delivered", "refunded", "expired", "frozen"];
let status;
while (true) {
const order = await rift(`/order/${id}`);
if (order.status !== status) console.log(`Order ${order.status}`);
if (settled.includes(order.status)) return order;
status = order.status;
await new Promise((resolve) => setTimeout(resolve, 10_000));
}
}The deposits are sent with viem on EVM chains and Hyperliquid, and with @scure/btc-signer on Bitcoin. Install both with npm install viem @scure/btc-signer. The paying wallet's key goes in PRIVATE_KEY (0x-prefixed hex) or, for Bitcoin, BITCOIN_WIF. Keep them in a .env file and run each script with node --env-file=.env <script>.mjs.
The Bitcoin examples pay their deposit with one more helper. It pays from the native SegWit (bc1q) wallet in BITCOIN_WIF, using the mempool.space API to find the wallet's coins, pick a fee rate and broadcast the transaction. Save it as bitcoin.mjs:
import * as btc from "@scure/btc-signer";
import { pubECDSA } from "@scure/btc-signer/utils.js";
// Pays `amount` BTC to `address` in one transaction from the wallet in BITCOIN_WIF.
export async function sendBitcoin(address, amount) {
const mempool = "https://mempool.space/api";
const getJson = (path) => fetch(mempool + path).then((res) => res.json());
const key = btc.WIF().decode(process.env.BITCOIN_WIF);
const wallet = btc.p2wpkh(pubECDSA(key));
const coins = await getJson(`/address/${wallet.address}/utxo`);
const { halfHourFee } = await getJson("/v1/fees/recommended");
const inputs = coins.map((coin) => ({
...wallet,
txid: coin.txid,
index: coin.vout,
witnessUtxo: { script: wallet.script, amount: BigInt(coin.value) },
}));
const outputs = [{ address, amount: btc.Decimal.decode(amount) }];
const payment = btc.selectUTXO(inputs, outputs, "default", {
changeAddress: wallet.address,
feePerByte: BigInt(halfHourFee),
createTx: true,
});
if (!payment) throw new Error(`Not enough BTC in ${wallet.address}`);
payment.tx.sign(key);
payment.tx.finalize();
const res = await fetch(`${mempool}/tx`, { method: "POST", body: payment.tx.hex });
if (!res.ok) throw new Error(`Broadcast failed: ${await res.text()}`);
return res.text();
}Every example below takes the same four steps, marked // 1. to // 4. in its code.
1. Get a quote
POST /quote prices the trade. For 0.5 ETH on Ethereum into Apple stock tokens on Robinhood Chain:
const quote = await rift("/quote", {
from: "ethereum.eth",
to: "robinhood.aapl",
from_amount: "0.5",
return_full_route: true,
});quote holds the response, shown here with its main fields:
{
"id": "01a0e938-82cb-72c2-9e28-109d455c5267",
"estimated_amount_out": "3.961555150436755734",
"expires_at": "2026-09-28T18:23:09.963523Z",
"route": [
{ "venue": "across", "from": "ethereum.eth", "to": "robinhood.eth" },
{ "venue": "nordstern", "from": "robinhood.eth", "to": "robinhood.aapl" }
]
}estimated_amount_out is what reaches the recipient after every venue and delivery cost. route lists the venue steps, here a bridge to Robinhood Chain followed by a swap into the stock token.
2. Create the order
POST /order binds the quote to the address that receives the output:
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourRobinhoodChainAddress", // receives the AAPL tokens
});{
"id": "01a0e938-96a9-7d92-a5cb-bab585c2fc47",
"status": "awaiting_deposit",
"from_amount": "0.5",
"deposit_address": "0x5adfc66c5e7158cbfb3b0129a8433c5da6f6382c",
"deposit_deadline": "2026-10-05T18:13:14.984542Z"
}to_address is an address on the to asset's chain: a Bitcoin address for bitcoin.btc, your Hyperliquid account for a hyperliquid asset. An optional refund_address on the from chain gets the deposit back if the trade fails. Without one, a refund goes back to the address the deposit came from.
3. Send the deposit
Send from_amount to deposit_address in one transfer before deposit_deadline, seven days after the order was created. The examples send it from the wallet in PRIVATE_KEY or BITCOIN_WIF, but any wallet or exchange withdrawal works.
Only the from asset on the from chain counts toward the order: for ethereum.usdt, send USDT on Ethereum, not ETH or USDT on another chain. A token transfer also needs a little of that chain's gas coin in the paying wallet.
4. Track the order
waitForOrder checks GET /order/{id} until the order settles. The status moves from awaiting_deposit to funded, executing and then delivered, when amount_out holds what reached the recipient. An order that can't complete is refunded. If far below from_amount arrives at the deposit address and the order can't be filled, it is underfunded.
Buy a stock token with ETH
This example sells 0.5 ETH on Ethereum for Apple stock tokens (robinhood.aapl) on Robinhood Chain. Save it as buy-aapl.mjs.
import { createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// 1. Get a quote
const quote = await rift("/quote", {
from: "ethereum.eth",
to: "robinhood.aapl",
from_amount: "0.5",
return_full_route: true,
});
console.log(`0.5 ETH -> ~${quote.estimated_amount_out} AAPL`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourRobinhoodChainAddress", // receives the AAPL tokens
});
// 3. Send the deposit, 0.5 ETH on Ethereum
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: mainnet,
transport: http(),
});
const hash = await wallet.sendTransaction({
to: order.deposit_address,
value: parseEther(order.from_amount),
});
console.log(`Deposit sent: https://etherscan.io/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} AAPL`);Sell a stock token for USDC
The way back out: this script sells 5 NVIDIA stock tokens (robinhood.nvda) on Robinhood Chain for USDC on Base. The deposit is a token transfer on Robinhood Chain, so the wallet also needs a little ETH there for gas.
import { createWalletClient, erc20Abi, http, parseUnits } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { robinhood } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// NVDA on Robinhood Chain, which has 18 decimals
const NVDA = "0xd0601CE157Db5bdC3162BbaC2a2C8aF5320D9EEC";
// 1. Get a quote
const quote = await rift("/quote", {
from: "robinhood.nvda",
to: "base.usdc",
from_amount: "5",
return_full_route: true,
});
console.log(`5 NVDA -> ~${quote.estimated_amount_out} USDC`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourBaseAddress", // receives the USDC
});
// 3. Send the deposit, an NVDA transfer on Robinhood Chain
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: robinhood,
transport: http(),
});
const hash = await wallet.writeContract({
address: NVDA,
abi: erc20Abi,
functionName: "transfer",
args: [order.deposit_address, parseUnits(order.from_amount, 18)],
});
console.log(`Deposit sent: https://robinhoodchain.blockscout.com/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} USDC`);Buy a stock token with Bitcoin
This script sells 0.01 BTC for NVIDIA stock tokens on Robinhood Chain, paying with sendBitcoin from bitcoin.mjs. The route passes through Hyperliquid, and the order starts once the Bitcoin deposit confirms, so expect it to take 30 to 60 minutes.
import { sendBitcoin } from "./bitcoin.mjs";
import { rift, waitForOrder } from "./rift.mjs";
// 1. Get a quote
const quote = await rift("/quote", {
from: "bitcoin.btc",
to: "robinhood.nvda",
from_amount: "0.01",
return_full_route: true,
});
const venues = quote.route.map((step) => step.venue).join(" -> ");
console.log(`0.01 BTC -> ~${quote.estimated_amount_out} NVDA via ${venues}`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourRobinhoodChainAddress", // receives the NVDA tokens
refund_address: "bc1qYourBitcoinAddress", // gets the BTC back if the trade fails
});
// 3. Send the deposit
const txid = await sendBitcoin(order.deposit_address, order.from_amount);
console.log(`Deposit sent: https://mempool.space/tx/${txid}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} NVDA`);Swap Bitcoin for USDC
This script quotes 0.05 BTC into USDC on Ethereum and creates the order with a Bitcoin refund address. It pays the deposit with sendBitcoin from bitcoin.mjs, then waits for the order to settle.
import { sendBitcoin } from "./bitcoin.mjs";
import { rift, waitForOrder } from "./rift.mjs";
// 1. Get a quote
const quote = await rift("/quote", {
from: "bitcoin.btc",
to: "ethereum.usdc",
from_amount: "0.05",
return_full_route: true,
});
console.log(`0.05 BTC -> ~${quote.estimated_amount_out} USDC`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourEthereumAddress", // receives the USDC
refund_address: "bc1qYourBitcoinAddress", // gets the BTC back if the trade fails
});
// 3. Send the deposit
const txid = await sendBitcoin(order.deposit_address, order.from_amount);
console.log(`Deposit sent: https://mempool.space/tx/${txid}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} USDC`);The deposit address is a regular Bitcoin address. To let someone pay from their own wallet instead, show them the payment URI bitcoin:<deposit_address>?amount=<from_amount> as a link or QR code. Without a refund_address, a refund goes back to the address the deposit came from. That address may not belong to you when the BTC comes from an exchange.
Buy Bitcoin with USDC
This script sells 1,000 USDC on Ethereum for native BTC, delivered to a Bitcoin address. The deposit is a USDC transfer, so the wallet in PRIVATE_KEY also needs a little ETH for gas.
import { createWalletClient, erc20Abi, http, parseUnits } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// USDC on Ethereum, which has 6 decimals
const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48";
// 1. Get a quote
const quote = await rift("/quote", {
from: "ethereum.usdc",
to: "bitcoin.btc",
from_amount: "1000",
return_full_route: true,
});
console.log(`1000 USDC -> ~${quote.estimated_amount_out} BTC`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "bc1qYourBitcoinAddress", // receives the BTC
});
// 3. Send the deposit, a USDC transfer on Ethereum
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: mainnet,
transport: http(),
});
const hash = await wallet.writeContract({
address: USDC,
abi: erc20Abi,
functionName: "transfer",
args: [order.deposit_address, parseUnits(order.from_amount, 6)],
});
console.log(`Deposit sent: https://etherscan.io/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} BTC`);Move USDC between chains
This script moves 1,000 USDC from Base to Ethereum. Any token works the same way: change from and to, and the token address and decimals of the deposit.
import { createWalletClient, erc20Abi, http, parseUnits } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// USDC on Base, which has 6 decimals
const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
// 1. Get a quote
const quote = await rift("/quote", {
from: "base.usdc",
to: "ethereum.usdc",
from_amount: "1000",
return_full_route: true,
});
console.log(`1000 USDC on Base -> ~${quote.estimated_amount_out} USDC on Ethereum`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourEthereumAddress", // receives the USDC
});
// 3. Send the deposit, a USDC transfer on Base
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: base,
transport: http(),
});
const hash = await wallet.writeContract({
address: USDC,
abi: erc20Abi,
functionName: "transfer",
args: [order.deposit_address, parseUnits(order.from_amount, 6)],
});
console.log(`Deposit sent: https://basescan.org/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} USDC on Ethereum`);Get gas on another chain
This script turns 20 USDC on Base into ETH on Arbitrum, to pay for transactions there. Any chain's gas coin works as the target, such as ethereum.eth, ink.eth or robinhood.eth.
import { createWalletClient, erc20Abi, http, parseUnits } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// USDC on Base, which has 6 decimals
const USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
// 1. Get a quote
const quote = await rift("/quote", {
from: "base.usdc",
to: "arbitrum.eth",
from_amount: "20",
return_full_route: true,
});
console.log(`20 USDC -> ~${quote.estimated_amount_out} ETH on Arbitrum`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourArbitrumAddress", // receives the ETH
});
// 3. Send the deposit, a USDC transfer on Base
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: base,
transport: http(),
});
const hash = await wallet.writeContract({
address: USDC,
abi: erc20Abi,
functionName: "transfer",
args: [order.deposit_address, parseUnits(order.from_amount, 6)],
});
console.log(`Deposit sent: https://basescan.org/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} ETH on Arbitrum`);Buy HYPE on Hyperliquid with ETH
This script sells 0.5 ETH on Ethereum for HYPE, delivered to the spot balance of a Hyperliquid account. to_address is the account's address.
import { createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// 1. Get a quote
const quote = await rift("/quote", {
from: "ethereum.eth",
to: "hyperliquid.hype",
from_amount: "0.5",
return_full_route: true,
});
console.log(`0.5 ETH -> ~${quote.estimated_amount_out} HYPE`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourHyperliquidAddress", // receives the HYPE
});
// 3. Send the deposit, 0.5 ETH on Ethereum
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: mainnet,
transport: http(),
});
const hash = await wallet.sendTransaction({
to: order.deposit_address,
value: parseEther(order.from_amount),
});
console.log(`Deposit sent: https://etherscan.io/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} HYPE`);Withdraw USDC from Hyperliquid
This script moves 1,000 USDC from a Hyperliquid spot balance to USDC on Base. On Hyperliquid the deposit isn't a transaction: it's a spot transfer that the account signs with the key in PRIVATE_KEY and sends to Hyperliquid's API. Hyperliquid charges the sender 1 USDC for a transfer to a new address, and every deposit address is new, so keep at least 1 USDC more than from_amount in the account.
import { parseSignature } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { rift, waitForOrder } from "./rift.mjs";
// 1. Get a quote
const quote = await rift("/quote", {
from: "hyperliquid.usdc",
to: "base.usdc",
from_amount: "1000",
return_full_route: true,
});
console.log(`1000 USDC on Hyperliquid -> ~${quote.estimated_amount_out} USDC on Base`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourBaseAddress", // receives the USDC
});
// 3. Send the deposit, a USDC spot transfer on Hyperliquid
const account = privateKeyToAccount(process.env.PRIVATE_KEY);
const time = Date.now();
const transfer = {
hyperliquidChain: "Mainnet",
destination: order.deposit_address.toLowerCase(),
token: "USDC:0x6d1e7cde53ba9467b783cb7c530ce054",
amount: order.from_amount,
time: BigInt(time),
};
const signature = await account.signTypedData({
domain: {
name: "HyperliquidSignTransaction",
version: "1",
chainId: 42161,
verifyingContract: "0x0000000000000000000000000000000000000000",
},
types: {
"HyperliquidTransaction:SpotSend": [
{ name: "hyperliquidChain", type: "string" },
{ name: "destination", type: "string" },
{ name: "token", type: "string" },
{ name: "amount", type: "string" },
{ name: "time", type: "uint64" },
],
},
primaryType: "HyperliquidTransaction:SpotSend",
message: transfer,
});
const { r, s, v } = parseSignature(signature);
const response = await fetch("https://api.hyperliquid.xyz/exchange", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
action: { type: "spotSend", signatureChainId: "0xa4b1", ...transfer, time },
nonce: time,
signature: { r, s, v: Number(v) },
}),
});
const result = await response.json();
if (result.status !== "ok") throw new Error(`Hyperliquid refused the transfer: ${JSON.stringify(result)}`);
console.log("Deposit sent");
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} USDC on Base`);Deposit into a Morpho vault
This script sells 5,000 USDT on Ethereum for shares of the Steakhouse USDC Morpho vault on Base (base.steakhouseUSDC). Vault shares are just another asset to the API. The router finds the path to USDC on Base, deposits into the vault, and delivers the shares to the recipient. The deposit is a USDT transfer, so the wallet in PRIVATE_KEY also needs a little ETH for gas.
import { createWalletClient, erc20Abi, http, parseUnits } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// USDT on Ethereum, which has 6 decimals
const USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7";
// 1. Get a quote
const quote = await rift("/quote", {
from: "ethereum.usdt",
to: "base.steakhouseUSDC",
from_amount: "5000",
return_full_route: true,
});
const venues = quote.route.map((step) => step.venue).join(" -> ");
console.log(`5000 USDT -> ~${quote.estimated_amount_out} steakUSDC via ${venues}`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourBaseAddress", // receives the vault shares
});
// 3. Send the deposit, a USDT transfer on Ethereum
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: mainnet,
transport: http(),
});
const hash = await wallet.writeContract({
address: USDT,
abi: erc20Abi,
functionName: "transfer",
args: [order.deposit_address, parseUnits(order.from_amount, 6)],
});
console.log(`Deposit sent: https://etherscan.io/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} steakUSDC`);Move between vaults on different chains
This script moves 1,000 shares of the Steakhouse USDC High Yield vault on Ethereum (ethereum.steakhouseUSDCHighYield) into the Gauntlet USDC Prime vault on Base (base.gauntletUSDCPrimeV2) in one order. The router redeems the shares, bridges the USDC to Base and deposits it into the new vault. The deposit is a transfer of the vault shares, so the wallet also needs a little ETH for gas.
import { createWalletClient, erc20Abi, http, parseUnits } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { mainnet } from "viem/chains";
import { rift, waitForOrder } from "./rift.mjs";
// Steakhouse USDC High Yield vault shares (bbqUSDC) on Ethereum, which have 18 decimals
const BBQ_USDC = "0xbeeff2C5bF38f90e3482a8b19F12E5a6D2FCa757";
// 1. Get a quote
const quote = await rift("/quote", {
from: "ethereum.steakhouseUSDCHighYield",
to: "base.gauntletUSDCPrimeV2",
from_amount: "1000",
return_full_route: true,
});
const venues = quote.route.map((step) => step.venue).join(" -> ");
console.log(`1000 bbqUSDC -> ~${quote.estimated_amount_out} gtUSDCp via ${venues}`);
// 2. Create the order
const order = await rift("/order", {
quote_id: quote.id,
to_address: "0xYourBaseAddress", // receives the Gauntlet vault shares
});
// 3. Send the deposit, a transfer of the vault shares on Ethereum
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY),
chain: mainnet,
transport: http(),
});
const hash = await wallet.writeContract({
address: BBQ_USDC,
abi: erc20Abi,
functionName: "transfer",
args: [order.deposit_address, parseUnits(order.from_amount, 18)],
});
console.log(`Deposit sent: https://etherscan.io/tx/${hash}`);
// 4. Track the order
const { status, amount_out } = await waitForOrder(order.id);
if (status === "delivered") console.log(`Delivered ${amount_out} gtUSDCp`);Things to know
- Quotes expire. Create the order before
expires_at, ten minutes after quoting. An expired quote returns410, so quote again. - A quote binds to one order. Repeating
POST /orderwith the same quote and addresses returns the same order. Other addresses return409. - Deposit amount. You can send more or less than
from_amount, and Rift uses everything you send to complete the order. The rate may differ from the quote when the amount is significantly different from what was quoted. return_full_routeis required. A quote request without it returns422. Set it tofalseto leaverouteout of the response.- Quote speed. The default
optimalmode gives every venue up to ten seconds. Set"quote_mode": "fast"for a 1.5-second cutoff. - Rate limits. The default rate limit is 1 request per second. If you need a key with a higher limit, contact the Rift team using Support.
- No route. When no venue can quote the pair and amount,
POST /quotereturns422with anerrorexplaining why. - Stock token hours. Certain tokenized stocks are only liquid 24/5. Most venues don't quote them during off hours.
- Attribution. Add
"integrator_id": "your-app"to the quote request. The ID is echoed in the quote and carried onto the order. - Polling. Check the order every few seconds and back off on errors. Stop at
delivered,refunded,expiredorfrozen. See get order status for every status.

