Skip to Content
Integration FlowsDirect Swaps

Direct Swaps Integration Guide

This guide walks you through integrating Direct Swaps.

Overview

Direct Swaps follow this order creation flow:

  1. Get the estimated amount you will receive (using rates or quotes)
  2. Create an order
  3. Send funds to the provider address
  4. 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:

  1. Call GET /v3/pairs to retrieve all available pairs
  2. Use the pairId from the response (e.g., BTC_ETH) to create your swap
  3. 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

CriteriaUse RatesUse Quotes
Swap volumeUnder $100,000 USDOver $100,000 USD
Amount certaintyGuaranteed outputMay vary with slippage
ImplementationSimpler (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:

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:

Required parameters:

  • pairId - The pair (e.g., BTC_ETH)
  • fromAmount - Amount to swap
  • fromAddress - User’s sending address. Make sure this address can receive funds in case it needs to be refunded
  • toAddress - User’s receiving address
  • toAmount - Expected output amount (from rate or quote)

Response includes:

  • orderId - Use this to track and update the order
  • payInAddress - 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 progress
  • complete - Swap successful
  • failed - Swap failed (check message field)
  • expired - Order expired before completion
  • refunded - Order was refunded
  • delayed - 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();
Last updated on