# ABP Authentication - API Key Setup
Source: https://docs.55-tech.com/abp-api/authentication
Learn how to authenticate ABP API requests using the X-API-Key header. API key resolution, rate limiting, and WebSocket authentication.
All ABP endpoints require authentication via the `X-API-Key` header.
## How to authenticate
Pass your API key in the `X-API-Key` request header:
```bash theme={null}
curl -H "X-API-Key: your-api-key" \
https://v2.55-tech.com/accounts
```
## API key metadata
Each API key is associated with:
| Field | Description |
| ------------ | ------------------------------------------------ |
| `clientName` | Your client identifier (used for filtering data) |
| `bookmakers` | Allowed bookmaker slugs (empty = all) |
| `rateLimit` | Requests per minute (default: 60) |
| `enabled` | Whether this key is active |
Your API key determines which accounts, orders, and bets you can access. All data is filtered by your `clientName`.
## WebSocket authentication
For WebSocket connections, authenticate via the login message after connecting:
```json theme={null}
{
"type": "login",
"apiKey": "your-api-key",
"channels": []
}
```
You must send the login message within **30 seconds** of connecting, or the connection is closed. See [WebSocket](/abp-api/websocket) for details.
## Rate limiting
Rate limits are enforced per API key using a sliding window.
* **Default:** 60 requests per minute
* **Exceeded:** Returns `429 Too Many Requests`
* **WebSocket:** Maximum 5 concurrent connections per API key
Implement exponential backoff when receiving `429` responses.
## Error responses
| Status | Meaning |
| ------ | ---------------------------------------------------------------------- |
| `401` | Missing or invalid API key |
| `403` | API key inactive or expired; or resource belongs to a different client |
| `429` | Rate limit exceeded |
**401 — missing key:**
```json theme={null}
{"detail": {"message": "Missing X-API-Key header", "code": "UNAUTHORIZED"}}
```
**401 — invalid key:**
```json theme={null}
{"detail": {"message": "Invalid API key", "code": "UNAUTHORIZED"}}
```
**403 — inactive/expired key:**
```json theme={null}
{"detail": {"message": "API key is inactive", "code": "FORBIDDEN"}}
```
# ABP Error Handling
Source: https://docs.55-tech.com/abp-api/errors
ABP API error codes, validation errors, order decline reasons, and resilience patterns. Learn how to handle errors in your integration.
## Error response format
All API errors return a JSON body:
```json theme={null}
{
"detail": {"message": "Invalid API key", "code": "UNAUTHORIZED"}
}
```
Validation errors include field-level detail:
```json theme={null}
{
"detail": [
{
"loc": ["body", "orders", 0, "orderStake"],
"msg": "Input should be greater than 0",
"type": "greater_than"
}
]
}
```
## HTTP status codes
| Status | Description |
| ------ | ------------------------------------------------------------------------------- |
| `200` | Success |
| `201` | Resource created |
| `204` | Resource deleted (no content) |
| `400` | Bad request — invalid parameters |
| `401` | Unauthorized — missing or invalid API key |
| `403` | Forbidden — API key inactive/expired, or resource belongs to a different client |
| `404` | Not found — resource does not exist |
| `409` | Conflict — duplicate `requestUuid` already in-flight |
| `422` | Validation error — request body failed validation |
| `429` | Rate limited — exceeded requests per minute |
| `500` | Internal server error |
| `503` | Service unavailable — database or dependency down |
## Order decline reasons
When placing orders via `POST /place-orders`, orders that fail business validation are returned in the `declinedOrders` array (not as HTTP errors). Each declined order includes a `declineReason`:
| Decline reason | Description |
| -------------------------- | --------------------------------------------------------------- |
| Stake exceeds limit | `orderStake` is higher than the available bookmaker limit |
| Stake below minimum | `orderStake` is below the bookmaker's or account's minimum |
| Invalid odds | `orderPrice` is not available or market is suspended |
| No active accounts | No active bookmaker accounts available for this market |
| Currency conversion failed | Could not convert between order currency and bookmaker currency |
| Bookmaker not available | Specified bookmaker doesn't have odds for this fixture/outcome |
| Duplicate requestUuid | This `requestUuid` is already in-flight (5-minute dedup window) |
**Example declined order response:**
```json theme={null}
{
"status": "declined",
"acceptedOrders": [],
"declinedOrders": [
{
"requestUuid": "fb5f2dd9-c855-4ba9-8ef9-4c2278ca2f1d",
"fixtureId": "id1000000861624412",
"outcomeId": 161,
"declineReason": "Order stake 15000.00 USD exceeds available limit 5000.00 USD"
}
]
}
```
## Common errors
### Authentication (401)
Missing or invalid API key.
```bash theme={null}
# Missing header → 401 {"detail": {"message": "Missing X-API-Key header", "code": "UNAUTHORIZED"}}
curl https://v2.55-tech.com/accounts
# Invalid key → 401 {"detail": {"message": "Invalid API key", "code": "UNAUTHORIZED"}}
curl -H "X-API-Key: wrong-key" https://v2.55-tech.com/accounts
```
### Forbidden (403)
Returned in two cases:
* **Inactive or expired API key** — your key exists but is disabled or past its expiry date
* **Wrong client** — attempting to access a resource that belongs to a different client
```json theme={null}
{"detail": {"message": "API key is inactive", "code": "FORBIDDEN"}}
{"detail": {"message": "API key has expired", "code": "FORBIDDEN"}}
{"detail": {"message": "Access denied to this order", "code": "FORBIDDEN"}}
```
### Duplicate request (409)
The `requestUuid` was already used within the last 5 minutes.
```json theme={null}
{"detail": "Duplicate request - requestUuid already in-flight"}
```
### Validation error (422)
Request body contains invalid data. Check the `loc` field for the problematic path:
```json theme={null}
{
"detail": [
{
"loc": ["body", "orders", 0, "fixtureId"],
"msg": "Field required",
"type": "missing"
}
]
}
```
### Rate limiting (429)
Exceeded requests per minute for your API key. Default limit: 60/min.
```json theme={null}
{"detail": "Rate limit exceeded"}
```
Implement exponential backoff: wait 1s, 2s, 4s, etc. before retrying.
## Resilience patterns
ABP implements several resilience mechanisms that may affect your integration:
### Circuit breakers
Per-bookmaker circuit breakers prevent cascading failures. If a bookmaker is experiencing issues, orders targeting that bookmaker may be declined until the circuit recovers.
Opens after consecutive failures, automatically tests recovery, and resumes normal operation once the bookmaker responds successfully.
### Emergency mode
In rare cases, the system may temporarily pause order processing during maintenance or upstream issues. The `emergency` WebSocket channel broadcasts status changes.
### Order expiry
Orders have a default `expiresAt` of 5 seconds from creation. If a bet hasn't been placed within this window, the order status changes to `EXPIRED`. Set a custom `expiresAt` for longer-lived orders.
# ABP API Overview - Automated Bet Placing
Source: https://docs.55-tech.com/abp-api/overview
ABP v2 API overview. Place bets across 30 bookmakers through a single unified API with smart routing, partial fills, real-time tracking, and automated settlement.
## What is ABP?
The **Automated Bet Placing (ABP)** API by [55 Tech](https://55-tech.com/) lets you place bets across 30 bookmakers through a single integration. Instead of building and maintaining individual bookmaker connectors, ABP handles the entire lifecycle from bet placement through settlement.
**Core capabilities:**
* **Account management** — Full CRUD for bookmaker accounts with priority-based selection, per-account stake limits, and multi-currency support
* **Betslip retrieval** — Get real-time odds and limits for any fixture/outcome across all configured bookmakers before placing
* **Futures support** — Place bets on outright/futures markets using `futureId` and `participantId` (coming soon)
* **Smart order routing** — Place single or bulk orders; ABP automatically selects the best bookmaker by odds and limits
* **Bet tracking** — Monitor every bet from placement through confirmation and settlement with full audit trail
* **Position & PnL analytics** — Aggregated views of exposure and profit/loss grouped by bookmaker, account, or user reference
## Data flow
Your app consumes the [OddsPapi v5 API](https://docs.oddspapi.io/) to discover fixtures, markets, outcomes, and real-time odds across all supported bookmakers. OddsPapi is the data layer — ABP is the execution layer built on top of it. Fixture IDs and outcome IDs are shared between both APIs.
Before placing, call `GET /betslip?fixtureId=...&outcomeId=...&playerId=...` to get aggregated odds and limits from all your configured bookmaker accounts. For futures markets, use `futureId` instead of `fixtureId` along with `participantId`. ABP resolves stake limits (account > bookmaker > odds) and returns the effective min/max per bookmaker.
Send `POST /place-orders` with one or more orders. Each order targets either a fixture (`fixtureId`) or a future (`futureId`). ABP routes each order to the best bookmaker(s) based on price, available limits, and account priority. Each bookmaker integration places the bet and reports back confirmation or decline.
Connect to `WS /ws` to receive real-time pushes for order status changes, bet confirmations, settlements, balance updates, and system events. No polling needed.
Use `GET /orders`, `GET /bets` for order/bet history with keyset pagination, and `GET /positions`, `GET /pnl` for aggregated exposure and profit/loss analytics.
## Base URL
```
https://v2.55-tech.com
```
## Key concepts
### Orders vs bets
An **order** is your instruction to place a bet. A **bet** is the actual wager placed on a bookmaker. One order can result in multiple bets when using partial fills or multi-bookmaker routing.
### Request deduplication
Each order requires a unique `requestUuid` (UUID format). ABP uses server-side deduplication (30-minute TTL) to prevent duplicate placements. Duplicate requests return `409 Conflict`.
### Order lifecycle
```
PENDING → PROCESSING → FILLED / PARTIALLY_FILLED / REJECTED / EXPIRED / CANCELLED / FAILED
```
* **PENDING** — Order received and queued
* **PROCESSING** — Routing to bookmakers
* **FILLED** — All stake placed successfully
* **PARTIALLY\_FILLED** — Some stake placed, remaining expired or no capacity
* **REJECTED** — Failed validation (bad odds, invalid fixture, etc.)
* **EXPIRED** — Order `expiresAt` time reached (default: 5 seconds, max: 24 hours)
* **CANCELLED** — Explicitly cancelled by client
* **FAILED** — Internal error during placement
### Bet lifecycle
```
PENDING → PLACED → CONFIRMED / REJECTED / CANCELLED / FAILED / VOID
```
* **PENDING** — Bet created, awaiting bookmaker response
* **PLACED** — Sent to bookmaker, awaiting confirmation
* **CONFIRMED** — Bookmaker accepted the bet
* **REJECTED** — Bookmaker rejected the bet
* **CANCELLED** — Bet cancelled before confirmation
* **FAILED** — Internal error during placement
* **VOID** — Bet voided by bookmaker
### Settlement lifecycle
```
UNSETTLED → WON / LOST / VOID / HALF_WON / HALF_LOST / PUSH / CASHOUT
```
* **UNSETTLED** — Bet is live, awaiting result
* **WON** — Full win
* **LOST** — Full loss
* **VOID** — Bet voided (stake returned)
* **HALF\_WON** — Asian handicap partial win
* **HALF\_LOST** — Asian handicap partial loss
* **PUSH** — Stake returned (tie on the line)
* **CASHOUT** — Early withdrawal at negotiated price
### Account priority
Each bookmaker account has a `priority` field (higher = preferred). When placing an order, ABP selects the highest-priority active account first for each bookmaker.
### Limit cascade
Stake limits are resolved in priority order: **account limits > bookmaker limits > odds limits**.
For example, if an account has `minStake: 10`, the bookmaker default is `minStake: 1`, and the odds entry shows `limitMin: 5`, the effective minimum is `10` (from the account override).
### Futures (coming soon)
ABP supports **futures** (outright) markets alongside standard fixture-based markets. Instead of a `fixtureId`, futures use a `futureId` to identify the outright market and a `participantId` to specify the selection (e.g., a team or player to win a league/tournament).
**Key differences from fixture orders:**
| | Fixture orders | Futures orders |
| ----------------- | ---------------------------------------- | ----------------------------------------------------- |
| Identifier | `fixtureId` | `futureId` |
| Selection | `outcomeId` + `playerId` | `outcomeId` + `playerId` + `participantId` |
| Betslip | `GET /betslip?fixtureId=...` | `GET /betslip?futureId=...` |
| Market key format | `fixtureId:bookmaker:outcomeId:playerId` | `futureId:bookmaker:outcomeId:playerId:participantId` |
**Current status:** The data model and betslip infrastructure are in place. Order placement and betslip retrieval for futures currently return `501 Not Implemented`. This will be enabled in an upcoming release.
### Bookmaker slugs
Bookmakers are identified by slug strings (e.g., `pinnacle`, `betfair-ex`, `polymarket`). Use `GET /bookmakers` to list all 30 supported bookmakers with their default stake limits.
## Endpoints at a glance
| Category | Endpoints | Description |
| -------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| **Accounts** | `GET/POST/PATCH/DELETE /accounts` | Manage bookmaker accounts (credentials, balances, priority, limits) |
| **Betslip** | `GET /betslip` | Get live odds & limits before placing (fixtures and futures) |
| **Orders** | `POST /place-orders`, `POST /cancel-orders`, `POST /cancel-all-orders`, `GET /orders` | Place, cancel, and track orders |
| **Bets** | `GET /bets`, `GET /bets/{bet_id}` | View individual bet results |
| **Analytics** | `GET /positions`, `GET /pnl` | Aggregated exposure and P\&L |
| **Bookmakers** | `GET /bookmakers` | List all supported bookmakers |
| **Markets** | `GET /markets` | Available markets and odds types |
| **WebSocket** | `WS /ws` | Real-time updates |
## Supported bookmakers
**Traditional sportsbooks:** pinnacle, pinnacleb2b, betamapola, betcris, bookmaker.eu, cloudbet, cloudbetb2b, justbet, kaiyun, monkeyline.vip, novig.us, 198bet, paradisewager, sharpbet, singbet, sports411.ag, 3et, vertex
**Betting exchanges:** betfair-ex, limitless-ex, matchbook, smarkets
**Prediction markets:** 4casters, kalshi, polymarket, polymarket.us, predict.fun, prophetx, sx.bet
**Punter platforms:** punter.io
## Resilience
ABP includes production-grade reliability features:
* **Circuit breakers** — Per-bookmaker circuit breakers prevent cascading failures and auto-recover
* **Retry with exponential backoff** — for transient failures
* **Emergency controls** — Orders may be temporarily paused during system maintenance
* **Rate limiting** — Per-API-key rate limits (configurable per client)
## Data source
ABP consumes real-time odds data from [OddsPapi v5](https://docs.oddspapi.io/). Fixture IDs and outcome IDs in ABP correspond directly to OddsPapi identifiers. Your app should use OddsPapi to discover fixtures and markets, then use ABP to execute bets.
## Next steps
Set up your API key.
Place your first bet in 5 steps.
# ABP Quickstart - Place Your First Bet
Source: https://docs.55-tech.com/abp-api/quickstart
Step-by-step guide to placing your first bet through the ABP API. From account setup to order placement, tracking, and settlement.
## Step 1: List your accounts
Check which bookmaker accounts are configured for your API key:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://v2.55-tech.com/accounts
```
Response includes each account's bookmaker, balance, priority, stake limits, and currency:
```json theme={null}
[
{
"bookmaker": "pinnacle",
"username": "pinnacle_main",
"client": "your-client",
"password": "***",
"balance": 5000.0,
"active": true,
"priority": 10,
"maxStake": 1000.0,
"minStake": 5.0,
"currencyId": "USD",
"verifyBetslip": false,
"meta": {},
"createdAt": "2026-01-01T00:00:00Z",
"updatedAt": "2026-02-01T12:00:00Z"
}
]
```
## Step 2: Get a betslip
Before placing a bet, retrieve current odds and limits. You need a `fixtureId`, `outcomeId`, and `playerId`:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
"https://v2.55-tech.com/betslip?fixtureId=id1000002361061419&outcomeId=101&playerId=0"
```
The betslip returns live odds from every bookmaker that has this market, with fixture and outcome metadata:
```json theme={null}
{
"fixtureId": "id1000002361061419",
"outcomeId": 101,
"playerId": 0,
"client": "your-client",
"fixtureInfo": {
"sport": { "sportId": 10, "sportName": "Soccer" },
"tournament": { "tournamentName": "Serie A", "categoryName": "Italy" },
"participants": {
"participant1Name": "AS Roma",
"participant2Name": "Cagliari Calcio"
}
},
"outcomeInfo": {
"marketName": "Full Time Result",
"outcomeName": "1"
},
"odds": {
"pinnacle": {
"id1000002361061419:pinnacle:101:0": {
"price": 1.5,
"limit": 15000,
"limitMin": 5,
"limitCurrency": "USD",
"active": true,
"account": "pinnacle_main"
}
},
"sharpbet": {
"id1000002361061419:sharpbet:101:0": {
"price": 1.52,
"limit": 751.34,
"limitMin": 1,
"limitCurrency": "EUR",
"limitUsd": 886.13,
"active": true,
"account": "sharpbet_user"
}
}
}
}
```
Use the [OddsPapi API](https://docs.oddspapi.io/) to discover fixture IDs and outcome IDs. ABP uses OddsPapi identifiers directly.
## Step 3: Place an order
Place a bet order with minimum price protection. Each order needs a unique `requestUuid` for idempotency:
```bash theme={null}
curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"orders": [
{
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"playerId": 0,
"orderStake": 10.0,
"orderPrice": 1.95,
"userRef": "my-strategy-1",
"testOrder": false
}
]
}' \
https://v2.55-tech.com/place-orders
```
**Key fields:**
| Field | Required | Description |
| -------------------- | -------- | ---------------------------------------------------------------- |
| `requestUuid` | Yes | Unique UUID for idempotency (duplicates within 5 min return 409) |
| `fixtureId` | Yes | OddsPapi fixture ID |
| `outcomeId` | Yes | Market outcome (e.g., 103 = away win) |
| `playerId` | Yes | Set to `0` for non-player-prop markets |
| `orderStake` | Yes | Total amount to wager |
| `orderPrice` | Yes | Minimum acceptable decimal odds |
| `userRef` | Yes | Your reference for grouping related orders |
| `testOrder` | Yes | Validate only, don't actually place (`false` for real bets) |
| `bookmakers` | No | Comma-separated slugs to target (omit for automatic selection) |
| `orderCurrency` | No | Currency code (default: `USD`) |
| `acceptBetterOdds` | No | Accept odds better than `orderPrice` (default: `true`) |
| `acceptPartialStake` | No | Allow partial fills (default: `true`) |
| `back` | No | Back bet (`true`) or lay bet (`false`) (default: `true`) |
| `expiresAt` | No | ISO 8601 expiry time (default: 5 seconds from now) |
| `meta` | No | Custom metadata object (stored but not sent to bookmaker) |
**Response:**
```json theme={null}
{
"status": "accepted",
"acceptedOrders": [
{
"orderId": 327,
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"orderStatus": "FILLED",
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"orderStake": 10.0,
"orderPrice": 1.95,
"filledStake": 10.0,
"bets": [
{
"betId": 73,
"bookmaker": "pinnacle",
"placedPrice": 1.98,
"placedStake": 10.0,
"betStatus": "CONFIRMED"
}
]
}
],
"declinedOrders": []
}
```
Orders that fail validation appear in `declinedOrders` with a `declineReason`:
```json theme={null}
{
"status": "declined",
"acceptedOrders": [],
"declinedOrders": [
{
"requestUuid": "fb5f2dd9-c855-4ba9-8ef9-4c2278ca2f1d",
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"declineReason": "Order stake 100.00 USD exceeds available limit 50.00 USD"
}
]
}
```
## Step 4: Track your order
Query orders by `userRef`, `orderIds`, or `requestUuids` (at least one filter required):
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
"https://v2.55-tech.com/orders?userRef=my-strategy-1"
```
View individual bets for specific orders:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
"https://v2.55-tech.com/bets?orderIds=327"
```
Each bet includes the bookmaker, placed price, placed stake, bet status, and settlement status:
```json theme={null}
{
"status": "success",
"bets": [
{
"betId": 73,
"orderId": 327,
"bookmaker": "pinnacle",
"bookmakerBetId": "3332684214",
"placedStake": 10.0,
"placedPrice": 1.98,
"placedCurrency": "USD",
"betStatus": "CONFIRMED",
"settlementStatus": "UNSETTLED",
"account": "pinnacle_main",
"userRef": "my-strategy-1"
}
],
"count": 1,
"hasMore": false,
"nextCursor": null
}
```
## Step 5: Check positions & PnL
View your aggregated open positions (grouped by bookmaker by default):
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://v2.55-tech.com/positions
```
```json theme={null}
{
"status": "success",
"groupBy": "bookmaker",
"positions": [
{ "bookmaker": "pinnacle", "openBets": 5, "totalStake": 250.0, "avgPrice": 1.92 }
],
"count": 1,
"totalStake": 250.0,
"totalOpenBets": 5
}
```
View profit and loss:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://v2.55-tech.com/pnl
```
```json theme={null}
{
"status": "success",
"groupBy": "bookmaker",
"pnl": [
{ "bookmaker": "pinnacle", "settledBets": 42, "wins": 23, "losses": 17, "netPnl": 250.0, "winRate": 54.8 }
],
"count": 1,
"totalNetPnl": 250.0,
"totalStaked": 2100.0,
"totalSettledBets": 42
}
```
## Next steps
Get real-time order, bet, and settlement updates.
Explore all available endpoints.
# ABP WebSocket - Real-Time Updates
Source: https://docs.55-tech.com/abp-api/websocket
Connect to the ABP WebSocket for real-time order, bet, settlement, account, and system updates. Server-backed with message ordering and reliable delivery.
## Endpoint
```
wss://v2.55-tech.com/ws
```
## Connection flow
### 1. Connect
Open a WebSocket connection. No authentication is needed at connection time.
### 2. Login
Send a login message within **30 seconds** of connecting:
```json theme={null}
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": []
}
```
An empty `channels` array subscribes to all available channels. To subscribe to specific channels:
```json theme={null}
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets", "settlements"]
}
```
You can optionally enable reliable delivery with message acknowledgments:
```json theme={null}
{
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets"],
"reliableDelivery": true
}
```
### 3. Login confirmation
On success, the server responds with:
```json theme={null}
{
"type": "login_ok",
"clientName": "your-client",
"channels": ["orders", "bets", "settlements"],
"subscriptionId": 1,
"access": {
"clientFiltered": ["orders", "bets", "settlements", "accounts", "balance", "betslip"],
"global": ["fixtures", "currencies", "status", "emergency"]
},
"reliableDelivery": false,
"features": {
"messageOrdering": true,
"acknowledgments": false,
"batchAck": false
}
}
```
### 4. Receive updates
Data messages follow this format:
```json theme={null}
{
"type": "data",
"channel": "orders",
"event": "INSERT",
"payload": { ... },
"ts": 1771183909789,
"seq": 1
}
```
| Field | Description |
| ------------ | --------------------------------------------------------------- |
| `type` | Always `"data"` for data messages |
| `channel` | Which channel this message belongs to |
| `event` | Event type: `INSERT`, `UPDATE`, `DELETE`, `SETTLED`, `STATUS` |
| `payload` | The full updated object (see per-channel payload schemas below) |
| `ts` | Server timestamp (milliseconds since epoch) |
| `seq` | Per-subscription monotonically increasing sequence number |
| `requireAck` | Present and `true` only when reliable delivery is enabled |
### 5. Keep alive
The server sends a ping every **30 seconds**:
```json theme={null}
{"type": "ping"}
```
Respond with pong within **120 seconds** or the connection is closed:
```json theme={null}
{"type": "pong"}
```
You can also send pings from the client — the server responds with pong.
## Channels
### Client-filtered channels
These channels only deliver data belonging to your `clientName`:
| Channel | Events | Description |
| ------------- | ---------------------- | --------------------------------------------------------------------------------------------------- |
| `orders` | INSERT, UPDATE, DELETE | Order placement, fills, status changes |
| `bets` | INSERT, UPDATE, DELETE | Bet placement, confirmation, and removal |
| `settlements` | SETTLED | Settlement status updates (same payload as bets) |
| `accounts` | INSERT, UPDATE, DELETE | Account creation, updates, and deletion |
| `balance` | UPDATE | Balance change notifications |
| `betslip` | UPDATE | Real-time odds updates for active betslip subscriptions (60s window after each `GET /betslip` call) |
### Global channels
All subscribers receive these:
| Channel | Events | Description |
| ------------ | ------ | -------------------------------------- |
| `fixtures` | UPDATE | Fixture metadata and score changes |
| `currencies` | UPDATE | Currency exchange rate updates |
| `status` | STATUS | System status changes |
| `emergency` | STATUS | Emergency mode activation/deactivation |
## Payload schemas
### Orders payload
Full database row from the `orders` table:
```json theme={null}
{
"orderId": 327,
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"client": "demo",
"clientName": "demo",
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"playerId": 0,
"orderPrice": 1.95,
"orderStake": 10.0,
"filledStake": 10.0,
"remainingStake": 0.0,
"orderStatus": "FILLED",
"statusReason": null,
"userRef": "bettor1234",
"testOrder": false,
"acceptBetterOdds": true,
"acceptPartialStake": true,
"orderCurrency": "USD",
"back": true,
"allowedBookmakers": "*",
"oddsInfo": null,
"meta": {},
"expiresAt": "2026-02-07T17:29:37+00:00",
"filledAt": "2026-02-07T17:29:32+00:00",
"createdAt": "2026-02-07T17:29:32+00:00",
"updatedAt": "2026-02-07T17:29:32+00:00"
}
```
### Bets / settlements payload
Full database row from the `bets` table:
```json theme={null}
{
"betId": 73,
"orderId": 327,
"bookmaker": "pinnacle",
"bookmakerBetId": "3332684214",
"betStatus": "CONFIRMED",
"settlementStatus": "UNSETTLED",
"placedStake": 10.0,
"placedPrice": 1.98,
"placedCurrency": "USD",
"account": "pinnacle_main",
"requestUuid": "eb45b192-317b-42d5-9f65-af497b9fa8c1",
"userRef": "bettor1234",
"testBet": false,
"client": "demo",
"clientName": "demo",
"sentData": { "stake": 10.0, "price": 1.95 },
"receivedData": { "betId": "3332684214", "status": "accepted", "price": 1.98 },
"settlementAmount": null,
"settlementReason": null,
"settledAt": null,
"declineReason": null,
"betRequestId": null,
"oddsInfo": null,
"meta": {},
"placedAt": "2026-02-07T17:29:32+00:00",
"createdAt": "2026-02-07T17:29:32+00:00",
"updatedAt": "2026-02-07T17:29:32+00:00"
}
```
### Balance payload
```json theme={null}
{
"clientName": "demo",
"bookmaker": "pinnacle",
"username": "pinnacle_main",
"balance": 4990.0,
"currency": "USD",
"ts": 1771183910123
}
```
### Emergency payload
```json theme={null}
{
"active": true,
"reason": "Upstream provider maintenance",
"ts": 1771183910123
}
```
### Betslip payload
Real-time odds updates pushed while a betslip subscription is active. Calling `GET /betslip` registers a **60-second sliding window** — during this window, price changes are broadcast in the same shape as the betslip REST response. Each poll resets the timer.
This applies to both fixture-based and futures-based betslip subscriptions. For futures, the payload includes `futureId` and `participantId` instead of (or in addition to) `fixtureId`.
**Fixture betslip example:**
```json theme={null}
{
"fixtureId": "id1000004461512432",
"outcomeId": 103,
"playerId": 0,
"client": "demo",
"userRef": null,
"odds": {
"pinnacle": {
"odds_12345": {
"price": 1.98,
"limit": 500.0,
"limitMin": 1.0,
"limitCurrency": "USD",
"limitUsd": 500.0,
"limitMinUsd": 1.0,
"active": true,
"account": "pinnacle_main"
}
}
}
}
```
**Futures betslip example (coming soon):**
```json theme={null}
{
"futureId": "fut_123456",
"outcomeId": 0,
"playerId": 0,
"participantId": 5,
"client": "demo",
"userRef": null,
"odds": {
"pinnacle": {
"fut_123456:pinnacle:0:0:5": {
"price": 3.50,
"limit": 1000.0,
"limitMin": 5.0,
"limitCurrency": "USD",
"limitUsd": 1000.0,
"limitMinUsd": 5.0,
"active": true,
"account": "pinnacle_main"
}
}
}
}
```
Futures betslip WebSocket broadcasting is not yet enabled. The subscription infrastructure is in place and will be activated in an upcoming release.
## Connection limits
| Setting | Value |
| ---------------------------------- | ------------- |
| Max connections per API key | 5 |
| Auth timeout | 30 seconds |
| Server ping interval | 30 seconds |
| Pong timeout (disconnect) | 120 seconds |
| Message buffer (reliable delivery) | 100 messages |
| Output queue per client | 2000 messages |
## Example: Python client
```python theme={null}
import asyncio
import json
import websockets
async def connect():
uri = "wss://v2.55-tech.com/ws"
async with websockets.connect(uri) as ws:
# Login
await ws.send(json.dumps({
"type": "login",
"apiKey": "YOUR_API_KEY",
"channels": ["orders", "bets", "settlements"]
}))
# Wait for login confirmation
login_resp = json.loads(await ws.recv())
print(f"Logged in as {login_resp.get('clientName')}")
# Listen for updates (respond to pings automatically)
async for message in ws:
data = json.loads(message)
if data["type"] == "ping":
await ws.send(json.dumps({"type": "pong"}))
elif data["type"] == "data":
print(f"[{data['channel']}:{data['event']}] {data['payload']}")
asyncio.run(connect())
```
## Example: JavaScript client
```javascript theme={null}
const WebSocket = require('ws');
const ws = new WebSocket('wss://v2.55-tech.com/ws');
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'login',
apiKey: 'YOUR_API_KEY',
channels: ['orders', 'bets', 'settlements']
}));
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.type === 'login_ok') {
console.log(`Logged in as ${msg.clientName}`);
}
if (msg.type === 'ping') {
ws.send(JSON.stringify({ type: 'pong' }));
}
if (msg.type === 'data') {
console.log(`[${msg.channel}:${msg.event}]`, msg.payload);
}
});
```
# AI & LLM Integration - Documentation Export
Source: https://docs.55-tech.com/ai
Download 55-Tech API documentation for AI tools, LLMs, and offline use. Machine-readable exports in TXT and OpenAPI JSON formats for ChatGPT, Claude, and other AI assistants.
## Download
| Resource | Format | Description |
| ---------------------------------------------------------- | ------ | --------------------------------------------------------------------- |
| [`/llms.txt`](/llms.txt) | TXT | Docs index — page titles and paths |
| [`/llms-full.txt`](/llms-full.txt) | TXT | Full docs bundle — all guide pages concatenated |
| [`/abp-api/openapi.json`](/abp-api/openapi.json) | JSON | ABP OpenAPI 3.1 spec — 13 endpoints, full schemas |
| [`/mm-api/openapi.json`](/mm-api/openapi.json) | JSON | MM OpenAPI 3.1 spec — REST reference |
| [`/scraping-api/openapi.json`](/scraping-api/openapi.json) | JSON | Scraping OpenAPI 3.1 spec — fetch, WebSocket, AMQP, network endpoints |
## What's included
The **ABP API** documentation covers:
* **Account management** — Full CRUD for bookmaker accounts across 30 integrations
* **Betslip** — Real-time odds and limits from OddsPapi v5 with multi-bookmaker aggregation
* **Order placement** — Smart routing with idempotency (`requestUuid`), partial fills, and configurable expiry
* **Bet tracking** — Keyset-paginated queries with full lifecycle from PENDING through CONFIRMED/SETTLED
* **Analytics** — Positions and PnL grouped by bookmaker, account, or user reference
* **WebSocket** — Real-time push for orders, bets, settlements, balance, fixtures, currencies, status, emergency
* **30 bookmaker integrations** — pinnacle, betfair-ex, polymarket, kalshi, cloudbet, smarkets, and more
The **MM API** documentation covers:
* Market-making engine configuration and real-time spread management
* WebSocket-driven pricing updates and order book interaction
The **Scraping API** documentation covers:
* **HTTP fetch** — GET/POST/PUT/PATCH/DELETE through 80+ geo-distributed agents
* **WebSocket relay** — Bidirectional frame relay for Socket.IO, SignalR, Centrifugo, GraphQL-WS, raw WS
* **AMQP consumer** — Stream RabbitMQ messages via SSE
* **Network intelligence** — Agent listing, geo distribution, per-domain health checks
* **Usage & rate limits** — Per-key metrics, request analytics, top domains
## Recommended usage
**For AI assistants that fetch URLs:**
* `/llms-full.txt` — best for "read everything once" (all guide pages + examples)
* `/abp-api/openapi.json` — ABP endpoint + schema accuracy (typed, with examples)
* `/mm-api/openapi.json` — MM endpoint + schema reference
* `/scraping-api/openapi.json` — Scraping endpoint + schema reference
**For copy/paste workflows:**
Open `/llms-full.txt` in the browser and paste the contents into your AI tool.
**For code generation:**
Feed the OpenAPI specs to tools like `openapi-generator`, `oapi-codegen`, or Cursor/Copilot for typed client SDK generation.
## Notes
* `llms-full.txt` follows the same ordering as the site navigation
* WebSocket protocols are documented in the guide pages; REST endpoints are in the OpenAPI specs
* All three APIs (ABP, MM, Scraping) are included in a single bundle
* All APIs authenticate via the `X-API-Key` header
***
## Ask an AI Assistant
Want to explore or ask questions about this API using your favorite AI?
Click one of the links below — each one opens the full docs bundle in the selected tool with a pre-filled prompt:
* [Ask ChatGPT](https://chatgpt.com/?prompt=Read+from+https%3A%2F%2Fdocs.55-tech.com%2Fllms-full.txt+and+help+me+with+this+API.)
* [Ask Claude](https://claude.ai/?prompt=Please+read+https%3A%2F%2Fdocs.55-tech.com%2Fllms-full.txt+and+help+me+use+this+API.)
* [Ask Perplexity](https://www.perplexity.ai/search?q=Read+from+https%3A%2F%2Fdocs.55-tech.com%2Fllms-full.txt)
* [Ask Gemini](https://gemini.google.com/app?query=Read+from+https%3A%2F%2Fdocs.55-tech.com%2Fllms-full.txt+and+help+me+use+this+API.)
# Create account
Source: https://docs.55-tech.com/api-reference/accounts/create-account
/zh/abp-api/openapi.json post /accounts
Create a new bookmaker account for the authenticated client. Each client can have multiple accounts per bookmaker. Use `priority` to control which account is selected first.
# Delete account
Source: https://docs.55-tech.com/api-reference/accounts/delete-account
/zh/abp-api/openapi.json delete /accounts/{bookmaker}/{username}
Delete a bookmaker account. Only the account owner (client) can delete their accounts.
# Get account by key
Source: https://docs.55-tech.com/api-reference/accounts/get-account-by-key
/zh/abp-api/openapi.json get /accounts/{bookmaker}/{username}
Get a specific account by its primary key (bookmaker, username). Only returns the account if it belongs to the authenticated client.
# List accounts for client
Source: https://docs.55-tech.com/api-reference/accounts/list-accounts-for-client
/zh/abp-api/openapi.json get /accounts
Get all bookmaker accounts for the authenticated client. Optionally filter by bookmaker. Accounts are ordered by priority (highest first). Passwords are always masked.
# Update account
Source: https://docs.55-tech.com/api-reference/accounts/update-account
/zh/abp-api/openapi.json patch /accounts/{bookmaker}/{username}
Update an existing bookmaker account. Only provided fields are updated. Only the account owner (client) can update their accounts.
# AMQP consumer relay (SSE)
Source: https://docs.55-tech.com/api-reference/amqp/amqp-consumer-relay-sse
/zh/scraping-api/openapi.json post /amqp
Connect to an AMQP/RabbitMQ broker through an agent and stream messages as Server-Sent Events. The agent creates a temporary auto-delete queue, binds it to the specified exchange, and streams messages back as SSE events (connected, message, error).
# Get open positions
Source: https://docs.55-tech.com/api-reference/analytics/get-open-positions
/zh/abp-api/openapi.json get /positions
Retrieve aggregated open (unsettled) positions grouped by bookmaker, account, or userRef.
Returns the number of open bets, total stake, and average price for each group.
Only includes bets with `betStatus` in (PLACED, CONFIRMED) and `settlementStatus` = UNSETTLED.
**Group by options:**
- `bookmaker` (default) — Group by bookmaker
- `account` — Group by bookmaker account
- `userRef` — Group by user reference
**Filters:**
- `bookmaker` — Filter to a specific bookmaker
- `userRef` — Filter to a specific user reference
- `account` — Filter to a specific account
# Get profit and loss
Source: https://docs.55-tech.com/api-reference/analytics/get-profit-and-loss
/zh/abp-api/openapi.json get /pnl
Retrieve aggregated PnL (profit and loss) grouped by bookmaker, account, or userRef.
Only includes settled bets (`settlementStatus` NOT in UNSETTLED).
**Group by options:**
- `bookmaker` (default) — Group by bookmaker
- `account` — Group by bookmaker account
- `userRef` — Group by user reference
**Filters:**
- `bookmaker` — Filter to a specific bookmaker
- `userRef` — Filter to a specific user reference
- `account` — Filter to a specific account
# Get a single bet
Source: https://docs.55-tech.com/api-reference/bets/get-a-single-bet
/zh/abp-api/openapi.json get /bets/{bet_id}
Retrieve a single bet by its ID. The bet must belong to the authenticated client.
# Get bets
Source: https://docs.55-tech.com/api-reference/bets/get-bets
/zh/abp-api/openapi.json get /bets
Retrieve bets by various filters with keyset pagination. At least one filter must be provided.
**Filters (OR logic):**
- `betIds` — Comma-separated list of bet IDs
- `orderIds` — Comma-separated list of order IDs
- `userRef` — User reference string
**Pagination:**
- Results are ordered by betId descending (newest first)
- Use `afterBetId` from `nextCursor` in the response to fetch the next page
- `hasMore` indicates if more results are available
**Bet Status Values:** PENDING, PLACED, CONFIRMED, REJECTED, CANCELLED, FAILED, VOID
**Settlement Status Values:** UNSETTLED, WON, LOST, VOID, HALF_WON, HALF_LOST, PUSH, CASHOUT
# Get live odds and betslip metadata
Source: https://docs.55-tech.com/api-reference/betslip/get-live-odds-and-betslip-metadata
/zh/abp-api/openapi.json get /betslip
Get current odds and metadata for a specific fixture/outcome/player combination, or a futures/outright market.
**Fixture mode (required parameters):** `fixtureId`, `outcomeId`, `playerId`
**Futures mode (coming soon):** `futureId`, `outcomeId`, `playerId`, `participantId` — currently returns `501 Not Implemented`
**Response includes:**
- `fixtureInfo` — Fixture details (teams, sport, tournament, start time, scores)
- `outcomeInfo` — Market and outcome details (market name, type, handicap)
- `playerInfo` — Player details (for player prop markets, null otherwise)
- `odds` — Live odds keyed by bookmaker, each containing:
- `price` — Current decimal odds
- `limit` / `limitMin` — Max/min stake in account currency
- `limitUsd` / `limitMinUsd` — Limits converted to USD
- `limitCurrency` — Account currency for limits
- `active` — Whether odds are currently available
- `account` — Account username for this bookmaker
- `currencyInfo` — Currency exchange rate details
- `bookmakerFixtureId/MarketId/OutcomeId` — Bookmaker's internal IDs
- `meta` — Exchange orderbook data (for Betfair, Polymarket, etc.)
**Limit cascade:** account.minStake/maxStake > bookmaker.minStake/maxStake > odds.limitMin/limit
# List all bookmakers
Source: https://docs.55-tech.com/api-reference/bookmakers/list-all-bookmakers
/zh/abp-api/openapi.json get /bookmakers
Returns all supported bookmakers with their stake limits. Results are cached for 60 seconds.
# Render a page with a real browser
Source: https://docs.55-tech.com/api-reference/browser/render-a-page-with-a-real-browser
/zh/scraping-api/openapi.json post /browser
Render a page using a real Chromium browser on the proxy node. Executes JavaScript, waits for dynamic content, captures all cookies (including httpOnly), and optionally takes a screenshot or evaluates custom JavaScript.
Use this instead of /fetch when the target page requires JavaScript to render content (SPAs, React, Next.js), or when you need to capture browser-set cookies.
The response format matches /fetch (meta + raw/raw_json), with additional cookies, screenshot, and js_result fields.
If expectSelector or expectContains is set and the rendered page doesn't match, the API automatically retries on a different node before returning an error.
# Cancel a specific order
Source: https://docs.55-tech.com/api-reference/client-api/cancel-a-specific-order
/zh/mm-api/openapi.json post /api/v1/orders/{order_id}/cancel
Cancel a specific order by its ID.
# Cancel all open orders
Source: https://docs.55-tech.com/api-reference/client-api/cancel-all-open-orders
/zh/mm-api/openapi.json post /api/v1/orders/cancel-all
Cancel ALL open orders for your client account across all exchanges.
**Use with caution** - this will cancel every open order you have.
Optionally filter by:
- **exchange**: Only cancel orders on a specific exchange
- **fixture_id**: Only cancel orders for a specific fixture
# Get bet details
Source: https://docs.55-tech.com/api-reference/client-api/get-bet-details
/zh/mm-api/openapi.json get /api/v1/bets/{bet_id}
Retrieve detailed information about a specific bet by its ID.
# Get bets summary
Source: https://docs.55-tech.com/api-reference/client-api/get-bets-summary
/zh/mm-api/openapi.json get /api/v1/bets/summary
Get aggregated statistics about all bets for your client account.
Optionally filter by date range to see statistics for a specific time period.
# Get current client info
Source: https://docs.55-tech.com/api-reference/client-api/get-current-client-info
/zh/mm-api/openapi.json get /api/v1/me
Returns the authenticated client's information based on their API key.
# Get order details
Source: https://docs.55-tech.com/api-reference/client-api/get-order-details
/zh/mm-api/openapi.json get /api/v1/orders/{order_id}
Retrieve detailed information about a specific order by its ID.
# Get orders summary
Source: https://docs.55-tech.com/api-reference/client-api/get-orders-summary
/zh/mm-api/openapi.json get /api/v1/orders/summary
Get aggregated statistics about all orders for your client account.
Optionally filter by date range to see statistics for a specific time period.
# Get positions summary
Source: https://docs.55-tech.com/api-reference/client-api/get-positions-summary
/zh/mm-api/openapi.json get /api/v1/positions/summary
Get aggregated statistics about all positions for your client account.
Optionally filter by date range to see statistics for a specific time period.
# Get profit and loss
Source: https://docs.55-tech.com/api-reference/client-api/get-profit-and-loss
/zh/mm-api/openapi.json get /api/v1/pnl
Get profit and loss (PnL) statistics for your client account.
**Realized PnL**: Profit/loss from settled orders
- WON orders: profit = matchedStake * (1/probability - 1)
- LOST orders: loss = -matchedStake
- HALF_WON/HALF_LOST: 50% of the above
**Unrealized PnL**: Potential profit from open positions (if they win)
- Calculated as: matchedStake * (1/probability - 1) for all unsettled matched orders
Optionally filter by date range to see PnL for a specific time period.
# List accounts
Source: https://docs.55-tech.com/api-reference/client-api/list-accounts
/zh/mm-api/openapi.json get /api/v1/accounts
List exchange accounts configured for your client.
Returns account metadata without sensitive credentials.
Shows which exchanges you have accounts on and their configuration.
# List bets
Source: https://docs.55-tech.com/api-reference/client-api/list-bets
/zh/mm-api/openapi.json get /api/v1/bets
Retrieve a paginated list of hedge bets placed on your behalf.
Bets are created when orders are matched on exchanges. They represent
the hedging activity on the bookmaker side.
Filter by:
- **order_id**: Get bets for a specific order
- **bookmaker**: Filter by bookmaker (e.g., vertex, pinnacle)
- **status**: Filter by bet status - supports comma-separated values (e.g., `placed,pending`)
- `placed`: Successfully placed bets
- `pending`: Bets awaiting confirmation
- `declined`: Rejected bets
# List fixtures
Source: https://docs.55-tech.com/api-reference/client-api/list-fixtures
/zh/mm-api/openapi.json get /api/v1/fixtures
Retrieve a paginated list of fixtures (events/matches).
Fixtures contain event metadata like participants, start time, and scores.
Filter by:
- **fixtureIds**: Filter by specific fixture IDs (repeated query param)
- **tournament_id**: Filter by tournament (e.g., 234 for NHL)
- **sport_id**: Filter by sport (e.g., 1 for Soccer)
- **live**: Filter by live status (true/false)
- **all**: Set to true to see all available fixtures (not just ones with your orders)
# List markets for sport
Source: https://docs.55-tech.com/api-reference/client-api/list-markets-for-sport
/zh/mm-api/openapi.json get /api/v1/markets
Get available markets for a sport from cached odds data.
**Note:** Markets are global reference data - they describe what market types
exist (1x2, Over/Under, etc.) and are not specific to any client. All clients
see the same market definitions.
Markets are aggregated from all active fixtures for the specified sport.
**Required:**
- **sportId**: Get markets for a sport (e.g., 1=Soccer, 14=Ice Hockey)
**Response fields:**
- `marketId`: Market identifier
- `outcomeIds`: List of outcome IDs available for this market
- `playerIds`: List of player IDs (for player prop markets)
- `bookmakers`: Bookmakers offering this market
# List open orders
Source: https://docs.55-tech.com/api-reference/client-api/list-open-orders
/zh/mm-api/openapi.json get /api/v1/orders/open
Retrieve all currently open (unfilled) orders for your client account.
Open orders have status PLACED or PENDING_PLACEMENT and are not yet fully matched.
This endpoint does not paginate - it returns all open orders at once.
# List orders
Source: https://docs.55-tech.com/api-reference/client-api/list-orders
/zh/mm-api/openapi.json get /api/v1/orders
Retrieve a paginated list of orders for your client account.
Orders are sorted by creation date (newest first) and can be filtered by:
- **fixture_id**: Filter by specific fixture/event
- **exchange**: Filter by exchange (polymarket, kalshi, etc.)
- **status**: Filter by order status (PLACED, FILLED, CANCELLED, etc.)
Use pagination parameters to navigate through large result sets.
# List players for sport
Source: https://docs.55-tech.com/api-reference/client-api/list-players-for-sport
/zh/mm-api/openapi.json get /api/v1/players
Get players for a sport from cached odds data.
**Note:** Players are global reference data - they describe what players
have prop markets available. All clients see the same player data.
Players are aggregated from all active fixtures for the specified sport
where playerId > 0 (player prop markets).
**Required:**
- **sportId**: Get players for a sport (e.g., 1=Soccer, 14=Ice Hockey)
**Response fields:**
- `playerId`: Player identifier
- `marketIds`: Markets this player has props in
- `outcomeIds`: Outcome IDs for this player
- `bookmakers`: Bookmakers offering props for this player
# List positions
Source: https://docs.55-tech.com/api-reference/client-api/list-positions
/zh/mm-api/openapi.json get /api/v1/positions
Get aggregated positions for your client account.
A position aggregates all orders for a specific fixture/outcome/exchange combination,
showing total stake, matched stake, and open stake.
This gives you a consolidated view of your exposure on each market.
**Status filters** (supports comma-separated values, e.g., `open,matched`):
- `open`: Only positions with open orders (still on exchange, not fully matched)
- `matched`: Only positions with matched stake > 0 (excludes cancelled)
- `filled`: Only fully matched orders
- `cancelled`: Only cancelled orders
- `active`: Exclude cancelled orders (default)
Examples:
- `/positions?status=open` - Only open positions
- `/positions?status=open,matched` - Open or matched positions
- `/positions?status=filled` - Only filled positions
Optionally filter by date range to see positions from a specific time period.
# Debug agent selection
Source: https://docs.55-tech.com/api-reference/debug/debug-agent-selection
/zh/scraping-api/openapi.json get /debug/pick
Preview which agent would be selected for a given URL without making the actual request.
# Fetch a URL (DELETE)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-delete
/zh/scraping-api/openapi.json delete /fetch
Send a DELETE request to the target URL through the agent network.
# Fetch a URL (GET)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-get
/zh/scraping-api/openapi.json get /fetch
Send a GET request to the target URL through the agent network. Pass the target URL via the X-Target-URL header.
# Fetch a URL (PATCH)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-patch
/zh/scraping-api/openapi.json patch /fetch
Send a PATCH request to the target URL through the agent network.
# Fetch a URL (POST)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-post
/zh/scraping-api/openapi.json post /fetch
Send a request to the target URL through the agent network. Pass the target URL via the X-Target-URL header. Optionally include a JSON body with method, headers, body, timeout, and allow_redirects fields.
# Fetch a URL (PUT)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-put
/zh/scraping-api/openapi.json put /fetch
Send a PUT request to the target URL through the agent network.
# Get all markets with odds types
Source: https://docs.55-tech.com/api-reference/markets/get-all-markets-with-odds-types
/zh/abp-api/openapi.json get /markets
Returns all available markets and their outcome types. Optionally filter by sportId. Markets are sorted by sportId then marketId.
# Agents by country
Source: https://docs.55-tech.com/api-reference/network/agents-by-country
/zh/scraping-api/openapi.json get /network/geo
Agents grouped by country with slugs listed for each.
# Domain health
Source: https://docs.55-tech.com/api-reference/network/domain-health
/zh/scraping-api/openapi.json get /network/health/{domain}
Per-agent health state for a specific domain. Shows which agents are healthy, soft-blocked, or hard-blocked.
# List all agents
Source: https://docs.55-tech.com/api-reference/network/list-all-agents
/zh/scraping-api/openapi.json get /network/agents
Returns the full agent registry with slug, name, and country for each agent.
# Network status
Source: https://docs.55-tech.com/api-reference/network/network-status
/zh/scraping-api/openapi.json get /network/status
Summary of total nodes and nodes per country.
# Cancel all pending orders
Source: https://docs.55-tech.com/api-reference/orders/cancel-all-pending-orders
/zh/abp-api/openapi.json post /cancel-all-orders
Cancel ALL pending and partially-placed orders for the authenticated client.
**Behavior:**
- Cancels orders in PENDING or PARTIALLY_FILLED status
- Only cancels orders without already-placed bets
- Orders with confirmed bets are returned in `notCancelled`
- No request body required
# Cancel specific orders
Source: https://docs.55-tech.com/api-reference/orders/cancel-specific-orders
/zh/abp-api/openapi.json post /cancel-orders
Cancel one or more orders by filter. At least one filter must be provided. Multiple filters are combined with OR logic.
**Filters:**
- `orderIds` — List of numeric order IDs
- `requestUuids` — List of request UUID strings
- `userRef` — Cancel all orders matching this user reference
**Behavior:**
- Only orders in PENDING or PARTIALLY_FILLED status can be cancelled
- Orders with confirmed bets cannot be fully cancelled
- Cancelled orders are marked in the database and signaled to stop retry loops
# Get orders
Source: https://docs.55-tech.com/api-reference/orders/get-orders
/zh/abp-api/openapi.json get /orders
Retrieve orders with keyset pagination. At least one filter must be provided.
**Filters (OR logic):**
- `orderIds` — Comma-separated list of order IDs
- `requestUuids` — Comma-separated list of request UUIDs
- `userRef` — User reference string
**Pagination:**
- Results are ordered by orderId descending (newest first)
- Use `afterOrderId` from `nextCursor` in the response to fetch the next page
- `hasMore` indicates if more results are available
Returns enriched order details including bets and fixture/outcome/player metadata.
**Order Status Values:** PENDING, PROCESSING, FILLED, PARTIALLY_FILLED, CANCELLED, REJECTED, EXPIRED, FAILED
# Place betting orders
Source: https://docs.55-tech.com/api-reference/orders/place-betting-orders
/zh/abp-api/openapi.json post /place-orders
Submit one or more betting orders for placement with bookmakers.
**Required fields per order:** `requestUuid`, `outcomeId`, `playerId`, `orderPrice`, `orderStake`, `userRef`, `testOrder`, and either `fixtureId` (fixture markets) or `futureId` (futures/outright markets — coming soon)
**Optional fields:** `bookmakers`, `participantId` (futures only), `orderCurrency`, `back`, `expiresAt`, `acceptBetterOdds`, `acceptPartialStake`, `meta`
**Key behavior:**
- `requestUuid` must be a valid UUID format (used for idempotency — duplicates within 30 minutes are rejected with 409)
- `fixtureId` and `futureId` are mutually exclusive — provide one or the other
- Orders with `futureId` currently return `501 Not Implemented` (coming soon)
- Server automatically selects the best bookmaker by odds/limits unless `bookmakers` is specified
- `clientName` is set automatically from your API key
- Orders expire after `expiresAt` (default: 5 seconds from now, capped at 24 hours maximum)
- Maximum 100 orders per request
**Response:**
- `status`: "accepted" (all accepted), "partial-success" (some declined), or "declined" (all declined)
- `acceptedOrders`: Orders that passed validation with their current status and any placed bets
- `declinedOrders`: Orders that failed validation with `declineReason`
# Health check
Source: https://docs.55-tech.com/api-reference/system/health-check
/zh/scraping-api/openapi.json get /healthz
Liveness probe. Returns {"ok": true}.
# Usage metrics
Source: https://docs.55-tech.com/api-reference/usage/usage-metrics
/zh/scraping-api/openapi.json get /usage
Per-key usage metrics: request counts, success/fail rates, bytes transferred, protocol breakdown, and top domains.
# Real-time Subscription (WebSocket)
Source: https://docs.55-tech.com/api-reference/websocket/real-time-subscription-websocket
/zh/mm-api/openapi.json get /ws/subscribe
## Real-time Data Subscription (WebSocket)
**URL:** `wss://mmapi.55-tech.com/ws/subscribe`
**Connection Limits:** Maximum 5 concurrent connections per API key.
### Protocol
1. **Connect** to `/ws/subscribe`
2. **Subscribe** (within 30 seconds):
```json
{
"type": "subscribe",
"api_key": "your-client-api-key-uuid",
"channels": ["orders", "bets", "accounts", "scores", "emergency"]
}
```
3. **Confirmation**:
```json
{"type": "subscribed", "subscription_id": 1, "channels": ["orders", "bets"]}
```
4. **Receive broadcasts**:
```json
{
"type": "broadcast",
"channel": "orders",
"event": "UPDATE",
"payload": {"id": 123, "orderStatus": "FILLED"},
"old": {"orderStatus": "PLACED"}
}
```
5. **Keep-alive**: Send `{"type": "ping"}` → Receive `{"type": "pong"}`
6. **Heartbeat**: Server sends `{"type": "heartbeat"}` every 60s
7. **Unsubscribe**: Send `{"type": "unsubscribe"}`
### Channels
| Channel | Description | Filtered |
|---------|-------------|----------|
| `orders` | Order placement, updates, fills | By client |
| `bets` | Hedge bet updates | By client |
| `accounts` | Balance changes | By client |
| `scores` | Live score updates | Global |
| `emergency` | Emergency mode status | Global |
### Error Codes
- `api_key required` - Missing api_key
- `Invalid api_key format` - Must be UUID
- `Invalid API key` - Not found or inactive
- `Connection limit exceeded` - Max 5 per API key
# 55-Tech API Documentation
Source: https://docs.55-tech.com/index
Official API documentation for 55-Tech's Market Making (MM) and Automated Bet Placing (ABP) platforms. High-frequency trading and automated bet execution for sports betting markets.
**B2B Platform** — 55-Tech provides enterprise trading infrastructure for licensed operators, trading firms, and sports betting platforms. Contact [contact@55-tech.com](mailto:contact@55-tech.com) for API access.
## What is 55-Tech?
55-Tech operates two core trading platforms:
* **Market Making (MM)** — High-frequency market making engine that places orders on prediction market exchanges (Polymarket, Kalshi) and hedges on bookmakers
* **Automated Bet Placing (ABP)** — Place bets across 30 bookmakers through a single API with real-time tracking and settlement
Both platforms consume real-time odds data from [OddsPapi](https://docs.oddspapi.io/) and expose REST + WebSocket APIs for client integration.
***
## Get started
Get connected to both APIs in minutes.
Place bets across 30 bookmakers through a single API.
High-frequency market making on prediction exchanges.
Real-time odds data powering both platforms.
# MM Authentication - API Key Setup
Source: https://docs.55-tech.com/mm-api/authentication
Learn how to authenticate MM API requests using the X-API-Key header (UUID). Client API and WebSocket authentication.
All MM client endpoints require authentication via the `X-API-Key` header (UUID format).
## Client API authentication
Pass your API key (UUID format) in the `X-API-Key` header:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/me
```
Your API key is bound to a specific `client` entity. All data returned is filtered to your client — you only see your own orders, bets, and positions.
## WebSocket authentication
For WebSocket connections, send your API key in the subscribe message:
```json theme={null}
{
"type": "subscribe",
"api_key": "YOUR_API_KEY",
"channels": ["orders", "bets", "accounts", "scores"]
}
```
You must send the subscribe message within **30 seconds** of connecting, or the connection is closed. See [WebSocket](/mm-api/websocket) for details.
## Rate limiting
Rate limits are enforced per client:
* **REST API:** 100 requests per minute
* **WebSocket:** Maximum 5 concurrent connections per API key
* **Exceeded:** Returns `429 Too Many Requests`
## Error responses
| Status | Meaning |
| ------ | ----------------------------------------------- |
| `401` | Invalid or missing API key (must be valid UUID) |
| `429` | Rate limit exceeded |
```json theme={null}
{"detail": "Invalid or missing API key"}
```
# MM Error Handling
Source: https://docs.55-tech.com/mm-api/errors
MM API error codes, HTTP status codes, error response format, and emergency mode behavior.
## Error response format
All API errors return a JSON body:
```json theme={null}
{
"detail": "Invalid or missing API key"
}
```
Validation errors include location details:
```json theme={null}
{
"detail": [
{
"loc": ["query", "fixture_id"],
"msg": "Field required",
"type": "missing"
}
]
}
```
## HTTP status codes
| Status | Description |
| ------ | -------------------------------------------------------------- |
| `200` | Success |
| `401` | Unauthorized — invalid or missing API key (must be valid UUID) |
| `404` | Not found — resource does not exist |
| `422` | Validation error — request parameters failed validation |
| `426` | Upgrade required — WebSocket endpoint called via HTTP |
| `429` | Rate limited — exceeded 100 requests/minute |
| `500` | Internal server error |
## Rate limiting
The MM API enforces **100 requests/minute** per client API key.
When rate limited, the API returns `429 Too Many Requests`. Implement exponential backoff in your retry logic (wait 1s, 2s, 4s, etc.).
## Common errors
### Authentication (401)
Your API key is missing, invalid, or not in UUID format.
```bash theme={null}
# Correct
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/me
# Wrong header name — returns 401
curl -H "Authorization: Bearer your-key" \
https://mmapi.55-tech.com/api/v1/me
# Missing header — returns 401
curl https://mmapi.55-tech.com/api/v1/me
```
### Not found (404)
The requested resource doesn't exist:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/orders/999999
# Returns: 404
```
### Validation errors (422)
Required query parameters are missing or have invalid values. Check the `loc` field to identify the problematic parameter.
### WebSocket upgrade (426)
If you call the WebSocket endpoint via HTTP instead of upgrading to a WebSocket connection, you receive a `426 Upgrade Required`.
## WebSocket errors
When subscribing to the WebSocket, these error messages may be returned:
| Error | Description |
| --------------------------- | -------------------------------------------- |
| `api_key required` | No `api_key` field in subscribe message |
| `Invalid api_key format` | Not a valid UUID |
| `Invalid API key` | Key not found or client inactive |
| `Connection limit exceeded` | Already at 5 active connections for this key |
# MM API Overview - Market Making Engine
Source: https://docs.55-tech.com/mm-api/overview
MM-V2 Trading API overview. High-frequency market making engine placing orders on prediction market exchanges (Polymarket, Kalshi) and automatically hedging on bookmakers (Pinnacle, Vertex).
## What is MM?
The **Market Making (MM)** engine is a high-frequency trading system that:
1. **Receives live odds** from bookmakers via [OddsPapi v5](https://docs.oddspapi.io/) WebSocket
2. **Posts orders** on prediction market exchanges at calculated prices
3. **Detects fills** in real-time via exchange WebSockets
4. **Automatically hedges** filled positions on bookmakers
5. **Tracks positions, exposure, and PnL** through the full settlement lifecycle
The client API gives you read access to all trading activity, positions, and analytics for your client.
## How market making works
```
Bookmaker odds (OddsPapi)
↓
Strategy engine (calculate exchange price)
↓
Place/replace orders on exchanges
↓
Exchange fill detected (WebSocket)
↓
Automatic hedge bet on bookmaker
↓
Position & PnL tracking
```
### Order pricing
The engine converts bookmaker odds to exchange probability prices and posts orders accordingly. Price ticks and order sizing are configured per-tournament.
### Hedging
When an exchange order fills, the engine automatically hedges by placing a corresponding bet on the assigned bookmaker. The process handles concurrent fills safely and reposts filled amounts back on the exchange to maintain market presence.
## Base URL
```
https://mmapi.55-tech.com
```
All client endpoints are prefixed with `/api/v1/`.
## Key concepts
### Clients
Clients are trading entities configured with specific exchange accounts and bookmaker hedging targets.
* **Simple format:** `"vertex"` — client name IS the bookmaker
* **Compound format:** `"demo:vertex"` — client=demo, hedges on vertex
### Exchanges
Target platforms where orders are placed:
| Exchange | Auth method | Order type |
| ------------- | ----------------------- | --------------------- |
| Polymarket | Ethereum wallet signing | CLOB limit orders |
| Polymarket US | JWT + gRPC | gRPC order submission |
| Kalshi | RSA-PSS signing | REST limit orders |
| Novig.us | OAuth 2.0 | REST orders |
| SX.bet | API key + EIP712 | Signed orders |
| Betfair | SSL certificate | Exchange API |
| ProphetX | JWT | REST orders |
| Matchbook | Session token | REST orders |
| Smarkets | Session token | REST orders |
| 4casters | Token auth | Socket.IO |
| Predict.fun | JWT + EIP712 | Signed orders |
### Order lifecycle
```
PENDING_PLACEMENT → PLACED → PARTIAL / FILLED / FAILED / CANCELLED / EXPIRED
```
### Match status
```
NOT_MATCHED → PARTIALLY_MATCHED → FULLY_MATCHED
```
### Settlement status
```
UNDECIDED → WON / LOST / VOID / HALF_WON / HALF_LOST
```
### Tradeable outcomes
Default outcome IDs: 111, 112, 121, 122, 131, 132, 141, 142, 151, 152, 161, 162, 171, 172
These cover 1x2 markets, over/under, handicap, correct score, and both sides.
## Endpoints at a glance
### Client API (`X-API-Key` auth)
| Category | Endpoints | Description |
| ------------- | ------------------------------- | -------------------------------------------- |
| **Identity** | `GET /api/v1/me` | Get your client info |
| **Orders** | `GET /api/v1/orders` | View exchange orders (paginated, filterable) |
| **Orders** | `GET /api/v1/orders/{id}` | Get specific order |
| **Orders** | `GET /api/v1/orders/open` | List open orders for a fixture |
| **Orders** | `GET /api/v1/orders/summary` | Order statistics |
| **Bets** | `GET /api/v1/bets` | View hedge bets (paginated) |
| **Bets** | `GET /api/v1/bets/{id}` | Get specific bet |
| **Bets** | `GET /api/v1/bets/summary` | Bet statistics |
| **Fixtures** | `GET /api/v1/fixtures` | List fixtures with scores |
| **Fixtures** | `GET /api/v1/fixtures/{id}` | Get specific fixture |
| **Positions** | `GET /api/v1/positions` | Aggregated positions |
| **Positions** | `GET /api/v1/positions/summary` | Position summary |
| **Accounts** | `GET /api/v1/accounts` | List exchange accounts (no credentials) |
### WebSocket
| Endpoint | Description |
| ------------------ | ----------------------------------------------------------- |
| `WS /ws/subscribe` | Real-time order, bet, account, score, and emergency updates |
## Rate limits
* REST API: **100 requests/minute** per client
* WebSocket: **5 concurrent connections** per API key
## Next steps
Set up your API key.
Make your first API calls.
# MM Quickstart - First API Calls
Source: https://docs.55-tech.com/mm-api/quickstart
Step-by-step guide to using the MM Trading API. Verify identity, view exchange orders, check positions, monitor hedge bets, and track PnL.
The MM engine places orders automatically based on bookmaker odds. The client API provides **read access** to all trading activity. You do not place orders through this API — the engine does it for you.
## Step 1: Verify your identity
Confirm your API key works and see your client info:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/me
```
Response:
```json theme={null}
{
"client": "your-client",
"clientName": "Your Client Name",
"active": true
}
```
## Step 2: View exchange orders
See orders the engine has placed on exchanges:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/orders
```
Filter by exchange, fixture, or status:
```bash theme={null}
# Filled Polymarket orders
curl -H "X-API-Key: YOUR_API_KEY" \
"https://mmapi.55-tech.com/api/v1/orders?exchange=polymarket&status=FILLED"
# Open orders for a specific fixture
curl -H "X-API-Key: YOUR_API_KEY" \
"https://mmapi.55-tech.com/api/v1/orders/open?fixture_id=id1000000861624412"
```
Each order includes exchange-specific identifiers, matched amounts, and odds snapshots:
```json theme={null}
{
"orderId": 123,
"fixtureId": "id1000000861624412",
"outcomeId": 161,
"exchange": "polymarket",
"exchangeOrderId": "0x1a2b3c...",
"exchangeOutcomeId": "12345678",
"orderStatus": "FILLED",
"matchedStatus": "FULLY_MATCHED",
"orderCents": 0.45,
"orderStake": 100.0,
"matchedStake": 100.0,
"settlementStatus": "UNDECIDED",
"bookmakerOutcomePrice": 1.808,
"bookmakerOutcomeLimit": 5000.0,
"createdAt": "2026-02-15T10:30:00Z",
"matchedAt": "2026-02-15T10:30:05Z"
}
```
## Step 3: View hedge bets
See bets placed on bookmakers to hedge exchange fills:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/bets
```
Each bet is linked to an exchange order via `orderId`:
```json theme={null}
{
"betId": 456,
"orderId": 123,
"client": "your-client",
"bookmaker": "vertex",
"bookmakerBetId": "789",
"placedPrice": 1.808,
"placedStake": 100.0,
"betStatus": "matched",
"settlementStatus": null,
"placedAt": "2026-02-15T10:30:08Z"
}
```
## Step 4: Check positions
View aggregated positions across all fixtures and exchanges:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/positions
```
Get a position summary:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/positions/summary
```
## Step 5: View profit & loss
Get your PnL breakdown:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/pnl
```
Filter by date range:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
"https://mmapi.55-tech.com/api/v1/pnl?from_date=2026-01-01&to_date=2026-01-31"
```
## Step 6: View fixtures
See which events the engine is trading on:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/fixtures
```
Filter by sport, tournament, or live status:
```bash theme={null}
# Live fixtures only
curl -H "X-API-Key: YOUR_API_KEY" \
"https://mmapi.55-tech.com/api/v1/fixtures?live=true"
# Filter by tournament
curl -H "X-API-Key: YOUR_API_KEY" \
"https://mmapi.55-tech.com/api/v1/fixtures?tournament_id=17"
```
## Step 7: List accounts
View your exchange accounts (credentials are never exposed):
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/accounts
```
```json theme={null}
[
{
"exchange": "polymarket",
"username": "0x1234...abcd",
"client": "your-client",
"bookmaker": "vertex",
"balance": 50000.0,
"maxOutcomeStake": 1000.0,
"active": true
}
]
```
## Next steps
Get real-time order fills, hedge bets, and score updates.
Explore all available endpoints.
# MM WebSocket - Real-Time Updates
Source: https://docs.55-tech.com/mm-api/websocket
Connect to the MM WebSocket for real-time order fills, hedge bet updates, account balance changes, live scores, and emergency status. server-backed broadcasting.
## Endpoint
```
wss://mmapi.55-tech.com/ws/subscribe
```
## Connection flow
### 1. Connect
Open a WebSocket connection. No authentication is needed at connection time.
### 2. Subscribe
Send a subscribe message within **30 seconds** of connecting:
```json theme={null}
{
"type": "subscribe",
"api_key": "your-uuid-api-key",
"channels": ["orders", "bets", "accounts", "scores", "emergency"]
}
```
### 3. Confirmation
On success, the server responds with:
```json theme={null}
{
"type": "subscribed",
"subscription_id": 1,
"channels": ["orders", "bets", "accounts", "scores", "emergency"]
}
```
### 4. Receive broadcasts
Data updates arrive as broadcast messages with both current and previous state:
```json theme={null}
{
"type": "broadcast",
"channel": "orders",
"event": "UPDATE",
"payload": {
"orderId": 123,
"orderStatus": "FILLED",
"matchedStake": 100.0,
"matchedAt": "2026-02-15T10:30:05Z"
},
"old": {
"orderStatus": "PLACED",
"matchedStake": 0
}
}
```
The `old` field contains the previous state, making it easy to detect what changed.
### 5. Keep alive
Send a ping to keep the connection alive:
```json theme={null}
{"type": "ping"}
```
Server responds with:
```json theme={null}
{"type": "pong"}
```
The server also sends heartbeats every **60 seconds**:
```json theme={null}
{"type": "heartbeat"}
```
### 6. Unsubscribe
To stop receiving updates and clean up your subscription:
```json theme={null}
{"type": "unsubscribe"}
```
## Channels
### Client-filtered channels
These channels only deliver data belonging to your client:
| Channel | Events | Description |
| ---------- | -------------- | ----------------------------------------------- |
| `orders` | INSERT, UPDATE | Exchange order placement, status changes, fills |
| `bets` | INSERT, UPDATE | Hedge bet placement, confirmation, settlement |
| `accounts` | UPDATE | Account balance and status changes |
### Global channels
All subscribers receive these:
| Channel | Events | Description |
| ----------- | ------ | ----------------------------------------- |
| `scores` | UPDATE | Live score updates (goals, sets, periods) |
| `emergency` | UPDATE | Emergency mode activation/deactivation |
## Payload examples
### Order fill
When an exchange order gets matched:
```json theme={null}
{
"type": "broadcast",
"channel": "orders",
"event": "UPDATE",
"payload": {
"orderId": 123,
"fixtureId": "id1000000861624412",
"outcomeId": 161,
"exchange": "polymarket",
"exchangeOrderId": "0x1a2b3c...",
"orderStatus": "FILLED",
"matchedStatus": "FULLY_MATCHED",
"orderCents": 0.45,
"orderStake": 100.0,
"matchedStake": 100.0,
"matchedAt": "2026-02-15T10:30:05Z"
},
"old": {
"orderStatus": "PLACED",
"matchedStatus": "NOT_MATCHED",
"matchedStake": 0
}
}
```
### Hedge bet placed
After an order fills, the system automatically places a hedge bet:
```json theme={null}
{
"type": "broadcast",
"channel": "bets",
"event": "INSERT",
"payload": {
"betId": 456,
"orderId": 123,
"client": "your-client",
"bookmaker": "vertex",
"placedPrice": 1.808,
"placedStake": 100.0,
"betStatus": "matched",
"sentData": {
"requestId": "a1b2c3d4",
"eventId": 98765,
"price": 1.808,
"amount": 100.0,
"side": "1"
},
"receivedData": {
"betId": "789",
"status": "U"
},
"placedAt": "2026-02-15T10:30:08Z"
}
}
```
### Live score update
```json theme={null}
{
"type": "broadcast",
"channel": "scores",
"event": "UPDATE",
"payload": {
"fixtureId": "id1000000861624412",
"live": true,
"statusId": 1,
"currentPeriod": "2nd Half",
"currentMinute": 67,
"scores": {
"home": 2,
"away": 1,
"period1Home": 1,
"period1Away": 0
}
}
}
```
### Emergency status
```json theme={null}
{
"type": "broadcast",
"channel": "emergency",
"event": "UPDATE",
"payload": {
"emergency": true,
"reason": "Manual trigger",
"triggeredAt": "2026-02-15T10:30:00Z"
}
}
```
## Connection limits
| Setting | Value |
| --------------------------- | ------------------------ |
| Max connections per API key | 5 |
| Auth timeout | 30 seconds |
| Server heartbeat interval | 60 seconds |
| Client ping interval | 30 seconds (recommended) |
## Error messages
| Error | Description |
| --------------------------- | -------------------------------------------------- |
| `api_key required` | Missing `api_key` field in subscribe message |
| `Invalid api_key format` | API key must be a valid UUID |
| `Invalid API key` | API key not found or client is inactive |
| `Connection limit exceeded` | Already have 5 active connections for this API key |
## Example: Python client
```python theme={null}
import asyncio
import json
import websockets
async def connect():
uri = "wss://mmapi.55-tech.com/ws/subscribe"
async with websockets.connect(uri) as ws:
# Subscribe
await ws.send(json.dumps({
"type": "subscribe",
"api_key": "YOUR_API_KEY",
"channels": ["orders", "bets", "scores"]
}))
# Wait for confirmation
sub_resp = json.loads(await ws.recv())
print(f"Subscribed (id={sub_resp.get('subscription_id')})")
# Keep-alive task
async def keep_alive():
while True:
await asyncio.sleep(30)
await ws.send(json.dumps({"type": "ping"}))
asyncio.create_task(keep_alive())
# Listen for updates
async for message in ws:
data = json.loads(message)
if data["type"] == "broadcast":
channel = data["channel"]
event = data["event"]
print(f"[{channel}:{event}] {data['payload']}")
if "old" in data:
print(f" Changed from: {data['old']}")
asyncio.run(connect())
```
## Example: JavaScript client
```javascript theme={null}
const WebSocket = require('ws');
const ws = new WebSocket('wss://mmapi.55-tech.com/ws/subscribe');
ws.on('open', () => {
ws.send(JSON.stringify({
type: 'subscribe',
api_key: 'YOUR_API_KEY',
channels: ['orders', 'bets', 'scores']
}));
});
ws.on('message', (raw) => {
const msg = JSON.parse(raw);
if (msg.type === 'subscribed') {
console.log(`Subscribed (id=${msg.subscription_id})`);
}
if (msg.type === 'broadcast') {
console.log(`[${msg.channel}:${msg.event}]`, msg.payload);
if (msg.old) {
console.log(' Changed from:', msg.old);
}
}
});
// Keep alive
setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
}
}, 30000);
```
# Quickstart - Connect to 55-Tech APIs
Source: https://docs.55-tech.com/quickstart
Get started with the 55-Tech ABP, MM, and Scraping APIs. Check connectivity, authenticate, and make your first API calls.
## Prerequisites
You need an API key for the platform you want to use. Contact [contact@55-tech.com](mailto:contact@55-tech.com) to get your key.
| Platform | Auth Header | Base URL |
| --------------------------- | ----------- | ---------------------------------- |
| ABP (Automated Bet Placing) | `X-API-Key` | `https://v2.55-tech.com` |
| MM (Market Making) | `X-API-Key` | `https://mmapi.55-tech.com` |
| Scraping API | `X-API-Key` | `https://scraping-api.55-tech.com` |
***
## ABP — Automated Bet Placing
### 1. List your accounts
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://v2.55-tech.com/accounts
```
### 2. Get a betslip (live odds)
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
"https://v2.55-tech.com/betslip?fixtureId=ID&outcomeId=161&playerId=0"
```
### 3. Place an order
```bash theme={null}
curl -X POST -H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{"orders": [{"fixtureId": "...", "outcomeId": 161, "playerId": 0, "stake": 10, "minPrice": 1.5}]}' \
https://v2.55-tech.com/place-orders
```
### 4. Track results
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://v2.55-tech.com/orders
```
***
## MM — Market Making
### 1. Verify your identity
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/me
```
### 2. List your orders
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/orders
```
### 3. Check positions
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/positions
```
### 4. View profit & loss
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://mmapi.55-tech.com/api/v1/pnl
```
***
## Scraping API
### 1. Check network status
```bash theme={null}
curl https://scraping-api.55-tech.com/network/status
```
### 2. Fetch a page
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://example.com" \
https://scraping-api.55-tech.com/fetch
```
### 3. Fetch with geo targeting
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://example.com" \
-H "X-Geo: DE" \
https://scraping-api.55-tech.com/fetch
```
### 4. Check usage
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://scraping-api.55-tech.com/usage
```
***
## Real-time updates
All APIs support WebSocket connections for live updates. See the WebSocket documentation for each API:
Real-time order, bet, and settlement updates.
Live order fills, bets, and score updates.
Real-time fetch results via WebSocket relay.
# Scraping API Authentication
Source: https://docs.55-tech.com/scraping-api/authentication
Authenticate Scraping API requests using the X-API-Key header. Rate limits, key formats, and per-key configuration.
All Scraping API endpoints (except `GET /healthz`) require authentication.
## Passing your API key
Pass your API key using one of these methods depending on the endpoint:
**HTTP header (for `/fetch`, `/usage`, `/network/*`, `/debug/pick`):**
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://example.com" \
https://scraping-api.55-tech.com/fetch
```
**JSON body field (for `/browser`, `/ws`, and `/amqp`):**
```json theme={null}
{
"apiKey": "YOUR_API_KEY",
"url": "https://example.com"
}
```
For `/browser`, `/ws`, and `/amqp`, the key is read from the JSON body field `apiKey` (or `key`), or the `X-API-Key` header.
## Error responses
| Status | Meaning |
| ------ | --------------- |
| `401` | Missing API key |
| `403` | Invalid API key |
## Rate limits
Each API key has a configurable rate limit:
| Setting | Default |
| ------------------------- | ------------ |
| Requests per second (RPS) | 10 |
| Burst capacity | Equal to RPS |
When rate limited, the API returns `429 Too Many Requests` with these headers:
```
Retry-After: 1
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
```
Implement exponential backoff: wait 1s, 2s, 4s, etc.
Check your current rate limit and usage:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://scraping-api.55-tech.com/usage
```
The response includes your remaining tokens:
```json theme={null}
{
"rate_limit": {
"rps": 10.0,
"burst": 10,
"remaining": 9.3
}
}
```
## Public endpoints
These endpoints do **not** require authentication:
| Endpoint | Description |
| -------------- | ------------------------------- |
| `GET /` | API overview and status |
| `GET /healthz` | Liveness probe (`{"ok": true}`) |
## Obtaining a key
Contact [contact@55-tech.com](mailto:contact@55-tech.com) to obtain an API key with a rate limit configured for your use case.
# Scraping API Error Handling
Source: https://docs.55-tech.com/scraping-api/errors
Scraping API error codes, block detection, and retry strategies.
## Error response format
All API errors return JSON:
```json theme={null}
{
"detail": "Invalid or missing API key"
}
```
## HTTP status codes
| Status | Description |
| ------ | ----------------------------------------------------------------------------------------- |
| `200` | Success — target response proxied (check `meta.blocked` for block detection) |
| `401` | Unauthorized — missing API key |
| `403` | Forbidden — invalid API key |
| `429` | Rate limited. Headers include `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining` |
| `502` | Bad gateway — no healthy agent available for this domain |
| `504` | Gateway timeout — target did not respond within timeout (default 30s) |
## Block detection
Every fetch response includes a `meta.blocked` field:
```json theme={null}
{
"meta": {
"status": 403,
"blocked": true,
"agent": { "id": "scraping-de5" }
},
"raw": "Access Denied..."
}
```
When `meta.blocked` is `true`:
* The agent that got blocked is automatically excluded for this domain
* Your next request will be routed to a different agent
* The response still contains the full target body for your inspection
You can check per-agent health for a domain:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://scraping-api.55-tech.com/network/health/example.com
```
The health endpoint returns three states: `available`, `limited` (temporary), and `unavailable` (longer exclusion). Agents recover automatically.
## Retry strategies
### Rate limit (429)
Use exponential backoff: wait 1s, 2s, 4s, etc. Check `GET /usage` for your current rate limit status.
### Blocked (meta.blocked = true)
No client-side retry needed. The API automatically routes your next request to a different, healthy agent. Just keep making requests normally.
### Browser validation failure (502)
If you use `expectSelector` or `expectContains` on `/browser` and the rendered page doesn't match, the API automatically retries on a different node before returning the error. No client-side retry needed for the first failure.
### Timeout (504)
* Increase the `timeout` field in your POST body (default: 30 seconds)
* For `/browser`, note that browser startup adds \~2-5 seconds — set `timeout` accordingly
* Use `X-Geo` to pick agents geographically closer to the target
* Use `GET /debug/pick?url=...` to preview which agent would be selected
### No agents available (502)
All agents for this domain are temporarily excluded. Wait a moment and retry, or use `GET /network/health/{domain}` to check recovery status.
## WebSocket errors
| Error | Cause | Close code |
| ------------------------ | ---------------------------------------------- | ---------- |
| Invalid API key | Missing or unrecognized key in connect message | `1008` |
| Rate limit exceeded | Too many concurrent connections | `1008` |
| Connect timeout | No JSON connect message within 10 seconds | `1008` |
| Target connection failed | Agent could not connect to target WS | `1011` |
| Agent unavailable | No agent available for the request | `1011` |
## AMQP errors
AMQP errors are delivered as SSE events:
```
event: error
data: {"message": "agent error: connection refused", "code": 1011}
```
After an error event, the SSE stream closes.
## Common errors
### Missing target URL
```bash theme={null}
# Wrong: no URL specified
curl -H "X-API-Key: YOUR_KEY" https://scraping-api.55-tech.com/fetch
# Right: URL in header
curl -H "X-API-Key: YOUR_KEY" \
-H "X-Target-URL: https://example.com" \
https://scraping-api.55-tech.com/fetch
```
### Invalid geo code
```bash theme={null}
# Wrong: full country name
-H "X-Geo: Germany"
# Right: ISO 2-char code
-H "X-Geo: DE"
```
### WebSocket connect timeout
The first JSON message with `apiKey` and `url` must be sent within **10 seconds** of opening the WebSocket connection, or it will be closed with code `1008`.
# Scraping API Overview
Source: https://docs.55-tech.com/scraping-api/overview
55 Tech Scraping API — route HTTP, browser, WebSocket, and AMQP requests through 80+ geo-distributed agents.
## What is the Scraping API?
The **Scraping API** by [55 Tech](https://55-tech.com/) lets you route HTTP, browser, WebSocket, and AMQP requests through a network of 80+ geo-distributed agents. Within seconds, the API automatically identifies the most reliable agents for each target — so your requests become instantly more successful without managing proxies yourself.
**What you can do:**
* **HTTP fetch** — GET/POST/PUT/PATCH/DELETE with full header, body, and cookie control
* **Browser fetch** — Real Chromium rendering with JS execution, Cloudflare bypass, cookies (including httpOnly), screenshots, proxy chaining, and response validation
* **WebSocket relay** — Bidirectional frame relay (Socket.IO, SignalR, Centrifugo, GraphQL-WS, raw WebSocket)
* **AMQP consumer** — Stream RabbitMQ messages via Server-Sent Events (SSE)
* **Geo targeting** — Pin requests to specific countries or individual agents
* **Proxy chaining** — Route browser requests through residential proxies for datacenter speed + residential fingerprint
* **Response validation** — Verify expected content exists in browser-rendered pages with auto-retry on different agent
* **Block detection** — Responses include a `meta.blocked` flag when access restrictions are detected
## Base URL
```
https://scraping-api.55-tech.com
```
## Authentication
Pass your API key via the `X-API-Key` header or (for WebSocket/AMQP) the `apiKey` body field. See [Authentication](/scraping-api/authentication) for details.
## Endpoints
| Endpoint | Method | Auth | Description |
| -------------------------- | ----------------------------- | ------------------ | ------------------------------------------------------------------------------ |
| `/fetch` | GET, POST, PUT, PATCH, DELETE | Required | Proxy HTTP requests through the agent network |
| `/browser` | POST | In body | Render a page with real Chromium (JS, cookies, screenshots, proxy, validation) |
| `/ws` | WebSocket | In connect message | Bidirectional WebSocket relay |
| `/amqp` | POST | In body or header | AMQP/RabbitMQ consumer relay (SSE stream) |
| `/usage` | GET | Required | Per-key usage metrics (requests, bytes, top domains) |
| `/network/agents` | GET | Required | List all agents with slug, name, country |
| `/network/status` | GET | Required | Network summary (total nodes, nodes per country) |
| `/network/geo` | GET | Required | Agents grouped by country |
| `/network/health/{domain}` | GET | Required | Per-agent health state for a domain |
| `/debug/pick` | GET | Required | Preview which agent would be selected |
| `/healthz` | GET | No | Liveness probe |
## Control headers
These headers control routing and are stripped before forwarding to the target:
| Header | Aliases | Description | Example |
| --------------- | ----------------------------------------------- | --------------------------------------------------------------- | ------------------------------ |
| `X-Target-URL` | — | Target URL (no encoding needed, recommended) | `https://example.com/?foo=bar` |
| `X-Geo` | `X-Geo-CC`, `X-Geo-Strict`, `X-CC`, `X-Country` | Restrict to specific countries (comma-separated ISO codes) | `US,DE,AT` |
| `X-Expect-JSON` | — | Hint that the target response is JSON | `1` |
| `X-Agent` | — | Route through specific agent(s) by slug or comma-separated list | `de1`, `de1,at5,us3` |
| `X-Timeout` | — | Override request timeout in seconds | `60` |
## Rate limiting
Rate limits are **per API key**:
* Default: 10 requests/second
* WebSocket connections consume 1 token on connect (not per frame)
When rate limited, the API returns `429` with headers:
```
Retry-After: 1
X-RateLimit-Limit: 10
X-RateLimit-Remaining: 0
```
Check your current usage with `GET /usage`.
## Fetch response format
All HTTP fetch responses follow this structure:
```json theme={null}
{
"meta": {
"status": 200,
"final_url": "https://example.com/",
"http_version": "HTTP/2",
"elapsed_ms": 245,
"blocked": false,
"headers": { "content-type": "text/html" },
"agent": { "id": "scraping-de1" },
"bytes": 4521
},
"raw": "...",
"raw_json": null
}
```
| Field | Description |
| ------------------- | ------------------------------------------------------------------ |
| `meta.status` | HTTP status code from the target |
| `meta.final_url` | Final URL after redirects |
| `meta.http_version` | HTTP version used (e.g. HTTP/2) |
| `meta.elapsed_ms` | Round-trip time in milliseconds |
| `meta.blocked` | `true` if access restrictions were detected in the response |
| `meta.agent.id` | Node that served the request (e.g. `scraping-de1`) |
| `meta.bytes` | Response body size in bytes |
| `raw` | Response body as text (when not JSON) |
| `raw_json` | Parsed JSON object (when response is valid JSON, otherwise `null`) |
## Next steps
Set up your API key.
Make your first proxied request.
Render JavaScript-heavy pages with a real browser.
# Scraping API Quickstart
Source: https://docs.55-tech.com/scraping-api/quickstart
Make your first proxied HTTP request, WebSocket connection, and AMQP stream through the 55 Tech Scraping API.
## Step 1: Check the network
See which agents are available:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://scraping-api.55-tech.com/network/status
```
```json theme={null}
{
"total_nodes": 80,
"total_countries": 12,
"nodes_by_country": {
"AT": 22,
"DE": 20,
"US": 7,
"IT": 5,
"UK": 4,
"AU": 3,
"GR": 3,
"ES": 2,
"FR": 2,
"BG": 1,
"BR": 1,
"PL": 1
}
}
```
## Step 2: Make an HTTP GET request
Fetch a URL through the agent network using the `X-Target-URL` header:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://httpbin.org/headers" \
https://scraping-api.55-tech.com/fetch
```
```json theme={null}
{
"meta": {
"status": 200,
"final_url": "https://httpbin.org/headers",
"http_version": "HTTP/2",
"elapsed_ms": 312,
"blocked": false,
"headers": { "content-type": "application/json" },
"agent": { "id": "scraping-de5" },
"bytes": 128
},
"raw": null,
"raw_json": {
"headers": {
"Accept": "*/*",
"Host": "httpbin.org"
}
}
}
```
JSON responses are automatically parsed into `raw_json`.
## Step 3: POST with a custom body and headers
Use `POST /fetch` with a JSON body to send custom headers, body, and method to the target:
```bash theme={null}
curl -X POST \
-H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://httpbin.org/post" \
-H "Content-Type: application/json" \
-d '{
"headers": {
"Authorization": "Bearer my-token",
"Accept": "application/json"
},
"body": "{\"key\": \"value\"}",
"timeout": 30
}' \
https://scraping-api.55-tech.com/fetch
```
**POST body fields:**
| Field | Required | Default | Description |
| ----------------- | -------- | ---------------------- | ---------------------------------------- |
| `method` | No | Matches request method | Override HTTP method |
| `headers` | No | `{}` | Custom headers sent to the target origin |
| `body` | No | `""` | Request body sent to the target |
| `timeout` | No | `30` | Request timeout in seconds |
| `allow_redirects` | No | `true` | Follow redirects |
## Step 4: Target a specific country
Use `X-Geo` to route through agents in specific countries:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://httpbin.org/headers" \
-H "X-Geo: US" \
https://scraping-api.55-tech.com/fetch
```
Pin to a specific agent with `X-Agent`:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://httpbin.org/headers" \
-H "X-Agent: de1" \
https://scraping-api.55-tech.com/fetch
```
Or pick randomly from a set of agents:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Target-URL: https://httpbin.org/headers" \
-H "X-Agent: de1,at5,us3" \
https://scraping-api.55-tech.com/fetch
```
## Step 5: Check domain health
See which agents can reach a specific domain:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://scraping-api.55-tech.com/network/health/example.com
```
```json theme={null}
{
"domain": "example.com",
"nodes_checked": 62,
"available": 58,
"limited": 2,
"unavailable": 2,
"details": [
{
"node_id": "scraping-de1",
"slug": "de1",
"state": "available",
"rtt_ms": 145,
"last_check": "2026-03-13T22:15:00Z"
}
]
}
```
## Step 6: Preview agent selection
Debug which agent would be picked without making the actual request:
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
-H "X-Geo: US" \
"https://scraping-api.55-tech.com/debug/pick?url=https://example.com"
```
```json theme={null}
{
"node_id": "scraping-us3",
"node_slug": "us3",
"country": "US",
"valid_until_epoch_ms": 1710374100000
}
```
## Step 7: Check your usage
```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
https://scraping-api.55-tech.com/usage
```
```json theme={null}
{
"api_key": "YOUR_KEY",
"total": 15243,
"success": 14821,
"fail": 422,
"success_rate": 97.2,
"bytes_transferred": 1847362541,
"rate_limit": {
"rps": 10.0,
"burst": 10,
"remaining": 9.3
},
"by_protocol": {
"fetch": 14200,
"ws": 923,
"amqp": 120
},
"top_domains": [
{ "domain": "example.com", "requests": 4521 }
]
}
```
## Next steps
Render JavaScript-heavy pages with a real browser.
Relay WebSocket connections and consume AMQP streams.
Status codes, block detection, and retry strategies.
# Scraping API WebSocket & AMQP
Source: https://docs.55-tech.com/scraping-api/websocket
Relay WebSocket connections and consume AMQP/RabbitMQ streams through the 55 Tech Scraping API agent network.
## WebSocket relay
The Scraping API can relay bidirectional WebSocket connections through the agent network. All WS sub-protocols are relayed transparently — Socket.IO, SignalR, Centrifugo, GraphQL-WS, raw WebSocket, etc.
### Endpoint
```
wss://scraping-api.55-tech.com/ws
```
### Connection flow
Open a WebSocket connection to `wss://scraping-api.55-tech.com/ws`. No authentication happens at connection time — the gateway accepts immediately.
Send a JSON message specifying the target URL and your API key. You have **10 seconds** to send this message before the connection is closed with code `1008`.
```json theme={null}
{
"apiKey": "YOUR_API_KEY",
"url": "wss://target.example.com/stream",
"headers": { "Authorization": "Bearer token" },
"cookies": { "session": "abc123" },
"geo": "US,DE",
"agent": "de1",
"idle_timeout": 0
}
```
| Field | Type | Required | Default | Description |
| -------------- | ------ | -------- | ------- | ------------------------------------------------------------------------ |
| `apiKey` | string | Yes | — | Your API key (also accepts `key`) |
| `url` | string | Yes | — | Target WebSocket URL (`wss://...` or `ws://...`) |
| `headers` | object | No | `{}` | Custom headers for the WS upgrade request |
| `cookies` | object | No | `{}` | Cookies to send with the WS upgrade request |
| `geo` | string | No | — | Country filter for agent selection (e.g., `US,DE`) |
| `agent` | string | No | — | Pin to specific agent(s) by slug (e.g., `de1` or `de1,at5`) |
| `idle_timeout` | float | No | `0` | Seconds to wait for a target message before disconnect. `0` = no timeout |
The gateway picks an agent, the agent connects to the target WebSocket, and the gateway responds with:
```json theme={null}
{
"type": "connected",
"status": 101,
"node_id": "scraping-de1"
}
```
| Field | Description |
| --------- | ----------------------------------- |
| `type` | Always `"connected"` |
| `status` | HTTP status of the WS upgrade (101) |
| `node_id` | Proxy node handling the connection |
After connection, all text and binary frames are relayed transparently:
* **Client → target**: Your messages are forwarded to the target as-is
* **Target → client**: Target messages are forwarded to you as-is
* **Close frame**: Triggers graceful shutdown on both sides
* **PONG frames**: From target are forwarded to client
### Error responses
If something goes wrong, the gateway sends:
```json theme={null}
{
"type": "error",
"message": "description of what went wrong"
}
```
Common errors:
* Invalid or missing API key (close code `1008`)
* Rate limit exceeded (close code `1008`)
* Connect message not received within 10 seconds (close code `1008`)
* Target connection failed
* Agent unavailable
### Example: Python
```python theme={null}
import asyncio
import json
import websockets
async def relay():
async with websockets.connect("wss://scraping-api.55-tech.com/ws") as ws:
# 1. Send connect message
await ws.send(json.dumps({
"apiKey": "YOUR_API_KEY",
"url": "wss://echo.websocket.events",
"geo": "DE"
}))
# 2. Wait for connected confirmation
resp = json.loads(await ws.recv())
if resp.get("type") == "error":
print(f"Error: {resp['message']}")
return
assert resp["type"] == "connected"
print(f"Connected via {resp['node_id']}")
# 3. Send and receive frames
await ws.send("hello from scraping api")
async for msg in ws:
print(f"Received: {msg}")
asyncio.run(relay())
```
### Example: JavaScript
```javascript theme={null}
const WebSocket = require('ws');
const ws = new WebSocket('wss://scraping-api.55-tech.com/ws');
ws.on('open', () => {
ws.send(JSON.stringify({
apiKey: 'YOUR_API_KEY',
url: 'wss://echo.websocket.events',
geo: 'DE'
}));
});
let connected = false;
ws.on('message', (raw) => {
if (!connected) {
const msg = JSON.parse(raw.toString());
if (msg.type === 'connected') {
console.log(`Connected via ${msg.node_id}`);
connected = true;
ws.send('hello from scraping api');
} else if (msg.type === 'error') {
console.error(`Error: ${msg.message}`);
}
return;
}
// After connected, frames are relayed transparently
console.log('Received:', raw.toString());
});
```
### Supported sub-protocols
All sub-protocols are relayed transparently — no special configuration needed:
* Raw WebSocket (text/binary)
* Socket.IO (engine.io transport)
* SignalR (JSON + `\x1e` delimiter)
* Centrifugo (JSON-RPC)
* GraphQL-WS subscriptions
***
## AMQP consumer (SSE)
Stream messages from a RabbitMQ broker through the agent network, delivered as Server-Sent Events. The agent connects to the broker, creates a temporary auto-delete queue, binds it to the specified exchange, and streams messages back.
### Endpoint
```
POST https://scraping-api.55-tech.com/amqp
```
### Request body
```json theme={null}
{
"apiKey": "YOUR_API_KEY",
"host": "broker.example.com",
"port": 5671,
"virtual_host": "/",
"username": "user",
"password": "pass",
"exchange": "my_exchange",
"routing_key": "#",
"queue_prefix": "scrape",
"ssl": true,
"heartbeat": 60,
"geo": "DE",
"agent": "de1"
}
```
Authentication can also be provided via the `X-API-Key` header instead of the `apiKey` body field.
| Field | Type | Required | Default | Description |
| -------------- | ------ | -------- | -------- | --------------------------------------------------------------- |
| `apiKey` | string | Yes | — | Your API key (or use `X-API-Key` header) |
| `host` | string | Yes | — | AMQP broker hostname |
| `port` | int | No | `5672` | Broker port (use `5671` for SSL) |
| `virtual_host` | string | No | `/` | AMQP virtual host |
| `username` | string | No | `""` | Broker username |
| `password` | string | No | `""` | Broker password |
| `exchange` | string | No | `""` | Exchange to bind to |
| `routing_key` | string | No | `#` | Routing key pattern (`#` = all messages) |
| `queue_prefix` | string | No | `scrape` | Prefix for the auto-delete queue name (e.g., `scrape_de1_4821`) |
| `ssl` | bool | No | `false` | Use AMQPS (TLS) connection |
| `heartbeat` | int | No | `60` | AMQP heartbeat interval in seconds |
| `geo` | string | No | — | Country filter for agent selection |
| `agent` | string | No | — | Pin to specific agent by slug |
### SSE events
**`connected`** — Agent connected to broker, queue created and bound:
```
event: connected
data: {"node_id": "scraping-de1", "queue_name": "scrape_de1_4821"}
```
**`message`** — Message received from queue:
```
event: message
data: {"body": "{\"odds\": 1.95}", "routing_key": "19454.match.123", "exchange": "19454.all", "delivery_tag": 1, "content_type": "application/json"}
```
**`error`** — Connection or consumption error (stream ends):
```
event: error
data: {"message": "agent error: connection refused", "code": 1011}
```
### Example: curl
```bash theme={null}
curl -N -X POST \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{
"host": "broker.example.com",
"port": 5671,
"ssl": true,
"username": "user",
"password": "pass",
"exchange": "my_exchange",
"routing_key": "#"
}' \
https://scraping-api.55-tech.com/amqp
```
### Example: Python
```python theme={null}
import requests
import json
resp = requests.post(
"https://scraping-api.55-tech.com/amqp",
headers={"X-API-Key": "YOUR_API_KEY"},
json={
"host": "broker.example.com",
"port": 5671,
"ssl": True,
"username": "user",
"password": "pass",
"exchange": "my_exchange",
"routing_key": "#",
},
stream=True,
)
for line in resp.iter_lines():
if line.startswith(b"data: "):
event = json.loads(line[6:])
print(event)
```
# WebSocket Protocol Reference
Source: https://docs.55-tech.com/scraping-api/ws-reference
Complete reference for the Scraping API WebSocket relay protocol. Connect message schema, response types, error codes, and interactive testing.
## Protocol overview
The WebSocket relay at `wss://scraping-api.55-tech.com/ws` provides a bidirectional proxy. You connect to the gateway, send a JSON connect message, and the gateway relays all frames between you and the target through a geo-distributed agent.
```
Client ←→ Gateway (wss://.../ws) ←→ Agent ←→ Target WebSocket
```
## Interactive playground
The `/ws/docs` endpoint returns the protocol schema:
```bash theme={null}
curl https://scraping-api.55-tech.com/ws/docs
```
```json theme={null}
{
"type": "connected",
"status": 101,
"node_id": "scraping-de1",
"message": "This is a documentation endpoint. Connect via WebSocket at wss://scraping-api.55-tech.com/ws"
}
```
For interactive WebSocket testing, use `wscat`:
```bash theme={null}
npm install -g wscat
wscat -c wss://scraping-api.55-tech.com/ws
```
Then paste:
```json theme={null}
{"apiKey":"YOUR_API_KEY","url":"wss://echo.websocket.events"}
```
## Connect message (client → gateway)
Must be sent as JSON text within **10 seconds** of connecting.
```json theme={null}
{
"apiKey": "YOUR_API_KEY",
"url": "wss://target.example.com/stream",
"headers": {
"Authorization": "Bearer token",
"Origin": "https://target.example.com"
},
"cookies": {
"session": "abc123"
},
"geo": "US,DE",
"agent": "de1",
"idle_timeout": 0
}
```
### Fields
| Field | Type | Required | Default | Description |
| -------------- | ------ | -------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `apiKey` | string | **Yes** | — | API key for authentication. `key` is also accepted. |
| `url` | string | **Yes** | — | Target WebSocket URL (`wss://...` or `ws://...`) |
| `headers` | object | No | `{}` | Custom headers sent with the WS upgrade request to the target. Useful for `Authorization`, `Origin`, `Cookie` headers that the target expects. |
| `cookies` | object | No | `{}` | Cookies sent with the WS upgrade request. Merged into the `Cookie` header. |
| `geo` | string | No | — | Restrict agent selection to specific countries. Comma-separated ISO codes (e.g. `US,DE,AT`). |
| `agent` | string | No | — | Pin to specific agent(s) by slug (e.g. `de1`) or comma-separated for random pick (`de1,at5,us3`). |
| `idle_timeout` | float | No | `0` | If > 0, disconnect after this many seconds without receiving a message from the target. `0` = no idle timeout. |
## Response messages (gateway → client)
### Connected
Sent once after the agent successfully connects to the target WebSocket.
```json theme={null}
{
"type": "connected",
"status": 101,
"node_id": "scraping-de1"
}
```
| Field | Type | Description |
| --------- | ------ | ---------------------------------------------------- |
| `type` | string | Always `"connected"` |
| `status` | int | HTTP status code of the WS upgrade. `101` = success. |
| `node_id` | string | Proxy node handling the relay (e.g. `scraping-de1`) |
### Error
Sent when connection fails or an error occurs. The WebSocket is closed after this message.
```json theme={null}
{
"type": "error",
"message": "Invalid or missing API key"
}
```
| Field | Type | Description |
| --------- | ------ | -------------------------------- |
| `type` | string | Always `"error"` |
| `message` | string | Human-readable error description |
### Relayed frames
After the `connected` message, all subsequent frames are raw relay — no JSON wrapping. Text frames stay text, binary frames stay binary.
## Close codes
| Code | Meaning |
| ------ | -------------------------------------------------------------------- |
| `1000` | Normal closure (you or target closed) |
| `1008` | Policy violation — invalid API key, rate limited, or connect timeout |
| `1011` | Internal error — agent failure or target unreachable |
## Error scenarios
| Scenario | What happens |
| ----------------------------- | -------------------------------------------------------------- |
| No connect message within 10s | Gateway closes with code `1008` |
| Invalid API key | Gateway sends error JSON, closes with `1008` |
| Rate limit exceeded | Gateway sends error JSON, closes with `1008` |
| Target connection refused | Gateway sends error JSON, closes with `1011` |
| No agent available | Gateway sends error JSON, closes with `1011` |
| Target closes connection | Gateway forwards the close frame with target's code and reason |
| Client closes connection | Gateway forwards close to target, cleans up |
| Agent crashes mid-relay | Gateway closes with `1011` |
## Frame flow diagram
```mermaid theme={null}
sequenceDiagram
participant C as Client
participant G as Gateway
participant A as Agent
participant T as Target
C->>G: WS connect
G-->>C: accept
C->>G: {"apiKey", "url"}
G->>A: pick agent
A->>T: WS upgrade
T-->>A: 101 Switching
A-->>G: connected
G-->>C: {"type":"connected"}
rect rgb(240, 248, 255)
Note over C,T: Bidirectional relay
C->>G: text/binary frame
G->>A: relay
A->>T: relay
T-->>A: text/binary frame
A-->>G: relay
G-->>C: relay
end
C->>G: close
G->>A: close
A->>T: close
T-->>A: close
A-->>G: close
G-->>C: close
```
## Rate limiting
WebSocket connections consume **1 token** from your rate limit bucket on connect (when the connect message is validated). Subsequent frames do not consume tokens. If your bucket is empty, the gateway sends an error and closes with `1008`.
## Testing with wscat
```bash theme={null}
# Install
npm install -g wscat
# Connect
wscat -c wss://scraping-api.55-tech.com/ws
# Paste connect message:
{"apiKey":"YOUR_KEY","url":"wss://echo.websocket.events"}
# After "connected" response, type messages to relay:
hello world
# The echo server will send back your message
```
## Testing with Python
```python theme={null}
import asyncio
import json
import websockets
async def test():
async with websockets.connect("wss://scraping-api.55-tech.com/ws") as ws:
await ws.send(json.dumps({
"apiKey": "YOUR_API_KEY",
"url": "wss://echo.websocket.events",
}))
resp = json.loads(await ws.recv())
print(f"Status: {resp}")
if resp["type"] == "connected":
await ws.send("test message")
reply = await ws.recv()
print(f"Echo: {reply}")
asyncio.run(test())
```
# AMQP consumer relay (SSE)
Source: https://docs.55-tech.com/api-reference/amqp/amqp-consumer-relay-sse
/zh/scraping-api/openapi.json post /amqp
Connect to an AMQP/RabbitMQ broker through an agent and stream messages as Server-Sent Events. The agent creates a temporary auto-delete queue, binds it to the specified exchange, and streams messages back as SSE events (connected, message, error).
# Render a page with a real browser
Source: https://docs.55-tech.com/api-reference/browser/render-a-page-with-a-real-browser
/zh/scraping-api/openapi.json post /browser
Render a page using a real Chromium browser on the proxy node. Executes JavaScript, waits for dynamic content, captures all cookies (including httpOnly), and optionally takes a screenshot or evaluates custom JavaScript.
Use this instead of /fetch when the target page requires JavaScript to render content (SPAs, React, Next.js), or when you need to capture browser-set cookies.
The response format matches /fetch (meta + raw/raw_json), with additional cookies, screenshot, and js_result fields.
If expectSelector or expectContains is set and the rendered page doesn't match, the API automatically retries on a different node before returning an error.
# Cancel a specific order
Source: https://docs.55-tech.com/api-reference/client-api/cancel-a-specific-order
/zh/mm-api/openapi.json post /api/v1/orders/{order_id}/cancel
Cancel a specific order by its ID.
# Cancel all open orders
Source: https://docs.55-tech.com/api-reference/client-api/cancel-all-open-orders
/zh/mm-api/openapi.json post /api/v1/orders/cancel-all
Cancel ALL open orders for your client account across all exchanges.
**Use with caution** - this will cancel every open order you have.
Optionally filter by:
- **exchange**: Only cancel orders on a specific exchange
- **fixture_id**: Only cancel orders for a specific fixture
# Get bet details
Source: https://docs.55-tech.com/api-reference/client-api/get-bet-details
/zh/mm-api/openapi.json get /api/v1/bets/{bet_id}
Retrieve detailed information about a specific bet by its ID.
# Get bets summary
Source: https://docs.55-tech.com/api-reference/client-api/get-bets-summary
/zh/mm-api/openapi.json get /api/v1/bets/summary
Get aggregated statistics about all bets for your client account.
Optionally filter by date range to see statistics for a specific time period.
# Get order details
Source: https://docs.55-tech.com/api-reference/client-api/get-order-details
/zh/mm-api/openapi.json get /api/v1/orders/{order_id}
Retrieve detailed information about a specific order by its ID.
# Get orders summary
Source: https://docs.55-tech.com/api-reference/client-api/get-orders-summary
/zh/mm-api/openapi.json get /api/v1/orders/summary
Get aggregated statistics about all orders for your client account.
Optionally filter by date range to see statistics for a specific time period.
# Get positions summary
Source: https://docs.55-tech.com/api-reference/client-api/get-positions-summary
/zh/mm-api/openapi.json get /api/v1/positions/summary
Get aggregated statistics about all positions for your client account.
Optionally filter by date range to see statistics for a specific time period.
# Get profit and loss
Source: https://docs.55-tech.com/api-reference/client-api/get-profit-and-loss
/zh/mm-api/openapi.json get /api/v1/pnl
Get profit and loss (PnL) statistics for your client account.
**Realized PnL**: Profit/loss from settled orders
- WON orders: profit = matchedStake * (1/probability - 1)
- LOST orders: loss = -matchedStake
- HALF_WON/HALF_LOST: 50% of the above
**Unrealized PnL**: Potential profit from open positions (if they win)
- Calculated as: matchedStake * (1/probability - 1) for all unsettled matched orders
Optionally filter by date range to see PnL for a specific time period.
# List accounts
Source: https://docs.55-tech.com/api-reference/client-api/list-accounts
/zh/mm-api/openapi.json get /api/v1/accounts
List exchange accounts configured for your client.
Returns account metadata without sensitive credentials.
Shows which exchanges you have accounts on and their configuration.
# List bets
Source: https://docs.55-tech.com/api-reference/client-api/list-bets
/zh/mm-api/openapi.json get /api/v1/bets
Retrieve a paginated list of hedge bets placed on your behalf.
Bets are created when orders are matched on exchanges. They represent
the hedging activity on the bookmaker side.
Filter by:
- **order_id**: Get bets for a specific order
- **bookmaker**: Filter by bookmaker (e.g., vertex, pinnacle)
- **status**: Filter by bet status - supports comma-separated values (e.g., `placed,pending`)
- `placed`: Successfully placed bets
- `pending`: Bets awaiting confirmation
- `declined`: Rejected bets
# List fixtures
Source: https://docs.55-tech.com/api-reference/client-api/list-fixtures
/zh/mm-api/openapi.json get /api/v1/fixtures
Retrieve a paginated list of fixtures (events/matches).
Fixtures contain event metadata like participants, start time, and scores.
Filter by:
- **fixtureIds**: Filter by specific fixture IDs (repeated query param)
- **tournament_id**: Filter by tournament (e.g., 234 for NHL)
- **sport_id**: Filter by sport (e.g., 1 for Soccer)
- **live**: Filter by live status (true/false)
- **all**: Set to true to see all available fixtures (not just ones with your orders)
# List markets for sport
Source: https://docs.55-tech.com/api-reference/client-api/list-markets-for-sport
/zh/mm-api/openapi.json get /api/v1/markets
Get available markets for a sport from cached odds data.
**Note:** Markets are global reference data - they describe what market types
exist (1x2, Over/Under, etc.) and are not specific to any client. All clients
see the same market definitions.
Markets are aggregated from all active fixtures for the specified sport.
**Required:**
- **sportId**: Get markets for a sport (e.g., 1=Soccer, 14=Ice Hockey)
**Response fields:**
- `marketId`: Market identifier
- `outcomeIds`: List of outcome IDs available for this market
- `playerIds`: List of player IDs (for player prop markets)
- `bookmakers`: Bookmakers offering this market
# List players for sport
Source: https://docs.55-tech.com/api-reference/client-api/list-players-for-sport
/zh/mm-api/openapi.json get /api/v1/players
Get players for a sport from cached odds data.
**Note:** Players are global reference data - they describe what players
have prop markets available. All clients see the same player data.
Players are aggregated from all active fixtures for the specified sport
where playerId > 0 (player prop markets).
**Required:**
- **sportId**: Get players for a sport (e.g., 1=Soccer, 14=Ice Hockey)
**Response fields:**
- `playerId`: Player identifier
- `marketIds`: Markets this player has props in
- `outcomeIds`: Outcome IDs for this player
- `bookmakers`: Bookmakers offering props for this player
# List positions
Source: https://docs.55-tech.com/api-reference/client-api/list-positions
/zh/mm-api/openapi.json get /api/v1/positions
Get aggregated positions for your client account.
A position aggregates all orders for a specific fixture/outcome/exchange combination,
showing total stake, matched stake, and open stake.
This gives you a consolidated view of your exposure on each market.
**Status filters** (supports comma-separated values, e.g., `open,matched`):
- `open`: Only positions with open orders (still on exchange, not fully matched)
- `matched`: Only positions with matched stake > 0 (excludes cancelled)
- `filled`: Only fully matched orders
- `cancelled`: Only cancelled orders
- `active`: Exclude cancelled orders (default)
Examples:
- `/positions?status=open` - Only open positions
- `/positions?status=open,matched` - Open or matched positions
- `/positions?status=filled` - Only filled positions
Optionally filter by date range to see positions from a specific time period.
# Debug agent selection
Source: https://docs.55-tech.com/api-reference/debug/debug-agent-selection
/zh/scraping-api/openapi.json get /debug/pick
Preview which agent would be selected for a given URL without making the actual request.
# Fetch a URL (DELETE)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-delete
/zh/scraping-api/openapi.json delete /fetch
Send a DELETE request to the target URL through the agent network.
# Fetch a URL (GET)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-get
/zh/scraping-api/openapi.json get /fetch
Send a GET request to the target URL through the agent network. Pass the target URL via the X-Target-URL header.
# Fetch a URL (PATCH)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-patch
/zh/scraping-api/openapi.json patch /fetch
Send a PATCH request to the target URL through the agent network.
# Fetch a URL (POST)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-post
/zh/scraping-api/openapi.json post /fetch
Send a request to the target URL through the agent network. Pass the target URL via the X-Target-URL header. Optionally include a JSON body with method, headers, body, timeout, and allow_redirects fields.
# Fetch a URL (PUT)
Source: https://docs.55-tech.com/api-reference/fetch/fetch-a-url-put
/zh/scraping-api/openapi.json put /fetch
Send a PUT request to the target URL through the agent network.
# Agents by country
Source: https://docs.55-tech.com/api-reference/network/agents-by-country
/zh/scraping-api/openapi.json get /network/geo
Agents grouped by country with slugs listed for each.
# Domain health
Source: https://docs.55-tech.com/api-reference/network/domain-health
/zh/scraping-api/openapi.json get /network/health/{domain}
Per-agent health state for a specific domain. Shows which agents are healthy, soft-blocked, or hard-blocked.
# List all agents
Source: https://docs.55-tech.com/api-reference/network/list-all-agents
/zh/scraping-api/openapi.json get /network/agents
Returns the full agent registry with slug, name, and country for each agent.
# Network status
Source: https://docs.55-tech.com/api-reference/network/network-status
/zh/scraping-api/openapi.json get /network/status
Summary of total nodes and nodes per country.
# Health check
Source: https://docs.55-tech.com/api-reference/system/health-check
/zh/scraping-api/openapi.json get /healthz
Liveness probe. Returns {"ok": true}.
# Usage metrics
Source: https://docs.55-tech.com/api-reference/usage/usage-metrics
/zh/scraping-api/openapi.json get /usage
Per-key usage metrics: request counts, success/fail rates, bytes transferred, protocol breakdown, and top domains.
# Real-time Subscription (WebSocket)
Source: https://docs.55-tech.com/api-reference/websocket/real-time-subscription-websocket
/zh/mm-api/openapi.json get /ws/subscribe
## Real-time Data Subscription (WebSocket)
**URL:** `wss://mmapi.55-tech.com/ws/subscribe`
**Connection Limits:** Maximum 5 concurrent connections per API key.
### Protocol
1. **Connect** to `/ws/subscribe`
2. **Subscribe** (within 30 seconds):
```json
{
"type": "subscribe",
"api_key": "your-client-api-key-uuid",
"channels": ["orders", "bets", "accounts", "scores", "emergency"]
}
```
3. **Confirmation**:
```json
{"type": "subscribed", "subscription_id": 1, "channels": ["orders", "bets"]}
```
4. **Receive broadcasts**:
```json
{
"type": "broadcast",
"channel": "orders",
"event": "UPDATE",
"payload": {"id": 123, "orderStatus": "FILLED"},
"old": {"orderStatus": "PLACED"}
}
```
5. **Keep-alive**: Send `{"type": "ping"}` → Receive `{"type": "pong"}`
6. **Heartbeat**: Server sends `{"type": "heartbeat"}` every 60s
7. **Unsubscribe**: Send `{"type": "unsubscribe"}`
### Channels
| Channel | Description | Filtered |
|---------|-------------|----------|
| `orders` | Order placement, updates, fills | By client |
| `bets` | Hedge bet updates | By client |
| `accounts` | Balance changes | By client |
| `scores` | Live score updates | Global |
| `emergency` | Emergency mode status | Global |
### Error Codes
- `api_key required` - Missing api_key
- `Invalid api_key format` - Must be UUID
- `Invalid API key` - Not found or inactive
- `Connection limit exceeded` - Max 5 per API key