Welcome

The Mintarex API provides programmatic access to OTC crypto trading, wallet management, and real-time market data for institutional clients.

Mintarex is an OTC (over-the-counter) trading platform — not an exchange. We use a Request-for-Quote (RFQ) model: you request a price, we lock it, you execute with zero slippage. This is what treasury desks, payment processors, and remittance companies need.

Institutional access only. API keys are available for corporate accounts with approved KYC. Contact business@mintarex.com to get started.

What you can do with the API

Supported currencies

TypeCurrencies
FiatUSD, EUR, GBP, CAD, AED, INR
CryptoBTC, ETH, USDT, USDC, SOL, XRP, ADA, DOGE, LTC, MATIC, TON, and 190+ more
Networks63 blockchain networks (62 EVM + Bitcoin, Solana, Cardano, XRP, TON, Tron, etc.)
Important: Network identifiers use short keys (e.g. eth, btc, sol), NOT full names (ethereum, bitcoin, solana). Always use GET /v1/networks to discover the exact identifiers.

Common coin & network identifiers

Use GET /v1/networks?coin={SYMBOL} for the complete list. Here are the most common ones:

CoinNetwork KeyChain Name
BTCbtcBitcoin
ETHethEthereum (ERC-20)
ETHarbArbitrum One
ETHbaseBase
ETHopOptimism
ETHmaticPolygon
USDTethEthereum (ERC-20)
USDTtronTron (TRC-20)
USDTsolSolana (SPL)
USDTarbArbitrum One
USDCethEthereum (ERC-20)
USDCsolSolana (SPL)
USDCbaseBase
SOLsolSolana
XRPxrpXRP Ledger
LTCltcLitecoin
DOGEdogeDogecoin
ADAadaCardano
TONtonThe Open Network
MATICmaticPolygon

For the full list of 200+ coins and 63 networks, call GET /v1/networks (no authentication required).

Base URL

All API requests are made to:

https://institutional.mintarex.com/v1

All requests must use HTTPS. HTTP requests are automatically redirected.

Health check

GET https://institutional.mintarex.com/health

Response:
{
  "ok": true,
  "service": "mintarex-otc-gateway",
  "version": "0.0.1",
  "timestamp": "2026-04-09T09:33:04.593Z",
  "uptime_seconds": 3600
}

Authentication

Every API request must include four authentication headers:

HeaderDescription
MX-API-KEYYour public API key (e.g. mxn_live_a8f3b2c1d4e5f6a7b8c9d0e1)
MX-SIGNATUREHMAC-SHA256 signature of the request (64 hex characters)
MX-TIMESTAMPCurrent Unix timestamp in seconds
MX-NONCEUnique UUID v4 per request (replay protection)
Timestamp window: Requests with a timestamp more than 30 seconds from server time will be rejected. Ensure your clock is NTP-synchronized.

Optional headers

HeaderDescription
MX-IDEMPOTENCY-KEYUUID v4 for safe retries on POST requests. Same key returns cached response within 24h.

Generating the API Key

  1. Log in to the Mintarex Dashboard with a corporate account
  2. Complete corporate KYC verification (must be approved)
  3. Navigate to Settings → API Keys
  4. Click Create API Key
  5. Select your desired permissions (scopes)
  6. Add at least one IP address to the whitelist (required for production keys)
  7. Your API key and secret will be displayed once — save the secret immediately
Save your secret! The API secret is shown only once at creation time. It cannot be recovered. If lost, create a new key.

Key format

API Key:    mxn_live_a8f3b2c1d4e5f6a7b8c9d0e1   (33 characters, public)
API Secret: YWJjZGVmZ2hpamtsbW5vcH...          (64 characters, secret — save immediately)

Permission scopes

ScopeDescription
read:accountView account info, fees, limits
read:walletView balances, deposit addresses
read:tradeView trade history
read:marketView market data and instruments
trade:quoteRequest RFQ quotes
trade:executeExecute trades (accept quotes)
withdraw:cryptoSubmit crypto withdrawals (requires pre-whitelisted addresses)
webhook:manageCreate/manage webhook endpoints
stream:marketSubscribe to price streams (SSE)
stream:accountSubscribe to account event streams (SSE)

IP whitelisting

Production keys require at least one IP address. Requests from non-whitelisted IPs are rejected with 403 IP_NOT_WHITELISTED. Supports IPv4, IPv6, and CIDR notation (e.g. 203.0.113.0/24).

Signing Requests

Every authenticated request must be signed with HMAC-SHA256 using your API secret.

Step 1: Build the canonical string

Concatenate with newline (\n) separator:

canonical = METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + NONCE + "\n" + BODY_HASH
ComponentDescriptionExample
METHODHTTP method (uppercase)GET
PATHFull path including query string/v1/account/balances?currency_type=crypto
TIMESTAMPUnix timestamp in seconds1712582345
NONCEUUID v4 (unique per request)550e8400-e29b-41d4-a716-446655440000
BODY_HASHSHA-256 hex of request bodye3b0c44298fc1c14... (empty body hash for GET)
For GET/DELETE requests: Use SHA-256 hash of empty string: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855

Step 2: Compute the HMAC

signature = HMAC-SHA256(api_secret, canonical_string).hex()   // 64 hex characters

Step 3: Send the headers

MX-API-KEY: mxn_live_a8f3b2c1d4e5f6a7b8c9d0e1
MX-TIMESTAMP: 1712582345
MX-NONCE: 550e8400-e29b-41d4-a716-446655440000
MX-SIGNATURE: 7d9b2c5f8a1e4b6d9c2e5f8a1b4c7d0e3f6a9b2c5f8a1e4b6d9c2e5f8a1b4c7

Code Samples

Complete signing and request examples in 5 languages.

Node.js

const crypto = require('node:crypto');
const https = require('node:https');

const API_KEY    = 'mxn_live_your_key_here';
const API_SECRET = 'your_secret_here';
const BASE       = 'https://institutional.mintarex.com';

function signedRequest(method, path, body = '') {
  const timestamp = Math.floor(Date.now() / 1000).toString();
  const nonce     = crypto.randomUUID();
  const bodyStr   = (body !== null && typeof body === 'object') ? JSON.stringify(body) : (body || '');
  const bodyHash  = crypto.createHash('sha256').update(bodyStr).digest('hex');
  const canonical = [method, path, timestamp, nonce, bodyHash].join('\n');
  const signature = crypto.createHmac('sha256', API_SECRET).update(canonical).digest('hex');

  return {
    headers: {
      'MX-API-KEY': API_KEY, 'MX-TIMESTAMP': timestamp,
      'MX-NONCE': nonce, 'MX-SIGNATURE': signature,
      'Content-Type': 'application/json',
    },
    body: bodyStr,
  };
}

// GET example
const { headers } = signedRequest('GET', '/v1/account/balances');
https.get(new URL('/v1/account/balances', BASE), { headers }, (res) => {
  let d = ''; res.on('data', c => d += c);
  res.on('end', () => console.log(JSON.parse(d)));
});

// POST example (request a quote)
const quoteBody = { base: 'BTC', quote: 'USD', side: 'buy', amount: '1000', amount_type: 'quote' };
const req = signedRequest('POST', '/v1/rfq', quoteBody);
const r = https.request(new URL('/v1/rfq', BASE), {
  method: 'POST', headers: req.headers,
}, (res) => {
  let d = ''; res.on('data', c => d += c);
  res.on('end', () => console.log(JSON.parse(d)));
});
r.write(req.body); r.end();

Python

import hashlib, hmac, json, time, uuid, requests

API_KEY    = 'mxn_live_your_key_here'
API_SECRET = 'your_secret_here'
BASE       = 'https://institutional.mintarex.com'

def signed_request(method, path, body=None):
    timestamp = str(int(time.time()))
    nonce     = str(uuid.uuid4())
    body_str  = json.dumps(body) if body else ''
    body_hash = hashlib.sha256(body_str.encode()).hexdigest()
    canonical = f"{method}\n{path}\n{timestamp}\n{nonce}\n{body_hash}"
    signature = hmac.new(API_SECRET.encode(), canonical.encode(), hashlib.sha256).hexdigest()

    headers = {
        'MX-API-KEY': API_KEY, 'MX-TIMESTAMP': timestamp,
        'MX-NONCE': nonce, 'MX-SIGNATURE': signature,
        'Content-Type': 'application/json',
    }
    return headers, body_str

# GET example
headers, _ = signed_request('GET', '/v1/account/balances')
resp = requests.get(f'{BASE}/v1/account/balances', headers=headers)
print(resp.json())

# POST example (request a quote)
body = {'base': 'BTC', 'quote': 'USD', 'side': 'buy', 'amount': '1000', 'amount_type': 'quote'}
headers, data = signed_request('POST', '/v1/rfq', body)
resp = requests.post(f'{BASE}/v1/rfq', headers=headers, data=data)
print(resp.json())

Go

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "io"
    "net/http"
    "strings"
    "time"

    "github.com/google/uuid"
)

const (
    apiKey    = "mxn_live_your_key_here"
    apiSecret = "your_secret_here"
    baseURL   = "https://institutional.mintarex.com"
)

func signRequest(method, path, body string) http.Header {
    timestamp := fmt.Sprintf("%d", time.Now().Unix())
    nonce := uuid.New().String()

    h := sha256.Sum256([]byte(body))
    bodyHash := hex.EncodeToString(h[:])

    canonical := strings.Join([]string{method, path, timestamp, nonce, bodyHash}, "\n")
    mac := hmac.New(sha256.New, []byte(apiSecret))
    mac.Write([]byte(canonical))
    signature := hex.EncodeToString(mac.Sum(nil))

    headers := http.Header{}
    headers.Set("MX-API-KEY", apiKey)
    headers.Set("MX-TIMESTAMP", timestamp)
    headers.Set("MX-NONCE", nonce)
    headers.Set("MX-SIGNATURE", signature)
    headers.Set("Content-Type", "application/json")
    return headers
}

func main() {
    path := "/v1/account/balances"
    req, _ := http.NewRequest("GET", baseURL+path, nil)
    req.Header = signRequest("GET", path, "")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.*;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.UUID;

public class MintarexApi {
    static final String API_KEY    = "mxn_live_your_key_here";
    static final String API_SECRET = "your_secret_here";
    static final String BASE_URL   = "https://institutional.mintarex.com";

    static String sha256(String data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        byte[] hash = md.digest(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte b : hash) sb.append(String.format("%02x", b));
        return sb.toString();
    }

    static String hmacSha256(String key, String data) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(key.getBytes("UTF-8"), "HmacSHA256"));
        byte[] hash = mac.doFinal(data.getBytes("UTF-8"));
        StringBuilder sb = new StringBuilder();
        for (byte b : hash) sb.append(String.format("%02x", b));
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        String method    = "GET";
        String path      = "/v1/account/balances";
        String timestamp = String.valueOf(Instant.now().getEpochSecond());
        String nonce     = UUID.randomUUID().toString();
        String bodyHash  = sha256("");
        String canonical = method + "\n" + path + "\n" + timestamp + "\n" + nonce + "\n" + bodyHash;
        String signature = hmacSha256(API_SECRET, canonical);

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(BASE_URL + path))
            .header("MX-API-KEY", API_KEY)
            .header("MX-TIMESTAMP", timestamp)
            .header("MX-NONCE", nonce)
            .header("MX-SIGNATURE", signature)
            .GET().build();

        HttpResponse<String> response = HttpClient.newHttpClient()
            .send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}

C#

using System.Security.Cryptography;
using System.Text;

var apiKey    = "mxn_live_your_key_here";
var apiSecret = "your_secret_here";
var baseUrl   = "https://institutional.mintarex.com";

string Sha256(string data) {
    var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(data));
    return Convert.ToHexString(bytes).ToLower();
}

string HmacSha256(string key, string data) {
    using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(key));
    var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(data));
    return Convert.ToHexString(hash).ToLower();
}

var method    = "GET";
var path      = "/v1/account/balances";
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
var nonce     = Guid.NewGuid().ToString();
var bodyHash  = Sha256("");
var canonical = $"{method}\n{path}\n{timestamp}\n{nonce}\n{bodyHash}";
var signature = HmacSha256(apiSecret, canonical);

using var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, baseUrl + path);
request.Headers.Add("MX-API-KEY", apiKey);
request.Headers.Add("MX-TIMESTAMP", timestamp);
request.Headers.Add("MX-NONCE", nonce);
request.Headers.Add("MX-SIGNATURE", signature);

var response = await client.SendAsync(request);
Console.WriteLine(await response.Content.ReadAsStringAsync());

PHP

<?php
$apiKey    = 'mxn_live_your_key_here';
$apiSecret = 'your_secret_here';
$baseUrl   = 'https://institutional.mintarex.com';

function signedRequest($method, $path, $body = '') {
    global $apiKey, $apiSecret;
    $timestamp = (string)time();
    $nonce     = sprintf('%s-%s-%s-%s-%s',
        bin2hex(random_bytes(4)), bin2hex(random_bytes(2)),
        bin2hex(random_bytes(2)), bin2hex(random_bytes(2)),
        bin2hex(random_bytes(6)));
    $bodyHash  = hash('sha256', $body);
    $canonical = implode("\n", [$method, $path, $timestamp, $nonce, $bodyHash]);
    $signature = hash_hmac('sha256', $canonical, $apiSecret);

    return [
        'MX-API-KEY: '    . $apiKey,
        'MX-TIMESTAMP: '  . $timestamp,
        'MX-NONCE: '      . $nonce,
        'MX-SIGNATURE: '  . $signature,
        'Content-Type: application/json',
    ];
}

// GET example
$path = '/v1/account/balances';
$headers = signedRequest('GET', $path);
$ch = curl_init($baseUrl . $path);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;

// POST example
$path = '/v1/rfq';
$body = json_encode(['base'=>'BTC','quote'=>'USD','side'=>'buy','amount'=>'1000','amount_type'=>'quote']);
$headers = signedRequest('POST', $path, $body);
$ch = curl_init($baseUrl . $path);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>

cURL

# Set credentials
API_KEY="mxn_live_your_key_here"
API_SECRET="your_secret_here"

# Build signature components
METHOD="GET"
PATH="/v1/account/balances"
TIMESTAMP=$(date +%s)
NONCE=$(python3 -c "import uuid; print(uuid.uuid4())")
BODY_HASH=$(echo -n "" | sha256sum | cut -d' ' -f1)

# Sign
CANONICAL=$(printf "%s\n%s\n%s\n%s\n%s" "$METHOD" "$PATH" "$TIMESTAMP" "$NONCE" "$BODY_HASH")
SIGNATURE=$(echo -ne "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" | cut -d' ' -f2)

# Request
curl -s "https://institutional.mintarex.com${PATH}" \
  -H "MX-API-KEY: ${API_KEY}" \
  -H "MX-TIMESTAMP: ${TIMESTAMP}" \
  -H "MX-NONCE: ${NONCE}" \
  -H "MX-SIGNATURE: ${SIGNATURE}" | python3 -m json.tool

Rate Limits

Rate limits are enforced per API key using a sliding window. Limits vary by endpoint type and your account tier.

TierRead /minWrite /minQuote /minExecute /minWithdraw /min
Starter60201053
Professional1204020105
Enterprise300100603015

Endpoint classification

BucketEndpoints
ReadAll GET requests (balances, history, fees, limits)
WritePOST/DELETE requests not in other buckets (webhooks, address management)
QuotePOST /v1/rfq — requesting a quote
ExecutePOST /v1/rfq/{id}/accept — executing a trade
WithdrawPOST /v1/crypto/withdraw

Rate limit headers

Every response includes IETF-standard rate limit headers:

HeaderDescription
RateLimit-LimitMaximum requests allowed in the current window
RateLimit-RemainingRequests remaining in the current window
RateLimit-ResetSeconds until the window resets
Retry-AfterSeconds to wait before retrying (only on 429 responses)

Error Codes

All errors return JSON with flat error and message fields:

{ "error": "invalid_signature", "message": "Request signature verification failed" }

HTTP status codes

StatusMeaning
200Success
400Bad request (invalid parameters)
401Authentication failed
403Forbidden (IP blocked, insufficient scope, feature not enabled)
404Resource not found
413Request body too large (max 100KB)
422Unprocessable (e.g. idempotency key reused with different body)
429Rate limit exceeded
500Internal server error
502Upstream service unavailable
503Service temporarily unavailable

Authentication error codes

CodeDescription
missing_auth_headersRequired headers missing (MX-API-KEY, MX-SIGNATURE, MX-TIMESTAMP, MX-NONCE)
invalid_key_formatKey doesn't match mxn_live_ or mxn_test_ + 24 hex chars
invalid_keyAPI key not found
key_disabledKey disabled by owner
key_suspendedKey suspended by administrator
key_revokedKey permanently revoked
key_expiredKey past expiry date
key_auto_disabledKey auto-disabled due to repeated auth failures
timestamp_skewTimestamp outside 30-second window
nonce_replayNonce already used (possible replay attack)
invalid_signatureHMAC signature verification failed
ip_not_whitelistedClient IP not in key's whitelist
insufficient_scopeKey lacks required permission scope
trading_not_enabledTrading not enabled for this key (contact support)
withdrawals_not_enabledWithdrawals not enabled for this key (contact support)
rate_limit_exceededToo many requests — check RateLimit-* headers

Trading error codes

CodeHTTPDescription
invalid_parameter400Invalid or missing request parameter
same_currency400Base and quote currencies must be different
unsupported_pair400One or more currencies not supported
fiat_disabled400Fiat currency temporarily disabled for new trades
no_network400Could not determine network — specify explicitly
invalid_network400Network not supported for this currency
price_unavailable400Unable to get market price — try again
amount_too_small400Trade amount too small (after spread)
amount_too_large400Amount exceeds maximum allowed
min_amount400Below minimum trade amount for this currency
max_amount400Above maximum trade amount for this currency
daily_limit400Daily trading limit reached
monthly_limit400Monthly trading limit reached
insufficient_balance400Not enough funds to execute trade
quote_expired_or_not_found410Quote has expired or does not exist
quote_already_consumed409Quote has already been executed
idempotency_key_conflict409Idempotency key used with a different quote
per_key_concurrency_limit429Too many concurrent trade executions per API key
service_unavailable503Temporary service issue — retry with backoff

Idempotency

For safe retries on POST requests (orders, withdrawals), include the MX-IDEMPOTENCY-KEY header with a UUID v4.

MX-IDEMPOTENCY-KEY: 550e8400-e29b-41d4-a716-446655440001
Best practice: Always use idempotency keys for trade execution and withdrawal requests. If your connection drops mid-request, you can safely retry without risk of duplicate execution.

Get Balances

GET /v1/account/balances

Returns all wallet balances. Requires read:wallet scope.

Query parameters

ParameterTypeRequiredDescription
currency_typestringNofiat or crypto
include_emptybooleanNoInclude zero-balance wallets (default: true)

Response

{
  "balances": [
    {
      "currency": "BTC",
      "currency_type": "crypto",
      "available": "1.25000000",
      "locked": "0.10000000",
      "pending_in": "0.05000000",
      "pending_out": "0.00000000",
      "total": "1.35000000",
      "usd_value": "88290.15",
      "usd_price": "65400.11"
    },
    {
      "currency": "USD",
      "currency_type": "fiat",
      "available": "50000.00",
      "locked": "0.00",
      "pending_in": "0.00",
      "pending_out": "0.00",
      "total": "50000.00"
    }
  ],
  "timestamp": "2026-04-09T10:30:00.000Z"
}

Get Single Balance

GET /v1/account/balance/{currency}

Returns balance for a specific currency across all wallet types. Requires read:wallet scope.

Response

{
  "currency": "USDT",
  "currency_type": "crypto",
  "total_available": "125000.00000000",
  "total_locked": "5000.00000000",
  "total": "130000.00000000",
  "by_wallet_type": [
    { "wallet_type": "funding", "available": "125000.00000000", "locked": "5000.00000000" }
  ],
  "timestamp": "2026-04-09T10:30:00.000Z"
}

Get Fee Rates

GET /v1/account/fees

Returns your account's fee rates (may differ from defaults for VIP/institutional accounts). Requires read:account scope.

{
  "trading_fee_rate": "0.005",
  "fiat_withdrawal_fee_rate": "0.001",
  "note": "Fee rates expressed as decimals (0.005 = 0.5%)",
  "timestamp": "2026-04-09T10:30:00.000Z"
}

Get Transaction Limits

GET /v1/account/limits

Returns daily and monthly transaction limits by type. Requires read:account scope.

{
  "limits": {
    "bank_deposit": { "min_amount": "100.00", "max_amount": "1000000.00", "daily_limit": "1000000.00", "monthly_limit": "5000000.00" },
    "crypto_withdrawal": { "min_amount": "10.00", "max_amount": "500000.00", "daily_limit": "500000.00", "monthly_limit": "2000000.00" },
    "fiat_withdrawal": { "min_amount": "100.00", "max_amount": "500000.00", "daily_limit": "500000.00", "monthly_limit": "2000000.00" }
  },
  "account_type": "corporate",
  "timestamp": "2026-04-09T10:30:00.000Z"
}

Request Quote (RFQ)

POST /v1/rfq

Request a locked-price quote. Valid for 30 seconds. Requires trade:quote scope.

Zero slippage. Once quoted, the price is locked. You get exactly the quoted price when you accept.
All-in pricing. The quoted price includes our OTC spread. There are no separate fees or hidden charges. The price you see is the price you pay.
Crypto-to-crypto swaps supported. Both crypto/fiat pairs (e.g. BTC/USD) and crypto/crypto pairs (e.g. ETH/BTC) are supported. For swaps, specify from_network and to_network.

Request

// Crypto/fiat trade
{ "base": "BTC", "quote": "USD", "side": "buy", "amount": "1000.00", "amount_type": "quote" }

// Crypto-to-crypto swap
{ "base": "ETH", "quote": "BTC", "side": "buy", "amount": "1.0", "amount_type": "base", "from_network": "eth", "to_network": "btc" }
FieldTypeRequiredDescription
basestringYesBase currency — must be crypto (BTC, ETH, SOL, etc.)
quotestringYesQuote currency — fiat (USD, EUR) or crypto for swaps (BTC, USDT)
sidestringYesbuy (receive base, pay quote) or sell (pay base, receive quote)
amountstringYesTrade amount (decimal string, must be positive)
amount_typestringYesbase or quote — which currency the amount is denominated in
networkstringNoNetwork for the crypto side (e.g. eth, btc, sol). Auto-detected if omitted.
from_networkstringNoSource network for crypto-to-crypto swaps (base currency network). Auto-detected if omitted.
to_networkstringNoDestination network for crypto-to-crypto swaps (quote currency network). Auto-detected if omitted.

Response

{
  "quote_id": "550e8400-e29b-41d4-a716-446655440000",
  "base": "BTC",
  "quote": "USD",
  "side": "buy",
  "network": "btc",
  "price": "72014.25000000",
  "base_amount": "0.01388614",
  "quote_amount": "1000.00",
  "expires_at": "2026-04-09T14:32:18Z",
  "expires_in_ms": 30000
}

For crypto-to-crypto swaps, additional fields are included:

{
  "is_swap": true,
  "from_network": "eth",
  "to_network": "btc",
  ...
}

Response fields

FieldTypeDescription
quote_idstringUnique quote identifier (UUID)
networkstringNetwork identifier for the primary crypto currency
pricestringExecutable price with spread included (all-in, no separate fee)
base_amountstringAmount of base currency in the trade
quote_amountstringAmount of quote currency in the trade
expires_atstringISO 8601 timestamp when the quote expires
expires_in_msintegerMilliseconds until expiry (30000)
is_swapbooleanPresent only for crypto-to-crypto swaps
from_networkstringBase currency network (swaps only)
to_networkstringQuote currency network (swaps only)
Note: The price field is NOT the raw market price. It includes the OTC spread. Market/indicative prices are available via GET /v1/instruments and the price stream.

Accept Quote

POST /v1/rfq/{quote_id}/accept

Execute a previously received quote before it expires. Requires trade:execute scope.

Request

The quote_id is in the URL path. The only body field is the idempotency key — all trade parameters are stored server-side in the quote.

{ "idempotency_key": "550e8400-e29b-41d4-a716-446655440000" }
FieldTypeRequiredDescription
idempotency_keystringYesUUID v4 for safe retries (max 64 chars). Same key returns same result.
Simplified accept. You do not need to echo back price, amounts, or signatures. The server stores the authoritative quote data — just pass the quote_id in the URL and an idempotency key in the body.

Response (200 — filled)

{
  "trade_id": "550e8400-e29b-41d4-a716-446655440001",
  "status": "filled",
  "base": "BTC",
  "quote": "USD",
  "side": "buy",
  "network": "btc",
  "price": "72014.25000000",
  "base_amount": "0.01388614",
  "quote_amount": "1000.00",
  "filled_at": "2026-04-09T14:32:01Z"
}

For crypto-to-crypto swaps, additional fields: is_swap, from_network, to_network.

Get Trade History

GET /v1/trades

Paginated list of executed trades. Requires read:trade scope.

Query parameters

ParameterTypeDescription
basestringFilter by base currency (e.g. BTC)
quotestringFilter by quote currency (e.g. USD)
sidestringFilter by side (buy or sell)
limitintegerResults per page (default 50, max 200)
offsetintegerResults to skip
fromstringStart date (ISO 8601)
tostringEnd date (ISO 8601)

Response

{
  "data": [
    {
      "trade_id": "tr_550e8400...",
      "base": "BTC", "quote": "USD", "side": "buy",
      "price": "72014.25", "base_amount": "0.01388614",
      "quote_amount": "1000.00",
      "status": "filled", "created_at": "2026-04-09T14:32:01Z"
    }
  ],
  "pagination": { "total": 23, "limit": 50, "offset": 0, "has_more": false }
}

Get Trade Detail

GET /v1/trade/{trade_id}

Returns details of a single trade. Requires read:trade scope.

Get Deposit Address

GET /v1/crypto/deposit-address?coin={symbol}&network={network}

Returns (or creates) a deposit address for the specified coin and network. If no network is specified, the default deposit-enabled network is used. Requires read:wallet scope.

Query parameters

ParameterTypeRequiredDescription
coinstringYesCoin symbol (e.g. BTC, ETH, USDT)
networkstringNoNetwork identifier (e.g. btc, eth, sol). If omitted, the default deposit-enabled network is used.

Response

{
  "address": "bc1q59c6n76ta7rgh093mlxr86kvhled77yux6km8r",
  "coin": "BTC",
  "network": "btc",
  "memo_required": false,
  "min_deposit": "0.00010000",
  "required_confirmations": 3,
  "timestamp": "2026-04-12T12:35:46.123Z"
}
Memo/Tag: Some networks (XRP, XLM, ATOM, HBAR) require a memo or tag in addition to the address. Check the memo_required field. Use GET /v1/public/networks?coin=XRP for network details.

Get Crypto Deposits

GET /v1/crypto/deposits

Paginated list of crypto deposits. Requires read:wallet scope. Supports pagination and filters.

Query parameters

ParameterTypeRequiredDescription
coinstringNoFilter by coin (e.g. BTC)
statusstringNodetected, pending_confirmations, confirming, crediting, completed, failed
fromISO 8601NoStart date
toISO 8601NoEnd date
limitintegerNo1-200, default 50
offsetintegerNoDefault 0

Response

{
  "data": [
    {
      "deposit_id": "550e8400-e29b-41d4-a716-446655440000",
      "coin": "BTC",
      "network": "btc",
      "amount": "0.50000000",
      "tx_hash": "abc123def456...",
      "from_address": "1A1zP1eP5QGefi2...",
      "confirmations": 6,
      "required_confirmations": 3,
      "status": "completed",
      "detected_at": "2026-04-09T10:00:00.000Z",
      "updated_at": "2026-04-09T10:45:00.000Z"
    }
  ],
  "pagination": { "total": 5, "limit": 50, "offset": 0, "has_more": false }
}

Submit Crypto Withdrawal

POST /v1/crypto/withdraw

Submit a crypto withdrawal request. The withdrawal enters pending_review status and requires admin approval before on-chain execution. The destination address must be pre-whitelisted and past its 24-hour cooling period. Requires withdraw:crypto scope + allow_withdrawals enabled.

Request

{
  "coin": "BTC",
  "network": "btc",
  "amount": "0.1",
  "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "address_tag": "",
  "idempotency_key": "my-withdrawal-20260412-001"
}

Fields

FieldTypeRequiredDescription
coinstringYesCoin symbol
networkstringYesNetwork identifier
amountstringYesPlain decimal amount (no scientific notation)
addressstringYesMust be in the active withdrawal allowlist
address_tagstringNoMemo/tag (max 100 chars, for chains that require it)
idempotency_keystringYesUnique key for safe retries (1-64 chars)

Response

{
  "withdrawal_id": "550e8400-e29b-41d4-a716-446655440000",
  "reference": "MNX-CW26041200001",
  "status": "pending_review",
  "coin": "BTC",
  "network": "btc",
  "amount": "0.10000000",
  "fee": "0.00050000",
  "total_deducted": "0.10050000",
  "amount_usd": "4215.05",
  "to_address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "message": "Withdrawal request submitted for admin review."
}
Address whitelisting required. Withdrawals can only be sent to pre-whitelisted addresses. New addresses require email confirmation + a 24-hour cooling period before first use. See Add Allowlist Address.

Get Crypto Withdrawals

GET /v1/crypto/withdrawals

Paginated list of crypto withdrawals. Requires read:wallet scope. Same filter parameters as Get Deposits.

Response

{
  "data": [
    {
      "withdrawal_id": "550e8400-e29b-41d4-a716-446655440000",
      "reference": "MNX-CW26041200001",
      "coin": "BTC",
      "network": "btc",
      "amount": "0.10000000",
      "fee": "0.00050000",
      "amount_usd": "4215.05",
      "to_address": "bc1qxy2kgdygjrsqtzq2n0yrf...",
      "tx_hash": null,
      "status": "pending_review",
      "reject_reason": null,
      "created_at": "2026-04-12T11:00:00.000Z",
      "completed_at": null
    }
  ],
  "pagination": { "total": 1, "limit": 50, "offset": 0, "has_more": false }
}

Get Withdrawal Detail

GET /v1/crypto/withdrawals/{uuid}

Returns a single withdrawal with full detail including tx_hash and explorer_url when available. Requires read:wallet scope.

Add Allowlist Address

POST /v1/crypto/withdrawal-addresses

Add a crypto address to the withdrawal allowlist. Triggers an email confirmation. After confirmation, a 24-hour cooling period begins before the address can be used for withdrawals. Requires withdraw:crypto scope.

Request

{
  "currency": "BTC",
  "network": "btc",
  "address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
  "address_tag": "",
  "label": "Cold Storage Vault"
}

Response (201)

{
  "success": true,
  "address_uuid": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending",
  "message": "Confirmation email sent. Click the link to confirm, then wait 24-hour cooling period."
}
Flow: Add address → Check email → Click confirm link → 24h cooling period → Address becomes usable for withdrawals.

List Allowlisted Addresses

GET /v1/crypto/withdrawal-addresses

Returns all addresses in your withdrawal allowlist with status and usage stats. Requires read:wallet scope. Filter by currency, network, or status.

Response

{
  "data": [
    {
      "address_uuid": "550e8400-e29b-41d4-a716-446655440000",
      "currency": "BTC",
      "network": "btc",
      "address": "bc1qxy2kgdygjrsqtzq2n0yrf...",
      "address_tag": null,
      "label": "Cold Storage Vault",
      "status": "active",
      "cooling_until": null,
      "is_usable": true,
      "withdrawal_count": 5,
      "total_withdrawn_amount": "1.50000000",
      "last_withdrawal_at": "2026-04-10T14:30:00.000Z",
      "created_at": "2026-04-01T09:00:00.000Z"
    }
  ],
  "pagination": { "total": 1, "limit": 50, "offset": 0, "has_more": false }
}

Revoke Address

DELETE /v1/crypto/withdrawal-addresses/{uuid}

Permanently revokes an address from the withdrawal allowlist. The address can no longer be used for withdrawals. Requires withdraw:crypto scope.

Response

{
  "success": true,
  "address_uuid": "550e8400-e29b-41d4-a716-446655440000",
  "status": "revoked"
}

Webhooks

Register HTTPS endpoints to receive push notifications for trades, deposits, withdrawals, and account events.

How it works

  1. Register a webhook URL via POST /v1/webhooks
  2. Subscribe to specific event types
  3. Mintarex signs each delivery with HMAC-SHA256 using your webhook secret
  4. Your server verifies the signature and responds with 200 OK within 10 seconds
  5. Failed deliveries retry with exponential backoff (30s, 1m, 2m, 4m, 8m, 16m, 32m, 64m — max 8 attempts)

Webhook delivery headers

HeaderDescription
X-Mintarex-SignatureHMAC-SHA256 of the request body using your webhook secret
X-Mintarex-TimestampUnix timestamp of the delivery
X-Mintarex-EventEvent type (e.g. trade.executed)

Delivery payload

{
  "id": "evt_550e8400...",
  "type": "trade.executed",
  "created_at": "2026-04-09T14:32:01Z",
  "data": {
    "trade_id": "tr_550e8400...",
    "base": "BTC", "quote": "USD",
    "price": "65432.10", "base_amount": "0.01521"
  }
}

Webhook Event Types

EventDescription
trade.executedRFQ trade was filled successfully
deposit.detectedCrypto deposit seen on-chain (unconfirmed)
deposit.confirmedDeposit reached required confirmations and credited to balance
withdrawal.requestedCrypto withdrawal submitted and pending admin approval
withdrawal.approvedWithdrawal approved by admin, queued for broadcast
withdrawal.completedWithdrawal confirmed on-chain
withdrawal.cancelledWithdrawal was cancelled or rejected

Verifying Webhook Signatures

Always verify the X-Mintarex-Signature header before processing.

Node.js

const crypto = require('node:crypto');

function verifyWebhook(rawBody, signature, secret) {
  const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
}

app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
  if (!verifyWebhook(req.body, req.headers['x-mintarex-signature'], WEBHOOK_SECRET)) {
    return res.status(401).send('Invalid signature');
  }
  const event = JSON.parse(req.body);
  console.log(event.type, event.data);
  res.status(200).send('OK');
});

Python

import hmac, hashlib

def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(secret.encode(), body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(signature, expected)

# In Flask:
@app.route('/webhook', methods=['POST'])
def webhook():
    if not verify_webhook(request.data, request.headers['X-Mintarex-Signature'], WEBHOOK_SECRET):
        return 'Invalid signature', 401
    event = request.json
    print(event['type'], event['data'])
    return 'OK', 200

Real-time Streaming (SSE)

Mintarex provides Server-Sent Events (SSE) streams for real-time price updates and account events. SSE uses a two-step authentication flow:

  1. Get a stream tokenPOST /v1/stream/token (authenticated via HMAC like any other endpoint)
  2. Connect to SSE — Pass the token as a query parameter. The SSE endpoints are on the main API, not the gateway.

Step 1: Get Stream Token

POST /v1/stream/token

Returns a single-use, 60-second token. Requires stream:market or stream:account scope.

// Response
{ "token": "a1b2c3d4e5f6...64 hex chars...", "expires_in": 60 }

Step 2: Connect to Price Stream

GET https://api.mintarex.com/api/v1/corporate-stream/prices?token={token}&instruments=BTC_USD,ETH_USD

Real-time price updates. Requires stream:market scope.

ParameterTypeDescription
tokenstringStream token from Step 1
instrumentsstringComma-separated pairs (e.g. BTC_USD,ETH_EUR,SOL_AED)

Step 2 (alt): Connect to Account Stream

GET https://api.mintarex.com/api/v1/corporate-stream/account?token={token}

Real-time account events (trade fills, deposits, withdrawals). Requires stream:account scope.

Account stream event types

Node.js example

const EventSource = require('eventsource');

// Step 1: Get token (using your signedRequest helper)
const tokenReq = signedRequest('POST', '/v1/stream/token', '');
const tokenRes = await fetch(BASE + '/v1/stream/token', {
  method: 'POST', headers: tokenReq.headers,
});
const { token } = await tokenRes.json();

// Step 2: Connect to SSE (note: different host — api.mintarex.com, not institutional)
const url = `https://api.mintarex.com/api/v1/corporate-stream/prices?token=${token}&instruments=BTC_USD,ETH_USD`;
const es = new EventSource(url);

es.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log(data.instrument, data.price, data.change_24h);
};

Stream limits

Changelog

2026-04-13 — v1.1.0

2026-04-09 — v1.0.0 (Initial Release)

Support

Need help integrating? We're here to help.

Response time: We typically respond to integration support requests within 4 business hours.

© 2026 Mintarex. All rights reserved.