Skip to content

Swap Integration Guide

Step-by-step guide to quoting and executing token swaps from customer wallets.

Prerequisites

  • An active Rach API key (live_sk_…)
  • At least one customer wallet provisioned via the Wallet-as-a-Service API
  • The customer wallet must hold enough of from_token plus gas (MATIC on Polygon, BNB on BSC) to cover the swap and network fees

Base URL

https://api.rach.finance

Authentication

Pass your API key in the header on every request:

bash
X-API-Key: live_sk_YOUR_KEY

Step 1 — Get a Quote

Always fetch a quote first. It tells you the expected output amount and the minimum you should accept (to_amount_min). Quotes expire in 30 seconds.

GET /api/v1/swap/quote
Query paramRequiredDescription
from_chainYesSource chain: POL, BSC, ETH, ARB, etc.
to_chainYesDestination chain (same as from_chain for DEX swaps)
from_tokenYesToken address or "native" for MATIC/BNB
to_tokenYesToken address or "native" for MATIC/BNB
amount_inYesAmount in base units (wei / smallest denomination)
bash
# Quote: 100 USDC → MATIC on Polygon
# USDC on Polygon: 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
curl "https://api.rach.finance/api/v1/swap/quote\
?from_chain=POL\
&to_chain=POL\
&from_token=0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174\
&to_token=native\
&amount_in=100000000" \
  -H "X-API-Key: live_sk_YOUR_KEY"
bash
# Quote: 100 USDC on Polygon → USDC on Arbitrum
curl "https://api.rach.finance/api/v1/swap/quote\
?from_chain=POL\
&to_chain=ARB\
&from_token=0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174\
&to_token=0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8\
&amount_in=100000000" \
  -H "X-API-Key: live_sk_YOUR_KEY"
javascript
async function getSwapQuote({ fromChain, toChain, fromToken, toToken, amountIn }) {
  const params = new URLSearchParams({ from_chain: fromChain, to_chain: toChain,
    from_token: fromToken, to_token: toToken, amount_in: amountIn.toString() })

  const res = await fetch(`https://api.rach.finance/api/v1/swap/quote?${params}`, {
    headers: { 'X-API-Key': 'live_sk_YOUR_KEY' }
  })
  if (!res.ok) throw new Error(await res.text())
  return res.json()
}

// 100 USDC (6 decimals) → MATIC on Polygon
const quote = await getSwapQuote({
  fromChain: 'POL',
  toChain: 'POL',
  fromToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
  toToken: 'native',
  amountIn: 100_000_000n  // 100 USDC
})

console.log(`Expected out: ${quote.to_amount_expected}`)
console.log(`Minimum out:  ${quote.to_amount_min}`)
console.log(`Platform fee: ${quote.platform_fee}`)
console.log(`Expires at:   ${new Date(quote.expires_at * 1000).toISOString()}`)
python
import requests

def get_swap_quote(from_chain, to_chain, from_token, to_token, amount_in):
    res = requests.get(
        'https://api.rach.finance/api/v1/swap/quote',
        params={
            'from_chain': from_chain,
            'to_chain': to_chain,
            'from_token': from_token,
            'to_token': to_token,
            'amount_in': str(amount_in),
        },
        headers={'X-API-Key': 'live_sk_YOUR_KEY'}
    )
    res.raise_for_status()
    return res.json()

quote = get_swap_quote(
    from_chain='POL',
    to_chain='POL',
    from_token='0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
    to_token='native',
    amount_in=100_000_000  # 100 USDC
)
print(f"Expected: {quote['to_amount_expected']}")
print(f"Min out:  {quote['to_amount_min']}")

Response (DEX — same-chain):

json
{
  "from_chain": "POL",
  "to_chain": "POL",
  "from_token": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
  "to_token": "native",
  "from_amount": "100000000",
  "to_amount_expected": "134521000000000000000",
  "to_amount_min":      "133948495000000000000",
  "platform_fee":       "300000",
  "estimated_seconds":  30,
  "expires_at":         1751673028
}

Response (bridge — cross-chain):

json
{
  "from_chain": "POL",
  "to_chain": "ARB",
  "from_token": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
  "to_token": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8",
  "from_amount": "100000000",
  "to_amount_expected": "99150000",
  "to_amount_min":      "98656000",
  "platform_fee":       "720000",
  "estimated_seconds":  180,
  "expires_at":         1751673028
}

Quote expiry

Quotes expire in 30 seconds. Execute the swap immediately after fetching the quote, using to_amount_min as your slippage floor. Fetching a fresh quote at execution time means the output is already slightly different — always use the quote's to_amount_min, not a value calculated yourself.


Step 2 — Execute the Swap

After getting a quote, execute the swap. The API broadcasts the transaction and returns the tx_hash immediately. Settlement is asynchronous.

POST /api/v1/swap/:customerID
FieldTypeRequiredDescription
from_chainstringYesSource chain (POL, BSC, ETH, …)
to_chainstringYesDestination chain
from_tokenstringYesToken address or "native"
to_tokenstringYesToken address or "native"
amount_instringYesAmount in base units
amount_out_minstringNoSlippage floor in base units (use quote's to_amount_min)
bash
curl -X POST "https://api.rach.finance/api/v1/swap/cust_abc123" \
  -H "X-API-Key: live_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from_chain": "POL",
    "to_chain": "POL",
    "from_token": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
    "to_token": "native",
    "amount_in": "100000000",
    "amount_out_min": "133948495000000000000"
  }'
javascript
async function executeSwap(customerId, body) {
  const res = await fetch(`https://api.rach.finance/api/v1/swap/${customerId}`, {
    method: 'POST',
    headers: {
      'X-API-Key': 'live_sk_YOUR_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(body)
  })
  if (!res.ok) throw new Error(await res.text())
  return res.json()
}

// Full flow: quote then execute
const quote = await getSwapQuote({ ... })

const result = await executeSwap('cust_abc123', {
  from_chain: 'POL',
  to_chain: 'POL',
  from_token: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
  to_token: 'native',
  amount_in: '100000000',
  amount_out_min: quote.to_amount_min   // slippage floor from quote
})

console.log(`Swap submitted: ${result.tx_hash}`)
console.log(`Status: ${result.status}`)   // "pending"
python
import requests

def execute_swap(customer_id, payload, api_key):
    res = requests.post(
        f'https://api.rach.finance/api/v1/swap/{customer_id}',
        json=payload,
        headers={'X-API-Key': api_key, 'Content-Type': 'application/json'}
    )
    res.raise_for_status()
    return res.json()

result = execute_swap('cust_abc123', {
    'from_chain': 'POL',
    'to_chain':   'POL',
    'from_token': '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',
    'to_token':   'native',
    'amount_in':  '100000000',
    'amount_out_min': quote['to_amount_min']
}, 'live_sk_YOUR_KEY')

print(f"tx_hash: {result['tx_hash']}")

Response:

json
{
  "tx_hash": "0x4a3f2e1b8c9d7e6f5a4b3c2d1e0f9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3",
  "status": "pending"
}

Transaction status

status: "pending" means the transaction was broadcast to the network. Use the tx_hash to track confirmation on:


Step 3 — Check Swap History

Get the swap history for a customer to show them past transactions.

GET /api/v1/swap/:customerID/history?page=1&limit=20
bash
curl "https://api.rach.finance/api/v1/swap/cust_abc123/history?page=1&limit=20" \
  -H "X-API-Key: live_sk_YOUR_KEY"

Response:

json
{
  "swaps": [
    {
      "id": 42,
      "customer_id": "cust_abc123",
      "type": "dex",
      "from_chain": "POL",
      "to_chain": "POL",
      "dex": "quickswap",
      "router_address": "0xa5E0829CaCEd8fFDD4De3c43696c57F7D7A678ff",
      "token_in": "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174",
      "token_out": "native",
      "amount_in": "100000000",
      "amount_out": "134521000000000000000",
      "platform_fee": "300000",
      "approve_tx_hash": "0xabc...",
      "swap_tx_hash": "0x4a3f...",
      "status": "confirmed",
      "confirmed_at": "2026-07-04T23:55:12Z",
      "created_at": "2026-07-04T23:54:40Z"
    }
  ],
  "total": 8,
  "page": 1,
  "limit": 20
}

Complete Integration Example

javascript
const API_KEY = 'live_sk_YOUR_KEY'
const BASE    = 'https://api.rach.finance'

const headers = { 'X-API-Key': API_KEY, 'Content-Type': 'application/json' }

async function swapTokens(customerId, { fromChain, toChain, fromToken, toToken, amountIn }) {
  // 1. Quote
  const qp = new URLSearchParams({
    from_chain: fromChain, to_chain: toChain,
    from_token: fromToken, to_token: toToken,
    amount_in:  amountIn.toString()
  })
  const quoteRes = await fetch(`${BASE}/api/v1/swap/quote?${qp}`, { headers })
  if (!quoteRes.ok) throw new Error(`Quote failed: ${await quoteRes.text()}`)
  const quote = await quoteRes.json()

  console.log(`Swapping ${amountIn} → expected ${quote.to_amount_expected}`)
  console.log(`Platform fee: ${quote.platform_fee} | Expires: ${new Date(quote.expires_at * 1000).toISOString()}`)

  // 2. Execute immediately (quote expires in 30s)
  const execRes = await fetch(`${BASE}/api/v1/swap/${customerId}`, {
    method: 'POST',
    headers,
    body: JSON.stringify({
      from_chain:     fromChain,
      to_chain:       toChain,
      from_token:     fromToken,
      to_token:       toToken,
      amount_in:      amountIn.toString(),
      amount_out_min: quote.to_amount_min    // slippage protection
    })
  })
  if (!execRes.ok) throw new Error(`Swap failed: ${await execRes.text()}`)
  const result = await execRes.json()

  console.log(`Swap submitted! tx_hash: ${result.tx_hash}`)
  return result
}

// DEX swap — 50 USDC → MATIC on Polygon
await swapTokens('cust_abc123', {
  fromChain: 'POL',
  toChain:   'POL',
  fromToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',  // USDC on Polygon
  toToken:   'native',                                        // MATIC
  amountIn:  50_000_000n  // 50 USDC (6 decimals)
})

// Bridge — 100 USDC from Polygon to Arbitrum
await swapTokens('cust_abc123', {
  fromChain: 'POL',
  toChain:   'ARB',
  fromToken: '0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174',  // USDC on Polygon
  toToken:   '0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8',  // USDC on Arbitrum
  amountIn:  100_000_000n
})

Common Token Addresses

Polygon

TokenAddress
MATIC (native)"native"
USDC0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174
USDT0xc2132D05D31c914a87C6611C10748AEb04B58e8F
DAI0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063
WBTC0x1BFD67037B42Cf73acF2047067bd4F2C47D9BfD6
WETH0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619

BSC

TokenAddress
BNB (native)"native"
USDC0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d
USDT0x55d398326f99059fF775485246999027B3197955
DAI0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3
WBTC0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c
ETH0x2170Ed0880ac9A755fd29B2688956BD959F933F8

Amount Conversions

All amounts are passed in base units (no decimals). Use the token's decimal count to convert:

TokenDecimals1 token in base units
MATIC / BNB (native)181000000000000000000
USDC61000000
USDT6 (BSC) / 6 (POL)1000000
WBTC8100000000
WETH181000000000000000000
javascript
// Helper: convert human amount to base units
function toBaseUnits(amount, decimals) {
  return BigInt(Math.round(amount * 10 ** decimals)).toString()
}

toBaseUnits(100, 6)   // "100000000" for 100 USDC
toBaseUnits(0.5, 18)  // "500000000000000000" for 0.5 MATIC

Error Responses

HTTPErrorCause
400from_chain, to_chain, from_token, to_token and amount_in are requiredMissing query params on quote
400amount_in must be a positive integer in base unitsNon-numeric or zero amount
400tokenIn and tokenOut resolve to the same addressSame token for in/out
400swap not supported on network "X"Chain not supported for DEX swaps
500customer wallet not found for …Customer has no provisioned wallet
500approve failed / swap transaction failedOn-chain transaction reverted

Best Practices

Always use amount_out_min — set it to the quote's to_amount_min. Without it, large price movements between quote and execution can result in a much worse rate.

Execute within 30 seconds — quotes expire quickly because on-chain prices move. Fetch the quote immediately before executing, not minutes earlier.

Convert amounts carefully — off-by-one on decimals is the most common integration mistake. Always verify the decimal count for each token before calculating base units.

Check wallet balance first — the swap will fail on-chain if the customer wallet lacks sufficient token balance or gas. Use the WaaS balance endpoints to verify before initiating.


Next Steps

Rach Payments API