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.
Quick Start
- Get a quote for your swap amount
- Create a swap with your payout address
- Send XMR to the deposit address
- 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:
X-API-Key: your_api_key_here
curl "/v1/quotes?from=XMR&to=USDC&amount=1" \ -H "X-API-Key: rps_live_abc123..."
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 |
usdc, usdt) with the destChain parameter. If destChain is not specified, it defaults to polygon.
/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 |
/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.
/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.
/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.
/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"
}'
409 Conflict. Expired quotes return 410 Gone.
Check Quote Status
/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).
/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.
/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.
/v1/providers
Example Response
{
"providers": ["trocador", "sideshift", "majesticbank"]
}
Get Limits
Get minimum and maximum swap amounts for a currency pair across all providers.
/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:
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
- Check availability:
GET /v1/permissions→ verifycreateSwap: true - Get supported pairs:
GET /v1/pairs→ populate currency selectors - Fetch limits:
GET /v1/limits?from=XMR&to=BTC→ validate user input - Get rate:
GET /v1/quotes?from=XMR&to=BTC&amount=1→ display rate to user - Create swap:
POST /v1/swapswithpayoutAddress+refundAddress - Send funds: User sends to
depositAddressreturned in step 5 - Poll status:
GET /v1/swaps/:iduntilstatus: "complete"
Key Features for Wallets
- No KYC: No account registration or identity verification required
- No API key required: Public endpoints work without authentication
- Tor-friendly: All endpoints accessible over Tor (.onion support planned)
- Affiliate revenue: Pass
affiliateIdon swap creation for revenue sharing - Refund support: Pass
refundAddressfor automatic refunds on failure - SideShift-compatible tickers: Compound ticker format (
usdcmatic,usdterc20) matches industry standard
Status Mapping
Map RPSwap statuses to your wallet's UI states:
| RPSwap Status | Wallet UI State |
|---|---|
waiting | Awaiting deposit |
confirming | Deposit received, confirming |
exchanging | Swap in progress |
sending | Sending to your address |
complete | Done ✓ |
failed | Failed — check refund |
refunded | Refunded to refund address |
expired | Expired — no deposit received |