BuyCrypt
For bot developers

Connect your trading bot. Same Binance API.

If your bot already speaks Binance USD-M Futures, swap one base URL and you're live on BuyCrypt. Our users discover your bot in the leaderboard, try it on a free $1,000 demo account, and graduate to real keys when they're convinced.

No code rewrite. No exchange-specific quirks. Same HMAC-SHA256 signing, same WebSocket user-data stream, same order types — wrapped over our demo trading engine so users can compare bots without putting capital at risk.

Why BuyCrypt

Demo-first distribution for trading bots

Free $1,000 demo per user

Each user that subscribes gets their own demo account with $1,000 virtual balance. Your bot trades it. They watch live PnL, drawdown, win rate. No risk to their real capital — and no acquisition cost to you.

Leaderboard exposure

Your bot's own demo account shows up in the public leaderboard with a 🤖 BOT badge. Users see PnL ranking among real traders. Tap the row, hit "Connect" — instant subscription.

Zero code rewrite

If your bot uses any Binance Futures SDK (binance-connector-python, python-binance, CCXT, etc.), point its base_url at https://api.buycrypt.com/fapi. HMAC, recvWindow, order types — identical.

Webhook-delivered keys

When a user subscribes, we POST the freshly minted API key + secret to your webhook URL — HMAC-signed, retried on failure with exponential backoff, dead-letter recoverable. You never have to ask the user for credentials.

User journey

How users discover and subscribe to your bot

1

Discover

User opens the leaderboard. Your bot's account appears with a 🤖 BOT chip next to the username, plus subscriber count. They tap the row.

2

Try on demo

On the bot's profile page they see "Connect to demo account". One tap creates a new $1,000 demo for them and your bot starts trading on it.

3

Watch + decide

User watches the bot trade their demo for days or weeks. PnL, positions, win rate — all live in the app. If they like it, they enter their real Binance API key inside BuyCrypt to upgrade from demo to production.

4

Graduate

The user adds their real Binance API key inside BuyCrypt. We deliver it to your webhook the same way we delivered the demo key — same subscription.created event, just with their production credentials. One integration covers both demo and live flows.

Developer onboarding

How to add your bot to BuyCrypt

1

Sign up

Register a regular BuyCrypt account — same flow as any user. You'll be issued a free demo account with a starting balance the moment you sign up. No application form, no review queue.

2

Trade your demo via API

Generate API keys right inside the BuyCrypt mobile app: My Profile → API keys → tap your demo account → Generate. You'll get a fresh apiKey + apiSecret shown once — copy them into your bot. Then point your existing Binance USD-M Futures bot at https://api.buycrypt.com/fapi and it just works — same HMAC, same endpoints, same order types.

3

Auto-flagged as a bot

As soon as we detect API-driven trading on your demo, your account is automatically flagged as a bot. Your demo shows up in the leaderboard with the 🤖 BOT chip and a public profile, so other users can discover and subscribe to it.

4

(Optional) Accept subscribers via webhook

When you're ready to take subscribers, add a webhook URL to your bot profile in settings. Every time a user subscribes — on demo or with real Binance keys — we POST a subscription.created event with the API key + secret for that user's account. Store (userId, apiKey, apiSecret) and start trading on their behalf.

Drop-in

Your existing Binance Futures bot, reused

Most Binance Futures SDKs accept a custom base URL. Here's the entire change a typical Python bot needs:

# before — trading on real Binance
from binance.um_futures import UMFutures
client = UMFutures(key=API_KEY, secret=API_SECRET)

# after — trading on a BuyCrypt user's demo
from binance.um_futures import UMFutures
client = UMFutures(
    key=API_KEY,
    secret=API_SECRET,
    base_url="https://api.buycrypt.com/fapi",   # <- the only line that changes
)

# everything below is identical to your existing code
client.new_order(symbol="BTCUSDT", side="BUY", type="MARKET", quantity=0.01)
client.account()
client.cancel_open_orders(symbol="BTCUSDT")

CCXT, python-binance, Go's go-binance, JS node-binance-api — all work the same way. Plain requests.get(...).headers["X-MBX-APIKEY"] = key bots also work; HMAC signing rule is byte-identical.

Webhook

Register your bot — full self-service

Everything below is copy-paste-friendly Python. No admin involvement except final Approve. Tested on macOS, Linux, Windows (Python 3.10+, requests).

TL;DR: sign up → tap «Generate API key» on your demo → generate a webhook secret → run one Python script → admin approves. ~5 minutes.

Step 1 — Sign up

Open buycrypt.com/terminal (or the mobile app). Sign in via Google or phone. A $1,000 USDT demo account is created automatically — that's the sandbox your bot will trade on.

Step 2 — Generate your trading API key (in the app)

In the app: Profile → API Keys → tap your demo account → «Bot API keys» → Generate. A dialog shows the apiKey + apiSecret once — copy both, you can't recover the secret afterwards.

# what you'll save
API_KEY    = "3fc370a4ac94b9aa756e2a97842dc04c"
API_SECRET = "3WJ86kV_VQzLWaED3hSRlY9R6wQHh_f3UEZ_q-CrwRU"

Same shape as Binance — your existing USD-M Futures bot library reads it as a regular Binance key with base URL https://api.buycrypt.com/fapi.

Step 3 — Generate your webhook signing secret

A second secret, separate from API_SECRET. We use it to sign the events we POST to your webhook URL — so you can verify the request really came from us. You generate it on your side; we encrypt-store it but never reveal back:

# any of these works — pick one. Save the output.
$ openssl rand -hex 32                          # macOS / Linux
$ python3 -c "import secrets; print(secrets.token_hex(32))"   # cross-platform

# sample output
8a31c2f4d5e6789abc01234567890def8a31c2f4d5e6789abc01234567890def

Step 4 — Register the bot (one signed POST)

Drop your three secrets into one signed POST. Save this as register_bot.py and run it once. Works on any OS with Python 3.10+ and pip install requests:

import hmac, hashlib, time, urllib.parse, requests

# ─── fill these in from steps 2 and 3 ─────────────────────
API_KEY        = "3fc370a4ac94b9aa756e2a97842dc04c"
API_SECRET     = "3WJ86kV_VQzLWaED3hSRlY9R6wQHh_f3UEZ_q-CrwRU"
WEBHOOK_SECRET = "8a31c2f4d5e6789abc01234567890def8a31c2f4d5e6789abc01234567890def"

SLUG         = "alpha-trader"                                # lowercase, [a-z0-9-], unique
DISPLAY_NAME = "Alpha Trader"                                # shown to users in the bot list
WEBHOOK_URL  = "https://your-bot.example.com/buycrypt/hook"   # MUST be HTTPS, public
# ──────────────────────────────────────────────────────────

params = {
    "slug":            SLUG,
    "displayName":     DISPLAY_NAME,
    "webhookUrl":      WEBHOOK_URL,
    "webhookSecret":   WEBHOOK_SECRET,
    "defaultLeverage": 10,
    "timestamp":       int(time.time() * 1000),
}
qs  = urllib.parse.urlencode(params)
sig = hmac.new(API_SECRET.encode(), qs.encode(), hashlib.sha256).hexdigest()

r = requests.post(
    "https://api.buycrypt.com/fapi/v1/bot/profiles",
    headers={
        "X-MBX-APIKEY":   API_KEY,
        "Content-Type":  "application/x-www-form-urlencoded",
    },
    data=qs + f"&signature={sig}",
)
print(r.status_code, r.json())

Notice no demoKeyPairId — your API_KEY already points to the demo it was generated for. Need to register against a different demo? Generate a separate API key for it (step 2 again).

Expected output:

201 {
  "botId": 42,
  "slug": "alpha-trader",
  "status": "PENDING_REVIEW",
  "webhookUrl": "https://your-bot.example.com/buycrypt/hook",
  "webhookStatus": "ACTIVE",
  "webhookSecretAutoGenerated": false
}

Common errors:

{"code":-2010, "msg":"Slug already in use."}                # pick another slug
{"code":-1102, "msg":"webhookSecret must be at least 32..."}  # too short — use 32+ chars
{"code":-1022, "msg":"Signature for this request is not valid."}  # API_SECRET wrong, or params order changed after signing
{"code":-1003, "msg":"Bot registration rate limit: 1 per hour..."}  # wait it out

Step 5 — Wait for approve, then receive events

Bot lands in PENDING_REVIEW. Our admin sees your request immediately and approves it. To check status anytime:

import hmac, hashlib, time, urllib.parse, requests

API_KEY    = "3fc370a4..."
API_SECRET = "3WJ86kV..."

qs  = urllib.parse.urlencode({"timestamp": int(time.time() * 1000)})
sig = hmac.new(API_SECRET.encode(), qs.encode(), hashlib.sha256).hexdigest()
r = requests.get(
    f"https://api.buycrypt.com/fapi/v1/bot/profiles?{qs}&signature={sig}",
    headers={"X-MBX-APIKEY": API_KEY},
)
print(r.json())   # [{"botId":42, "status":"ACTIVE", ...}] when approved

Once "status":"ACTIVE": every time a user subscribes we POST to your webhookUrl with the per-subscriber API key your bot will trade with. The events shape and a copy-paste signature-verification snippet are right below this section.

Lost your webhook secret? Pick a new one and rotate (same signing pattern as registration):

params = {
    "webhookSecret": NEW_SECRET,                     # or omit to get backend-generated
    "timestamp": int(time.time() * 1000),
}
qs  = urllib.parse.urlencode(params)
sig = hmac.new(API_SECRET.encode(), qs.encode(), hashlib.sha256).hexdigest()
requests.post(
    "https://api.buycrypt.com/fapi/v1/bot/profiles/42/webhook/rotate-secret",
    headers={"X-MBX-APIKEY": API_KEY,
             "Content-Type": "application/x-www-form-urlencoded"},
    data=qs + f"&signature={sig}",
)

Old secret invalid the second the new one saves. Rate limit: 10 rotations/hour per bot.

Webhook event shapes (what we POST to you)

subscription.created — user just subscribed

POST https://your-bot.example.com/buycrypt/hook
Content-Type: application/json
User-Agent:               BuyCrypt-Webhook/1.0
X-BuyCrypt-Event:         subscription.created
X-BuyCrypt-Delivery-Id:   7f3b2e8c-a4d1-4f12-93b8-0c1e9f8d2a4b   # dedup key — store + reject duplicates
X-BuyCrypt-Timestamp:     1745605200123   # ms epoch when payload was built
X-BuyCrypt-Signature:     5e8c2d…         # hex(HMAC-SHA256(webhookSecret, ts + "." + body))

{
  "event":        "subscription.created",
  "deliveryId":   "7f3b2e8c-a4d1-4f12-93b8-0c1e9f8d2a4b",
  "timestamp":    1745605200123,
  "data": {
    "subscriptionId":   42,
    "botProfileId":     7,
    "subscriberHandle": "user_fb_uid_a",
    "apiKey":           "3fc370a4ac94b9aa756e2a97842dc04c",
    "apiSecret":        "3WJ86kV_VQzLWaED3hSRlY9R6wQHh_f3UEZ_q-CrwRU",
    "demoKeyPairId":    9001,
    "initialBalance":   "1000.00000000",
    "permissions":      "READ_TRADE",
    "ipWhitelist":      "",
    "createdAt":        "2026-04-25T18:00:00Z"
  }
}

subscription.disconnected — user disconnected, key disabled

X-BuyCrypt-Event: subscription.disconnected

{
  "event":      "subscription.disconnected",
  "deliveryId": "…",
  "timestamp":  …,
  "data": {
    "subscriptionId":  42,
    "botProfileId":    7,
    "apiKey":          "3fc370a4…",
    "reason":          "USER_UNSUBSCRIBED",
    "disconnectedAt":  "2026-04-25T18:30:00Z"
  }
}

secret.regenerated — user/admin rotated the subscription's API secret

X-BuyCrypt-Event: secret.regenerated

{
  "event":      "secret.regenerated",
  "deliveryId": "…",
  "timestamp":  …,
  "data": {
    "subscriptionId":       42,
    "apiKey":               "3fc370a4…",
    "newApiSecret":         "new_plaintext_keep_old_valid_24h",
    "oldSecretValidUntil":  "2026-04-26T18:00:00Z"
  }
}

Verify the signature (constant-time)

Use the raw request body bytes — DO NOT parse JSON first and re-serialize, you'll lose key order and signatures will diverge. Always compare in constant time.

# Python (FastAPI / Flask)
import hmac, hashlib, time

WEBHOOK_SECRET = "whsec_aGVsbG8gd29ybGQ_..."   # whsec_…43 chars

def verify(headers, body_bytes):
    sig = headers["X-BuyCrypt-Signature"]
    ts  = int(headers["X-BuyCrypt-Timestamp"])

    # anti-replay: reject if too old
    if abs(time.time() * 1000 - ts) > 5 * 60 * 1000:
        return False

    payload = f"{ts}.".encode() + body_bytes
    expected = hmac.new(WEBHOOK_SECRET.encode(), payload, hashlib.sha256).hexdigest()

    return hmac.compare_digest(expected, sig)
// Node.js / Express — read RAW body, not parsed JSON
const crypto = require('crypto');
const SECRET = 'whsec_aGVsbG8gd29ybGQ_...';

app.post('/buycrypt/hook',
  express.raw({ type: 'application/json' }),   // raw, not json()!
  (req, res) => {
    const sig = req.headers['x-buycrypt-signature'];
    const ts  = parseInt(req.headers['x-buycrypt-timestamp'], 10);

    if (Math.abs(Date.now() - ts) > 5 * 60 * 1000) return res.status(401).end();

    const expected = crypto.createHmac('sha256', SECRET)
      .update(`${ts}.`).update(req.body).digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
      return res.status(401).end();
    }
    // req.body is a Buffer here — JSON.parse(req.body.toString('utf8'))
    res.sendStatus(200);
});

Retry policy

6 attempts at offsets 0s, 10s, 30s, 2m, 10m, 1h. We treat 4xx (except 408 / 429) as a deliberate refusal and stop retrying. After all attempts fail, the payload lands in dead-letter and you can replay via POST /api/v6/bots/profiles/{id}/webhook/replay?since=<ms>.

Auto-disable

10 consecutive failed deliveries → webhookStatus = DISABLED, no further events. Fix your endpoint, then PUT /webhook with the same URL to reset the counter (URL change auto-resets it too). Test with POST /webhook/test before going live.

Idempotency

X-BuyCrypt-Delivery-Id is a UUID stable across retries. Store seen IDs and short-circuit duplicates — we WILL retry on transient failures. Always reply 2xx within 10s; longer than that we treat as timeout and retry.

API surface

Every Binance USD-M Futures method we support

Base URL: https://api.buycrypt.com/fapi. Auth: X-MBX-APIKEY header + HMAC-SHA256 signature on signed endpoints. Recv window: 5,000 ms default, 60,000 ms max.

Public market data (no auth)

GET
/v1/ping
Health check. Returns {}.
GET
/v1/time
Server time in ms — sync your clock to avoid recvWindow mismatches.
GET
/v1/exchangeInfo
Symbols, filters, precision, leverage tiers.
GET
/v1/ticker/price · /ticker/24hr · /ticker/bookTicker
Last trade price · 24h stats · best bid/ask.
GET
/v1/depth?symbol=&limit=
Order-book snapshot (limit ∈ {5,10,20,50,100,500,1000}).
GET
/v1/klines
OHLCV candlesticks for any interval (1m, 5m, 1h, 4h, 1d).
GET
/v1/markPrice · /v1/fundingRate · /v1/premiumIndex
Mark price for liquidation calc · funding history · premium index.
GET
/v1/trades · /v1/aggTrades
Recent trades and aggregated trades.

Account & positions (signed)

GET
/v1/account · /v2/account · /v3/account
Full account snapshot — balances, total margin, per-asset, current positions. All three path versions return the same payload (Binance compat).
GET
/v2/balance · /v3/balance
Per-asset wallet balance. We emit USDT only.
GET
/v2/positionRisk · /v3/positionRisk
Active positions with mark price, entry price, unrealised PnL, liquidation price.
GET
/v1/userTrades · /v1/income · /v1/commissionRate · /v1/leverageBracket
Trade history · realised income · per-symbol fee rate · leverage tiers.

Trading (signed)

POST
/v1/order
Place an order. Supports MARKET, LIMIT, STOP_MARKET, TAKE_PROFIT_MARKET, STOP, TAKE_PROFIT (limit-flavor), TRAILING_STOP_MARKET. Flags: reduceOnly, closePosition, timeInForce (GTC/IOC/FOK), newClientOrderId for idempotency, workingType, callbackRate.
GET
/v1/order
Query a single order by orderId or origClientOrderId.
DELETE
/v1/order
Cancel a single order.
DELETE
/v1/allOpenOrders?symbol=X
Cancel every open order on a symbol in one call.
GET
/v1/openOrders
All currently active (non-filled, non-canceled) orders.
GET
/v1/allOrders
Order history. Latest 500 by default.
POST
/v1/leverage · /v1/marginType · /v1/positionMargin
Set leverage (1×–125×) · ISOLATED/CROSS · top up isolated margin.
GET
/v1/positionSide/dual
Hedge-mode flag. We're one-way only for now — returns {dualSidePosition: false}.

User-data WebSocket (private)

POST
/v1/listenKey
Create a 60-min listenKey. Use it to authenticate the WS handshake.
PUT
/v1/listenKey
Extend the listenKey lease another 60 min. Call every < 60 min to keep the stream alive.
DELETE
/v1/listenKey
Close the listenKey when shutting down.
WS
wss://api.buycrypt.com/fapi/ws/<listenKey>
Push events for orders, balance, positions: ORDER_TRADE_UPDATE, ACCOUNT_UPDATE, MARGIN_CALL. Same payload shape as Binance.

Market-data WebSocket (public)

WS
wss://api.buycrypt.com/fapi/stream?streams=btcusdt@trade/btcusdt@kline_1m
Multi-stream subscription. Supports subscribe/unsubscribe/list_subscriptions control messages — passthrough to upstream Binance.
Limits & errors

Rate limits and error codes

Per-API-key buckets

  • Weight: 2,400 / minute
  • Order placement: 300 / 10 seconds
  • Signed reads: 1,200 / minute
  • Headers on every reply: X-MBX-USED-WEIGHT-1M, X-MBX-ORDER-COUNT-10S, Retry-After on 429.

Common error codes

  • -1003 Too many requests — back off via Retry-After
  • -1021 Timestamp outside recvWindow
  • -1022 Invalid signature
  • -1102 Mandatory parameter not sent
  • -1116 Invalid order type
  • -2010 Order rejected (insufficient balance, etc.)
  • -2011 Unknown order
  • -2014 API-key format invalid
  • -2015 Invalid API-key, IP, or permissions
  • -2019 Margin insufficient

Ready to plug your bot in?

No application form, no review queue, no integration fees. Sign up like any user, generate API keys in the mobile app, point your bot at https://api.buycrypt.com/fapi — that's the entire onboarding.