Direct Swaps Integration Guide
This guide walks you through integrating Direct Swaps.
Overview
Direct Swaps follow this order creation flow:
- Get the estimated amount you will receive (using rates or quotes)
- Create an order
- Send funds to the provider address
- Update and track the order status
Pair Availability
Important: Direct Swaps only support pairs that are explicitly returned by the
/pairs endpoint.
To find available pairs for Direct Swaps:
- Call
GET /v3/pairsto retrieve all available pairs - Use the
pairIdfrom the response (e.g.,BTC_ETH) to create your swap - Only pairs returned by this endpoint can be used with Direct Swaps
Example:
// Get all available pairs for Direct Swaps
const pairs = await fetch('https://exchange.exodus.io/v3/pairs', {
headers: {
'App-Name': 'acme-inc',
},
}).then((res) => res.json());
// Use one of the returned pairIds (e.g., pairs[0].id)
const pairId = pairs[0].id;Restriction: If a pair is not returned by the /pairs endpoint, it cannot be used for Direct Swaps. Use Indirect Swaps for more flexible pair combinations.
Rates vs Quotes
Direct Swaps support two methods to get the estimated output amount: rates and quotes. Select the path best aligned with your needs.
Rates
Best for: Simpler integrations with guaranteed output amounts.
- Guaranteed output — You get exactly what is expected
- Static rates — The amount is fixed, so you can calculate the exact amount the user will receive without additional API calls
- Lower maximums — Maximums are not that high, usually less than 100,000 USD
- Low Latency — API response is fast
Quotes
Best for: Larger swaps with potentially better exchange rates.
- Higher maximums — Supports larger swap amounts
- Better amounts — Usually offers slightly better amount than fixed rates
- Ouput based on input amount — Every quote is for an input amount; new amount requires getting a new quote
- Floating amount — You could receive a slightly different amount than the original, depending on the slippage
Quick Decision Guide
| Criteria | Use Rates | Use Quotes |
|---|---|---|
| Swap volume | Under $100,000 USD | Over $100,000 USD |
| Amount certainty | Guaranteed output | May vary with slippage |
| Implementation | Simpler (no need to do extra API calls) | API call if user changes the amount |
Recommendation: If you’re unsure which to use, start with rates for most use cases. Switch to quotes only if you need to support larger swap amounts or want slightly better rates.
Prerequisites
Before starting, make sure you have:
- Identified the pair you want to swap
- Set up the proper authorization headers
Step-by-Step Integration
Step 1: Estimate the amount that you will receive
Choose between rates or quotes based on your needs:
Option A: Rates
Use GET /v3/pairs/:pairId/rates to get multiple rates.
The endpoint returns multiple rates sorted by best rate. Each rate includes:
- Exchange rate multiplier (
rate.amount.value) - Miner fee (
rate.minerFee.value) - Minimum and maximum amounts (
rate.min.value,rate.max.value) - Expiry time
Calculate the best output amount:
const rates = await fetch(`https://exchange.exodus.io/v3/pairs/BTC_ETH/rates`, {
headers: {
'App-Name': 'acme-inc',
},
}).then((res) => res.json());
const bestOutputAmount = rates.reduce((acc, rate) => {
const rateMin = rate.min.value;
const rateMax = rate.max.value;
// Ensure the input amount is within the rate's min and max values.
if (fromAmount >= rateMin && fromAmount <= rateMax) {
const outputAmount = fromAmount * rate.amount.value - rate.minerFee.value;
if (!acc || outputAmount > acc) {
// Select the rate with highest output amount.
return outputAmount;
}
}
return acc;
}, null);Option B: Quotes
Use GET /v3/pairs/:pairId/quotes?amount=1 to get a quote for a specific amount.
Get quote for user’s amount:
const quote = await fetch(
`https://exchange.exodus.io/v3/pairs/BTC_ETH/quotes?amount=${userAmount}`,
{
headers: {
'App-Name': 'acme-inc',
},
}
).then((res) => res.json());
// Use quote.toAmount.value as the output amount
const toAmount = quote.toAmount.value;Step 2: Create an Order
Once you have the output amount, create an order using the appropriate endpoint:
- For rates: Use
POST /v3/orders - For quotes: Use
POST /v3/orders/float
Required parameters:
pairId- The pair (e.g.,BTC_ETH)fromAmount- Amount to swapfromAddress- User’s sending address. Make sure this address can receive funds in case it needs to be refundedtoAddress- User’s receiving addresstoAmount- Expected output amount (from rate or quote)
Response includes:
orderId- Use this to track and update the orderpayInAddress- Address where user should send funds
Step 3: Send Funds to Provider Address
The user needs to send the fromAmount to the payInAddress provided in the order response.
Note that the payInAddress will always be in the same network as the from asset.
This is a standard blockchain transaction on the source asset’s network. For example:
- If swapping BTC → ETH, send a Bitcoin transaction
- If swapping SOL → USDC, send a Solana transaction
Important: Save the transaction ID - you’ll need it in the next step.
Step 4: Update the Order
After the user sends the funds, update the order with the transaction ID using PATCH /v3/orders/:orderId.
This starts the verification process and allows the provider to:
- Confirm receipt of funds
- Begin processing the swap
- Send the output funds to the user
Step 5: Check Order Status
Poll the order status using GET /v3/orders/:orderId to track progress.
Order statuses:
inProgress- Swap in progresscomplete- Swap successfulfailed- Swap failed (checkmessagefield)expired- Order expired before completionrefunded- Order was refundeddelayed- Order is delayed
Continue polling until the status is complete, failed, expired, or refunded.
Complete Examples
Rates Example
// 1. Get rates
const rates = await fetch(`https://exchange.exodus.io/v3/pairs/BTC_ETH/rates`, {
headers: {
'App-Name': 'acme-inc',
},
}).then((res) => res.json());
// 2. Select best rate
const userAmount = 0.002;
const bestRate = rates.find(
(rate) => userAmount >= rate.min.value && userAmount <= rate.max.value && rate.expiry > Date.now()
);
// 3. Calculate output
const toAmount = userAmount * bestRate.amount.value - bestRate.minerFee.value;
// 4. Create Fixed Order
const order = await fetch('https://exchange.exodus.io/v3/orders', {
method: 'POST',
headers: {
'App-Name': 'acme-inc',
'Content-Type': 'application/json',
},
body: JSON.stringify({
pairId: 'BTC_ETH',
fromAmount: userAmount,
fromAddress: 'user-btc-address',
toAddress: 'user-eth-address',
toAmount: toAmount,
}),
}).then((res) => res.json());
// 5. User sends funds to order.payInAddress
// This is a user action - they need to send the fromAmount to the payInAddress
// using their wallet. Save the transaction ID/hash from this transaction.
// 6. Update order with transaction ID
// After the user sends funds, update the order with the transaction ID
// This starts the verification process
await fetch(`https://exchange.exodus.io/v3/orders/${order.id}`, {
method: 'PATCH',
headers: {
'App-Name': 'acme-inc',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fromTransactionId: 'user-transaction-id',
}),
});
// 7. Poll order status
const checkStatus = async () => {
const updatedOrder = await fetch(`https://exchange.exodus.io/v3/orders/${order.id}`, {
headers: {
'App-Name': 'acme-inc',
},
}).then((res) => res.json());
if (updatedOrder.status === 'complete') {
console.log('Swap completed!');
} else if (updatedOrder.status === 'failed' || updatedOrder.status === 'expired') {
console.error('Swap failed:', updatedOrder.message);
} else {
// Still in progress (inProgress, delayed), check again later
setTimeout(checkStatus, 5000);
}
};
checkStatus();Quotes Example
// 1. Get quote for specific amount
const userAmount = 1.5; // Larger amount
const quote = await fetch(
`https://exchange.exodus.io/v3/pairs/BTC_ETH/quotes?amount=${userAmount}`,
{
headers: {
'App-Name': 'acme-inc',
},
}
).then((res) => res.json());
// 2. Use quote output amount
const toAmount = quote.toAmount.value;
// 3. Create Floating Order
const order = await fetch('https://exchange.exodus.io/v3/orders/float', {
method: 'POST',
headers: {
'App-Name': 'acme-inc',
'Content-Type': 'application/json',
},
body: JSON.stringify({
pairId: 'BTC_ETH',
fromAmount: userAmount,
fromAddress: 'user-btc-address',
toAddress: 'user-eth-address',
toAmount: toAmount,
}),
}).then((res) => res.json());
// 4. User sends funds to order.payInAddress
// This is a user action - they need to send the fromAmount to the payInAddress
// using their wallet. Save the transaction ID/hash from this transaction.
// 5. Update order with transaction ID
// After the user sends funds, update the order with the transaction ID
// This starts the verification process
await fetch(`https://exchange.exodus.io/v3/orders/${order.id}`, {
method: 'PATCH',
headers: {
'App-Name': 'acme-inc',
'Content-Type': 'application/json',
},
body: JSON.stringify({
fromTransactionId: 'user-transaction-id',
}),
});
// 6. Poll order status until completion
// Continue checking the order status until it's completed or failed
const checkStatus = async () => {
const updatedOrder = await fetch(`https://exchange.exodus.io/v3/orders/${order.id}`, {
headers: {
'App-Name': 'acme-inc',
},
}).then((res) => res.json());
if (updatedOrder.status === 'complete') {
console.log('Swap completed successfully!');
// Funds have been sent to the user's toAddress
} else if (updatedOrder.status === 'failed' || updatedOrder.status === 'expired') {
console.error('Swap failed:', updatedOrder.message);
// Check the message field for failure details
} else {
// Still in progress (inProgress, delayed), check again in 5 seconds
setTimeout(checkStatus, 5000);
}
};
checkStatus();Related Endpoints
- Get Rates for Pair
- Get Quotes for Pair
- Create Fixed Order (for rates)
- Create Floating Quote Order (for quotes)
- Get Single Order
- Update Order