Skip to main content

Overview

Private swaps route through multiple CEX hops to break the transaction trail, providing enhanced anonymity. Optionally routes through Monero (XMR) as an untraceable intermediate layer. In API v2, private swaps use the same /quotes and /exchanges endpoints as standard and DEX swaps — pass types=private to get only private quotes, or filter the response by type: "private".
Best For: Users who prioritize privacy and are willing to accept longer completion times (15–45 minutes) for enhanced anonymity.
Looking for the v1 private swap guide? See API v1 — Private Swap.

Key Features

Multi-Hop Routing

Routes through 2 exchanges to break the transaction trail

Optional XMR Privacy Layer

Monero used as an untraceable intermediate when available

No Wallet Connection

No browser wallet or on-chain approvals required

Maximum Anonymity

No direct on-chain link between source and destination

How It Works

1

Get Tokens

Bulk fetch CEX-supported tokens and cache to your DB, or search by name/symbol. Note each token’s id.
2

Get Quotes

Call GET /quotes with token IDs. Either pass types=private to get only private quotes, or filter the response by type: "private".
3

Create Order

Call POST /exchanges with the selected quoteId and destination address.
4

Send Deposit

Send exactly inAmount to the depositAddress returned in the order.
5

Monitor Status

Poll GET /orders/{houdiniId}. Private swaps pass through ANONYMIZING before FINISHED.

Integration Guide

Step 1: Get Tokens

There are two approaches for getting tokens. Choose the one that fits your integration:
Fetch all CEX-supported tokens once and store them in your backend database.
Cache the token list in your backend database. Never call /tokens on every user request — load on server startup or via a scheduled job and refresh every 24 hours.
async function fetchAllCexTokens() {
  let page = 1;
  let allTokens = [];

  while (true) {
    const response = await fetch(
      `https://api-partner.houdiniswap.com/v2/tokens?hasCex=true&pageSize=20&page=${page}`,
      {
        headers: { 'Authorization': `${API_KEY}:${API_SECRET}` }
      }
    );

    const { tokens, totalPages } = await response.json();
    allTokens = allTokens.concat(tokens);

    if (page >= totalPages) break;
    page++;
  }

  return allTokens; // save to your DB
}

Step 2: Get Private Quote

Call GET /quotes with token IDs. Pass types=private to receive only private quotes, or omit it to get all types and filter by type: "private".
const params = new URLSearchParams({
  amount: '1',
  from: '6689b73ec90e45f3b3e51566',  // ETH token id
  to:   '6689b73ec90e45f3b3e51577',  // SOL token id
  types: 'private',                  // only return private quotes
});

const response = await fetch(
  `https://api-partner.houdiniswap.com/v2/quotes?${params}`,
  {
    headers: {
      'Authorization': `${API_KEY}:${API_SECRET}`,
      'x-user-ip': userIp,
      'x-user-agent': userAgent,
      'x-user-timezone': userTimezone
    }
  }
);

const { quotes } = await response.json();

// Select a private (multi-hop) quote
const privateQuote = quotes.find(q => q.type === 'private');
console.log('Amount out:', privateQuote.amountOut);
console.log('ETA:', privateQuote.duration, 'minutes');  // typically 60 min
console.log('Quote ID:', privateQuote.quoteId);         // needed for /exchanges

Quotes Response (private quote)

{
  "quotes": [
    {
      "quoteId": "69af93b1f9c5affabcaccbdb",
      "type": "private",
      "amountIn": 1,
      "amountOut": 25842.84927999,
      "amountOutUsd": 2007.5759034667433,
      "min": 0.09828,
      "max": 4228.846045,
      "duration": 60,
      "rewardsAvailable": true
    }
  ],
  "total": 5
}
Key Private Quote Fields:
  • quoteId: Pass this to /exchanges to create the order
  • type: "private" indicates multi-hop routing
  • duration: Estimated time in minutes — longer due to multi-hop (typically 60 min)

Step 3: Create Private Order

Pass the quoteId and destination address to POST /exchanges. No additional parameters are needed to enable private routing — the quote type determines the routing.
const response = await fetch('https://api-partner.houdiniswap.com/v2/exchanges', {
  method: 'POST',
  headers: {
    'Authorization': `${API_KEY}:${API_SECRET}`,
    'Content-Type': 'application/json',
    'x-user-ip': userIp,
    'x-user-agent': userAgent,
    'x-user-timezone': userTimezone
  },
  body: JSON.stringify({
    quoteId: '694cd4b5d924391f000561e3',  // private quote from /quotes
    addressTo: '1nc1nerator11111111111111111111111111111111'
  })
});

const order = await response.json();
console.log('Order ID:', order.houdiniId);
console.log('Deposit to:', order.depositAddress);
console.log('Send amount:', order.inAmount, order.inSymbol);
console.log('Expires:', order.expires);

Order Response

{
  "houdiniId": "bwc5iKVeeW5GiQpLHCm65w",
  "created": "2025-12-25T06:13:21.293Z",
  "expires": "2025-12-25T06:43:21.293Z",
  "depositAddress": "0xA2fC2BD472aB6FAF3176EBcBCaeeC7f95F563Ada",
  "receiverAddress": "1nc1nerator11111111111111111111111111111111",
  "anonymous": true,
  "status": -1,
  "statusLabel": "NEW",
  "inAmount": 1,
  "inSymbol": "ETH",
  "inStatus": 0,
  "inStatusLabel": "NEW",
  "outAmount": 23.66258493,
  "outSymbol": "SOL",
  "outStatus": 0,
  "outStatusLabel": "NEW",
  "eta": 28,
  "swapName": "Changelly → Quickex"
}
Send exactly inAmount of inSymbol to depositAddress before expires (typically 30 minutes).

Step 4: Monitor Order Status

Poll GET /orders/{houdiniId} to track multi-hop progress, or subscribe via the WebSocket API for real-time updates:
const response = await fetch(
  `https://api-partner.houdiniswap.com/v2/orders/${order.houdiniId}`,
  {
    headers: {
      'Authorization': `${API_KEY}:${API_SECRET}`,
      'x-user-ip': userIp,
      'x-user-agent': userAgent,
      'x-user-timezone': userTimezone
    }
  }
);

const status = await response.json();
console.log('Overall status:', status.statusLabel);
console.log('First hop:', status.inStatusLabel);   // input leg
console.log('Second hop:', status.outStatusLabel); // output leg

Private Swap Status Progression

Private swaps pass through an additional ANONYMIZING stage during the XMR privacy layer:
NEW / WAITING
  ↓ Deposit sent and detected
CONFIRMING
  ↓ Blockchain confirmations received
EXCHANGING
  ↓ First CEX hop processing
ANONYMIZING
  ↓ Routing through XMR privacy layer
  ↓ Second CEX hop processing
FINISHED
Status Fields:
  • statusLabel: Overall order status
  • inStatusLabel: First hop status
  • outStatusLabel: Second hop status (private swaps only)
Poll every 30 seconds. Private swaps typically complete in 15–45 minutes due to multi-hop routing. For real-time updates without polling, use the WebSocket API.

Best Practices

Clearly communicate the 15–45 minute completion time. Users should understand they are trading speed for privacy.
Display both inStatusLabel and outStatusLabel to show users which leg of the swap is processing.
Implement loading states and progress indicators. Multi-hop routing takes significantly longer than standard swaps.
Never expose API keys in frontend code. Validate all destination addresses before submitting. Store houdiniId for support lookups.

Common Issues

Cause: Multi-hop routing through 2 exchanges takes longer than a direct swap.Solution: This is expected for private swaps. Monitor inStatusLabel and outStatusLabel to see which hop is processing.
Question: “How private is this really?”Answer: Private swaps break the transaction trail by routing through multiple exchanges. When XMR routing is used, it adds an untraceable intermediate step. However, this is not absolute anonymity — compliance checks still apply.
Cause: Deposit was not received before the expires timestamp.Solution: Fetch a new quote and create a new order.

Example Repositories

See full working integrations on GitHub:

Next Steps