Developers · api_version 2026-09-24
Webhooks
Premium members can push BetCrypto signals into their own systems: price alerts, whale trades from wallets they follow (copy-trading) and portfolio settlements. Each event is an HTTPS POST of signed JSON.
Quick start
- Add an HTTPS endpoint in Settings → Webhooks. You can add auth headers (bearer token, API key or basic auth).
- Copy the signing secret. It is shown once, and you can rotate it any time.
- Verify each request's signature and answer 2xx within the timeout. Send a test ping to activate the endpoint.
- Create a price alert or a trade alert and choose Webhook as its channel.
Events
- trade_alert.triggered
- A whale trade matches a trade alert: a wallet you follow, or a size threshold. Webhook trade alerts have no cooldown, so every matching fill is sent.
- price_alert.triggered
- A price alert crosses its threshold. Repeat alerts send one message per fresh crossing.
- position.settled
- An event you hold a position on settles. Turn this on per endpoint.
- ping
- A test message from Settings → Webhooks.
The request
Every event is a POST with a JSON envelope: id, type, api_version, created_at and the event data. The id equals the webhook-id header and stays the same across retries. Your endpoint should answer 2xx within the timeout (5 s by default, up to 10 s); the response body is ignored.
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: BetCrypto-Webhooks/1.0 (+https://betcrypto.now/developers/webhooks)
webhook-id: msg_01k8x3t2a9c4v7b5n6m1q0r2s3
webhook-timestamp: 1758736186
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=
Authorization: Bearer <your token, if you added one>Verify the signature
Requests are signed with your endpoint's secret using the Standard Webhooks scheme, so any Standard Webhooks library can verify them. Or use one of the snippets below. Always verify against the raw request body, reject timestamps more than 5 minutes old, and dedupe on webhook-id.
import crypto from 'node:crypto';
// secret: the whsec_… value from Settings → Webhooks.
// rawBody: the request body exactly as received (don't re-serialize JSON).
export function verifyBetCryptoWebhook(
secret,
headers,
rawBody,
toleranceSeconds = 300,
) {
const id = headers['webhook-id'];
const timestamp = headers['webhook-timestamp'];
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > toleranceSeconds) {
return false;
}
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = crypto
.createHmac('sha256', key)
.update(`${id}.${timestamp}.${rawBody}`)
.digest('base64');
return (headers['webhook-signature'] ?? '').split(' ').some((part) => {
const [version, signature = ''] = part.split(',');
return (
version === 'v1' &&
signature.length === expected.length &&
crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
)
);
});
}
Example payloads
Fields may be added over time, but none are renamed or removed within an api_version. Ignore fields you don't recognise.
trade_alert.triggered
{
"id": "msg_01k8x3t2a9c4v7b5n6m1q0r2s3",
"type": "trade_alert.triggered",
"api_version": "2026-09-24",
"created_at": "2026-09-24T09:12:04Z",
"data": {
"alert": {
"id": 42,
"type": "trader",
"filters": {
"trader_address": "0x8a8685a792c184e5c2ee8c7d4d4baba7c2c94998",
"trader_display_name": "BrotherObama",
"min_size_usd": null,
"side": null,
"platform_id": null,
"category_id": null,
"market_event_id": null
}
},
"trade": {
"id": 649976,
"tx_hash": "0x4f1c9e7a2b3d5f60718293a4b5c6d7e8f90a1b2c3d4e5f60718293a4b5c6d7e8",
"side": "buy",
"price": 0.46,
"size_usd": 86848.86,
"shares": 188801.869565,
"traded_at": "2026-09-23T17:49:45Z",
"observed_at": "2026-09-23T17:49:46Z",
"whale_trigger": "absolute"
},
"trader": {
"address": "0x8a8685a792c184e5c2ee8c7d4d4baba7c2c94998",
"display_name": "BrotherObama",
"profile_url": "https://betcrypto.now/go/t/649976"
},
"platform": {
"slug": "polymarket",
"name": "Polymarket"
},
"outcome": {
"id": 318842,
"name": "Over",
"venue_outcome_id": "52434866333180586246150525701655085190090468278272175676426834184679515458316",
"venue_event_id": "1038129",
"current_price": 0.46,
"best_bid": 0.45,
"best_ask": 0.46,
"venue_url": "https://betcrypto.now/go/e/881203"
},
"market": {
"id": 120455,
"label": "Total runs 8.5",
"is_primary": false
},
"event": {
"id": 51230,
"slug": "arizona-vs-colorado",
"title": "Arizona vs Colorado",
"starts_at": "2026-09-23T20:40:00Z",
"url": "https://betcrypto.now/events/arizona-vs-colorado"
}
}
}price_alert.triggered
{
"id": "msg_01k8x41mdw2c9y3f8h4j6k7l8m",
"type": "price_alert.triggered",
"api_version": "2026-09-24",
"created_at": "2026-09-24T09:12:04Z",
"data": {
"alert": {
"id": 17,
"condition": "price_above",
"threshold": 0.6,
"repeat": true,
"fired_count": 3
},
"observed_price": 0.62,
"outcome": {
"id": 204117,
"name": "Spain"
},
"market": {
"id": 98012,
"label": null,
"is_primary": true
},
"event": {
"id": 40211,
"slug": "spain-vs-argentina",
"title": "Spain vs Argentina",
"starts_at": "2026-10-04T19:00:00Z",
"url": "https://betcrypto.now/events/spain-vs-argentina"
},
"venues": [
{
"platform": {
"slug": "polymarket",
"name": "Polymarket"
},
"venue_event_id": "1041877",
"venue_outcome_id": "71321045863084981365469209934385961187049862946213471596312356021487412803110",
"current_price": 0.62,
"best_bid": 0.61,
"best_ask": 0.62,
"price_changed_at": "2026-09-24T09:12:03Z",
"venue_url": "https://betcrypto.now/go/e/901554"
},
{
"platform": {
"slug": "kalshi",
"name": "Kalshi"
},
"venue_event_id": "KXFIFAGAME-26OCT04ESPARG",
"venue_outcome_id": "KXFIFAGAME-26OCT04ESPARG-ESP_YES",
"current_price": 0.6,
"best_bid": 0.59,
"best_ask": 0.6,
"price_changed_at": "2026-09-24T09:11:41Z",
"venue_url": "https://betcrypto.now/go/e/901561"
}
],
"triggered_at": "2026-09-24T09:12:04Z"
}
}position.settled
{
"id": "msg_01k8x4b7qz5r1t9w2e8u6i4o3p",
"type": "position.settled",
"api_version": "2026-09-24",
"created_at": "2026-09-24T09:12:04Z",
"data": {
"event": {
"id": 40211,
"slug": "spain-vs-argentina",
"title": "Spain vs Argentina",
"starts_at": "2026-10-04T19:00:00Z",
"url": "https://betcrypto.now/events/spain-vs-argentina",
"settled_at": "2026-10-04T21:10:00Z",
"abandoned_at": null,
"winning_outcome": {
"id": 204117,
"name": "Spain"
}
},
"positions": [
{
"account_type": "paper",
"outcome_name": "Spain",
"won": true,
"void": false,
"abandoned": false,
"shares": 250,
"payout": 250
}
],
"totals": {
"payout": 250
}
}
}ping
{
"id": "msg_01k8x4c2hv6n8b1m3k5j7g9f0d",
"type": "ping",
"api_version": "2026-09-24",
"created_at": "2026-09-24T09:12:04Z",
"data": {
"message": "BetCrypto webhook test",
"endpoint": {
"id": 3,
"name": "Copy-trade bot"
}
}
}Delivery and retries
- Each message gets 6 attempts by default (you can set 1 to 8). Retries wait 30 s, 2 min, 10 min, 1 h, 3 h, 6 h, 12 h after each failed attempt.
- Timeouts, connection errors and 408, 425, 429 and 5xx responses are retried. A Retry-After header is honoured, up to 1 h.
- Other 4xx responses are final, and so are 3xx responses: redirects are never followed.
- A 410 Gone disables the endpoint, and so do 20 failed deliveries in a row. You get a notice, and a successful test ping re-enables it.
- Delivery is at-least-once and unordered. Use webhook-id to dedupe and the timestamps in data to order events.
- Set "Drop if older than" on an endpoint to discard retries that would arrive too late to act on.
Copy-trading notes
- Only whale-size trades are captured, using per-venue size floors. Smaller trades by a wallet you follow are not delivered.
- Polymarket trades usually arrive within a few seconds; Azuro and Limitless trades within several minutes. Kalshi and Gemini trades are anonymous, so there is no wallet to follow on those venues, but size alerts still work there.
- We keep the largest fill of each transaction. If a larger fill of the same transaction arrives after the first, usually within seconds, you get a second message for it. Dedupe on trade.tx_hash plus trader.address if you want one action per wallet per transaction.
- outcome.venue_outcome_id is the venue's own id (the Polymarket CLOB token id, or the Kalshi market ticker), so a bot can place a matching order directly.
- Signals are informational. You are responsible for any orders your systems place.
Limits
Up to 5 endpoints per account and 5 custom headers per endpoint, 10,000 deliveries per day, and a 30-day delivery log with one-click redelivery.
Security
- HTTPS only. URLs that resolve to private or internal network addresses are refused, and the check runs again before every send.
- Redirects are never followed.
- Endpoint URLs, signing secrets and header values are encrypted at rest and never shown again after you save them.