RPSwap API

Integrate private crypto swaps into your application

Default swap provider for Rebel Pay

Overview

The RPSwap API aggregates multiple swap providers to offer the best rates for privacy-focused crypto swaps. Supported directions: XMR ↔ stablecoins (USDC, USDT on 7 chains) and XMR ↔ BTC.

Base URL:

Quick Start

  1. Get a quote for your swap amount
  2. Create a swap with your payout address
  3. Send XMR to the deposit address
  4. Poll for status or receive webhook callback

Response Format

All responses are JSON. Successful responses return the requested data. Error responses include an error field:

{
  "error": "Missing required parameters: from, to, amount"
}

Authentication

The RPSwap API supports two authentication modes:

Public API (No Auth Required)

Basic endpoints are available without authentication, subject to rate limits. Suitable for testing and low-volume usage.

API Key Authentication

For higher rate limits and webhook support, authenticate using an API key in the request header:

HEADER X-API-Key: your_api_key_here
curl "/v1/quotes?from=XMR&to=USDC&amount=1" \
  -H "X-API-Key: rps_live_abc123..."
Note: API keys are not yet available for self-service. Contact us for enterprise access.

Rate Limits

To ensure fair usage, the API enforces rate limits:

Tier Requests/Minute Requests/Day
Public (No API Key) 30 1,000
Authenticated 120 50,000
Enterprise Unlimited Unlimited

Rate limit headers are included in responses:

X-RateLimit-Limit: 30
X-RateLimit-Remaining: 28
X-RateLimit-Reset: 1699012345

When rate limited, you'll receive a 429 Too Many Requests response.

Supported Pairs

RPSwap uses compound tickers that encode both the token and destination chain (e.g., usdcmatic for USDC on Polygon). This follows the same pattern used by ChangeNOW and SideShift.

BTC Pairs

From To Direction
XMR BTC Monero → Bitcoin
BTC XMR Bitcoin → Monero

Stablecoin Pairs (All 13)

Compound Ticker Token Chain Network Fee
usdcmatic USDC Polygon $0.01
usdcerc20 USDC Ethereum $5.00
usdcsol USDC Solana $0.01
usdcbase USDC Base $0.01
usdcarb USDC Arbitrum $0.10
usdcbsc USDC BSC $0.10
usdtmatic USDT Polygon $0.01
usdterc20 USDT Ethereum $5.00
usdtsol USDT Solana $0.01
usdtbase USDT Base $0.01
usdttrc20 USDT Tron $1.00
usdtarb USDT Arbitrum $0.10
usdtbsc USDT BSC $0.10
Backwards Compatibility: You can also pass simple tokens (usdc, usdt) with the destChain parameter. If destChain is not specified, it defaults to polygon.
GET /v1/pairs

Example Response

{
  "pairs": [
    { "from": "XMR", "to": "usdcmatic", "token": "USDC", "chain": "polygon", "name": "USDC (Polygon)", "networkFee": 0.01 },
    { "from": "XMR", "to": "usdcerc20", "token": "USDC", "chain": "ethereum", "name": "USDC (Ethereum)", "networkFee": 5.00 },
    { "from": "XMR", "to": "usdcsol", "token": "USDC", "chain": "solana", "name": "USDC (Solana)", "networkFee": 0.01 },
    { "from": "XMR", "to": "usdcbase", "token": "USDC", "chain": "base", "name": "USDC (Base)", "networkFee": 0.01 },
    { "from": "XMR", "to": "usdcarb", "token": "USDC", "chain": "arbitrum", "name": "USDC (Arbitrum)", "networkFee": 0.10 },
    { "from": "XMR", "to": "usdcbsc", "token": "USDC", "chain": "bsc", "name": "USDC (BSC)", "networkFee": 0.10 },
    { "from": "XMR", "to": "usdtmatic", "token": "USDT", "chain": "polygon", "name": "USDT (Polygon)", "networkFee": 0.01 },
    { "from": "XMR", "to": "usdterc20", "token": "USDT", "chain": "ethereum", "name": "USDT (Ethereum)", "networkFee": 5.00 },
    { "from": "XMR", "to": "usdtsol", "token": "USDT", "chain": "solana", "name": "USDT (Solana)", "networkFee": 0.01 },
    { "from": "XMR", "to": "usdtbase", "token": "USDT", "chain": "base", "name": "USDT (Base)", "networkFee": 0.01 },
    { "from": "XMR", "to": "usdttrc20", "token": "USDT", "chain": "tron", "name": "USDT (Tron)", "networkFee": 1.00 },
    { "from": "XMR", "to": "usdtarb", "token": "USDT", "chain": "arbitrum", "name": "USDT (Arbitrum)", "networkFee": 0.10 },
    { "from": "XMR", "to": "usdtbsc", "token": "USDT", "chain": "bsc", "name": "USDT (BSC)", "networkFee": 0.10 }
  ]
}

Supported Networks

Stablecoins can be received on the following networks. The suffix field shows how to construct compound tickers (e.g., usdc + matic = usdcmatic).

Network ID Network Name Ticker Suffix Estimated Fee Confirmation Time
polygon Polygon (MATIC) matic ~$0.01 ~2 seconds
tron Tron (TRC-20) trc20 ~$1.00 ~3 seconds
solana Solana sol ~$0.01 ~1 second
base Base base ~$0.01 ~2 seconds
arbitrum Arbitrum arb ~$0.10 ~1 second
ethereum Ethereum (ERC-20) erc20 ~$5.00 ~12 seconds
bsc BSC (BNB Chain) bsc ~$0.10 ~3 seconds
GET /v1/networks
{
  "networks": [
    { "id": "polygon", "name": "Polygon (MATIC)", "fee": "~$0.01", "suffix": "matic" },
    { "id": "tron", "name": "Tron (TRC-20)", "fee": "~$1.00", "suffix": "trc20" },
    { "id": "solana", "name": "Solana", "fee": "~$0.01", "suffix": "sol" },
    { "id": "base", "name": "Base", "fee": "~$0.01", "suffix": "base" },
    { "id": "arbitrum", "name": "Arbitrum", "fee": "~$0.10", "suffix": "arb" },
    { "id": "ethereum", "name": "Ethereum (ERC-20)", "fee": "~$5.00", "suffix": "erc20" },
    { "id": "bsc", "name": "BSC (BNB Chain)", "fee": "~$0.10", "suffix": "bsc" }
  ]
}

Permissions

Check whether the RPSwap service is available for creating swaps. Useful for wallet integrations to disable the swap button during maintenance.

GET /v1/permissions

Example Response

{
  "createSwap": true,
  "getQuote": true,
  "maintenance": false,
  "message": null
}
Field Type Description
createSwap boolean Whether swap creation is currently available
getQuote boolean Whether quotes can be fetched
maintenance boolean Whether the service is in maintenance mode
message string|null Human-readable status message (null when operational)

Get Quotes

Fetch quotes from all providers for a currency pair. Returns quotes sorted by best output amount. Supports all directions: XMR → stablecoins, stablecoins → XMR, XMR → BTC, and BTC → XMR.

GET /v1/quotes

Query Parameters

Parameter Type Required Description
from string Yes Source currency (XMR, BTC, USDC, USDT)
to string Yes Destination: compound ticker (usdcmatic, usdttrc20), simple token (usdc), or BTC/XMR
amount number Yes Amount to swap in the source currency
fromChain string No Source chain for stablecoin → XMR swaps (default: polygon)
destChain string No Destination chain if using simple token (default: polygon)

Example Requests

# XMR → Stablecoin (compound ticker)
curl "/v1/quotes?from=XMR&to=usdttrc20&amount=1"

# Stablecoin → XMR
curl "/v1/quotes?from=USDT&to=XMR&amount=500&fromChain=polygon"

# XMR → BTC
curl "/v1/quotes?from=XMR&to=BTC&amount=10"

# BTC → XMR
curl "/v1/quotes?from=BTC&to=XMR&amount=0.1"
// XMR → USDT on Tron
const response = await fetch('/v1/quotes?from=XMR&to=usdttrc20&amount=1');
const data = await response.json();
console.log('Best rate:', data.bestQuote.rate);
console.log('You receive:', data.bestQuote.outputAmount);

// BTC → XMR
const btcResp = await fetch('/v1/quotes?from=BTC&to=XMR&amount=0.1');
const btcData = await btcResp.json();
console.log('XMR output:', btcData.bestQuote.outputAmount);
import requests

# XMR → USDT on Tron
resp = requests.get('/v1/quotes', params={
    'from': 'XMR', 'to': 'usdttrc20', 'amount': 1
})

# USDC → XMR (reverse)
resp = requests.get('/v1/quotes', params={
    'from': 'USDC', 'to': 'XMR', 'amount': 500, 'fromChain': 'polygon'
})

Example Response

{
  "from": "XMR",
  "to": "usdttrc20",
  "token": "USDT",
  "chain": "tron",
  "chainName": "USDT (Tron)",
  "amount": 1,
  "networkFee": 1.00,
  "bestQuote": {
    "provider": "trocador",
    "rate": 223.45,
    "outputAmount": 222.45,
    "networkFee": 1.00
  },
  "quotes": [
    {
      "provider": "trocador",
      "rate": 223.45,
      "outputAmount": 222.45,
      "networkFee": 1.00
    },
    {
      "provider": "sideshift",
      "rate": 222.89,
      "outputAmount": 221.89,
      "networkFee": 1.00
    },
    {
      "provider": "majesticbank",
      "rate": 221.50,
      "outputAmount": 220.50,
      "networkFee": 1.00
    }
  ]
}

Fixed-Rate Quotes

Lock a rate for 10 minutes by creating a fixed-rate quote. The locked rate includes a 0.5% premium over the variable rate to cover price risk. Use the returned quoteId when creating a swap to guarantee the quoted output amount.

POST /v1/quotes

Request Body

Parameter Type Required Description
from string Yes Source currency (XMR, BTC, USDC, USDT)
to string Yes Destination (compound ticker or currency)
amount number Yes Amount in source currency
settleAddress string No Payout address (can also be passed when creating swap)
refundAddress string No Refund address
fromChain string No Source chain for stablecoin → XMR

Example Request

curl -X POST "/v1/quotes" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "XMR",
    "to": "usdtmatic",
    "amount": 5,
    "settleAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f5d2a1"
  }'

Example Response

{
  "id": "rpsq_1699012345678_abc123",
  "from": "XMR",
  "to": "usdtmatic",
  "pair": "XMR_USDT",
  "direction": "xmr_to_stable",
  "depositCoin": "xmr",
  "settleCoin": "usdt",
  "depositNetwork": "monero",
  "settleNetwork": "polygon",
  "depositAmount": "5",
  "settleAmount": "1715.42",
  "rate": "343.09",
  "networkFee": 0.01,
  "expiresAt": "2024-11-03T12:10:00.000Z",
  "createdAt": "2024-11-03T12:00:00.000Z"
}

Using a Fixed-Rate Quote

Pass the quoteId when creating a swap to use the locked rate:

curl -X POST "/v1/swaps" \
  -H "Content-Type: application/json" \
  -d '{
    "quoteId": "rpsq_1699012345678_abc123",
    "payoutAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f5d2a1"
  }'
Note: Each quote can only be used once. Attempting to reuse a quote returns 409 Conflict. Expired quotes return 410 Gone.

Check Quote Status

GET /v1/quotes/:id
{
  "id": "rpsq_1699012345678_abc123",
  "from": "XMR",
  "to": "usdtmatic",
  "direction": "xmr_to_stable",
  "depositAmount": "5",
  "settleAmount": "1715.42",
  "rate": "343.09",
  "networkFee": 0.01,
  "expiresAt": "2024-11-03T12:10:00.000Z",
  "used": false
}

Create Swap

Create a new swap and receive a deposit address. Two modes: variable rate (pass from/to/amount) or fixed rate (pass quoteId from a fixed-rate quote).

POST /v1/swaps

Request Body

Parameter Type Required Description
quoteId string No* Fixed-rate quote ID from POST /v1/quotes. If provided, from/to/amount are taken from the quote.
from string Yes* Source currency (XMR, BTC, USDC, USDT)
to string Yes* Compound ticker or currency
amount number Yes* Amount in source currency
payoutAddress string Yes Address to receive output currency (must match the target chain)
refundAddress string No XMR address for refunds
provider string No Specific provider to use
destChain string No Destination chain if using simple token (default: polygon)
affiliateId string No Affiliate/partner ID for revenue sharing
webhookUrl string No URL for status update callbacks

Example Request (Compound Ticker)

# Using compound ticker (recommended)
curl -X POST "/v1/swaps" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "XMR",
    "to": "usdttrc20",
    "amount": 1.5,
    "payoutAddress": "TN8RtFXeQZyFHGmH1iiSRm5r5oe6m9nFxD",
    "webhookUrl": "https://myapp.com/webhooks/rpswap"
  }'
// Using compound ticker (recommended)
const response = await fetch('/v1/swaps', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    from: 'XMR',
    to: 'usdttrc20',  // USDT on Tron
    amount: 1.5,
    payoutAddress: 'TN8RtFXeQZyFHGmH1iiSRm5r5oe6m9nFxD',
    webhookUrl: 'https://myapp.com/webhooks/rpswap'
  })
});

const swap = await response.json();
console.log('Pair:', swap.to);           // "usdttrc20"
console.log('Token:', swap.token);       // "USDT"
console.log('Chain:', swap.chain);       // "tron"
console.log('Network Fee:', swap.networkFee);  // 1.00
console.log('Deposit', swap.depositAmount, 'XMR to:', swap.depositAddress);
import requests

# Using compound ticker (recommended)
response = requests.post('/v1/swaps', json={
    'from': 'XMR',
    'to': 'usdttrc20',  # USDT on Tron
    'amount': 1.5,
    'payoutAddress': 'TN8RtFXeQZyFHGmH1iiSRm5r5oe6m9nFxD',
    'webhookUrl': 'https://myapp.com/webhooks/rpswap'
})

swap = response.json()
print(f"Pair: {swap['to']}")            # "usdttrc20"
print(f"Token: {swap['token']}")        # "USDT"
print(f"Chain: {swap['chain']}")        # "tron"
print(f"Network Fee: ${swap['networkFee']}")
print(f"Deposit {swap['depositAmount']} XMR to: {swap['depositAddress']}")

Example Response

{
  "id": "rps_1699012345678_abc123",
  "from": "XMR",
  "to": "usdttrc20",
  "token": "USDT",
  "chain": "tron",
  "chainName": "USDT (Tron)",
  "amount": 1.5,
  "payoutAddress": "TN8RtFXeQZyFHGmH1iiSRm5r5oe6m9nFxD",
  "refundAddress": null,
  "provider": "trocador",
  "providerId": "trc_xyz789",
  "depositAddress": "84Xc2LKJF8aSd9jG5kPmNqR7tBvYx4HwZcUe3WfLnMpD2hK6rTsV1bQoAiEyCzFgJuRxSvNmW8aP",
  "depositMemo": null,
  "depositAmount": 1.5,
  "outputAmount": 334.18,
  "rate": 223.45,
  "networkFee": 1.00,
  "expiresAt": "2024-11-03T12:10:00.000Z",
  "status": "waiting",
  "createdAt": "2024-11-03T12:00:00.000Z"
}

Get Swap Status

Check the status of an existing swap.

GET /v1/swaps/:id

Path Parameters

Parameter Type Description
id string RPSwap swap ID (e.g., rps_1699012345678_abc123)

Example Request

curl "/v1/swaps/rps_1699012345678_abc123"
const swapId = 'rps_1699012345678_abc123';
const response = await fetch(`/v1/swaps/${swapId}`);
const swap = await response.json();

console.log('Status:', swap.status);
if (swap.txHash) {
  console.log('TX Hash:', swap.txHash);
}
import requests

swap_id = 'rps_1699012345678_abc123'
response = requests.get(f'/v1/swaps/{swap_id}')
swap = response.json()

print(f"Status: {swap['status']}")
if 'txHash' in swap:
    print(f"TX Hash: {swap['txHash']}")

Example Response (Complete)

{
  "id": "rps_1699012345678_abc123",
  "from": "XMR",
  "to": "usdcmatic",
  "token": "USDC",
  "chain": "polygon",
  "chainName": "USDC (Polygon)",
  "amount": 1.5,
  "payoutAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f5d2a1",
  "refundAddress": null,
  "provider": "trocador",
  "providerId": "trc_xyz789",
  "depositAddress": "84Xc2LKJF8aSd9jG5kPmNqR7tBvYx4HwZcUe3WfLnMpD2hK6rTsV1bQoAiEyCzFgJuRxSvNmW8aP",
  "depositAmount": 1.5,
  "outputAmount": 335.17,
  "networkFee": 0.01,
  "status": "complete",
  "inputTxHash": "a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456",
  "txHash": "0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba",
  "createdAt": "2024-11-03T12:00:00.000Z"
}

Status Values

Status Description
waiting Waiting for XMR deposit
confirming Deposit received, awaiting confirmations
exchanging Swap in progress at provider
sending Sending stablecoin to payout address
complete Swap completed successfully
failed Swap failed - contact support
refunded Funds refunded to refund address

List Providers

Get a list of enabled swap providers.

GET /v1/providers

Example Response

{
  "providers": ["trocador", "sideshift", "majesticbank"]
}

Get Limits

Get minimum and maximum swap amounts for a currency pair across all providers.

GET /v1/limits

Query Parameters

Parameter Type Required Description
from string Yes Source currency
to string Yes Destination currency

Example Request

curl "/v1/limits?from=XMR&to=USDC"

Example Response

{
  "from": "XMR",
  "to": "USDC",
  "providers": [
    {
      "provider": "trocador",
      "min": 0.01,
      "max": 100
    },
    {
      "provider": "sideshift",
      "min": 0.005,
      "max": 50
    },
    {
      "provider": "majesticbank",
      "min": 0.02,
      "max": 75
    }
  ]
}

Webhooks

Receive real-time notifications when your swap status changes. Configure a webhook URL when creating a swap to receive POST callbacks.

Webhook Request

When the swap status changes, we'll send a POST request to your webhook URL:

POST your-webhook-url

Request Headers

Header Description
Content-Type application/json
X-Webhook-Signature HMAC-SHA256 signature of the request body
X-Webhook-Timestamp Unix timestamp of the webhook

Webhook Payload

{
  "event": "swap.status_changed",
  "timestamp": "2024-11-03T12:05:00.000Z",
  "data": {
    "id": "rps_1699012345678_abc123",
    "status": "complete",
    "previousStatus": "sending",
    "outputAmount": 335.18,
    "txHash": "0x9876543210fedcba9876543210fedcba9876543210fedcba9876543210fedcba"
  }
}

Webhook Events

Event Description
swap.status_changed Swap status has changed
swap.deposit_received XMR deposit has been received
swap.completed Swap has completed successfully
swap.failed Swap has failed

Verifying Signatures

To verify webhook authenticity, compute the HMAC-SHA256 signature:

const crypto = require('crypto');

function verifyWebhook(payload, signature, timestamp, secret) {
  const signedPayload = `${timestamp}.${JSON.stringify(payload)}`;
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(signedPayload)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

Retry Policy

If your webhook endpoint returns a non-2xx status code, we'll retry:

  • Retry 1: After 1 minute
  • Retry 2: After 5 minutes
  • Retry 3: After 30 minutes
  • Retry 4: After 2 hours
  • Retry 5: After 24 hours (final)

Wallet Integration Guide

RPSwap is designed to integrate seamlessly into non-custodial wallets. Here's the typical integration flow:

Integration Flow

  1. Check availability: GET /v1/permissions → verify createSwap: true
  2. Get supported pairs: GET /v1/pairs → populate currency selectors
  3. Fetch limits: GET /v1/limits?from=XMR&to=BTC → validate user input
  4. Get rate: GET /v1/quotes?from=XMR&to=BTC&amount=1 → display rate to user
  5. Create swap: POST /v1/swaps with payoutAddress + refundAddress
  6. Send funds: User sends to depositAddress returned in step 5
  7. Poll status: GET /v1/swaps/:id until status: "complete"

Key Features for Wallets

Status Mapping

Map RPSwap statuses to your wallet's UI states:

RPSwap Status Wallet UI State
waitingAwaiting deposit
confirmingDeposit received, confirming
exchangingSwap in progress
sendingSending to your address
completeDone ✓
failedFailed — check refund
refundedRefunded to refund address
expiredExpired — no deposit received