# Blocky API Documentation > Complete API documentation for Blocky Exchange - a cryptocurrency trading platform. > Base URL: https://blocky.com.br/api/v1 > WebSocket: wss://blocky.com.br/api/v1/ws/ > Python Library: pip install blockypy[all] --- Welcome to the Blocky API! It's an HTTP RESTful API that allows for querying market data and order management. We also deliver WebSocket streams for public data, and a Python library to make it all more streamlined. If anything doesn't work as expected, please feel free to send us an email at **support@blocky.com.br**. ## Base URL ``` https://blocky.com.br/api/v1 ``` For BlockyCRAFT (Minecraft server economy): ``` https://craft.blocky.com.br/api/v1 ``` ## Python Library Install the library: ```bash pip install blockypy[all] ``` ### Quick Example ```python from blocky import Blocky client = Blocky( api_key="your_key", endpoint="https://craft.blocky.com.br/api/v1" ) # Public endpoints (no API key needed) markets = client.get_markets() ticker = client.get_ticker("btc_xbrl") orderbook = client.get_orderbook("btc_xbrl", depth=20) # Private endpoints wallets = client.get_wallets() order = client.create_order( type_="limit", market="btc_xbrl", side="buy", price="50.0", quantity="1.0" ) ``` ## WebSocket Streaming ```python from blocky import BlockyWebSocket import asyncio async def on_trade(data): side = "BUY" if data.get("is_buying_tx") else "SELL" print(f"{side}: {data['quantity']} @ {data['price']}") async def main(): ws = BlockyWebSocket(endpoint="wss://craft.blocky.com.br/api/v1/ws/") await ws.connect() await ws.subscribe_transactions("btc_xbrl", callback=on_trade) await ws.run_forever() asyncio.run(main()) ``` ## Timestamps All timestamps are in **nanoseconds UTC Unix**, as strings. ``` 1 second = 1,000,000,000 nanoseconds ``` ## Generating an API Key 1. **Log in to the Dashboard:** Navigate to Dashboard > Security Center > API Keys. 2. **Create a New API Key:** Click on the option to create a new API key. 3. **Important:** - The API key will be shown **ONLY ONCE** during creation. - **Store your API key securely.** Do **NOT** expose it in public folders, filesystems, unencrypted, or unprotected systems. ## Using Your API Key When making HTTP requests, include this header: - **`x-api-key`**: Your API key. This ensures that your requests are authenticated and authorized. ### cURL Example ```bash curl -X GET \ -H "x-api-key: YOUR_API_KEY" \ https://blocky.com.br/api/v1/wallets ``` ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Client automatically includes x-api-key header wallets = client.get_wallets() ``` ### Python (requests) ```python import requests headers = {"x-api-key": "YOUR_API_KEY"} response = requests.get( "https://blocky.com.br/api/v1/wallets", headers=headers ) data = response.json() ``` ## Successful Response (2xx) All responses are in JSON format. A typical successful response includes: - **`success`**: `true` indicating the request was successful. - Additional data fields specific to each endpoint. Example: ```jsonc { "success": true // ... endpoint-specific data fields } ``` ## Error Response (4xx) When an error occurs, the response will have `success` set to `false`: - **`error_code`**: An unsigned integer representing the error. - **`error_message`**: A description of what went wrong. Example: ```jsonc { "success": false, "error_code": 1105, "error_message": "Not found: Market 'invalid' was not found." } ``` For a detailed list of error codes, please refer to the [Error Codes](./3-errors-codes) page. ## Timestamps All time-related fields (such as `created_at` or `finalized_at`) are provided in **nanoseconds UTC Unix**, as strings. | Conversion | Value | | ---------------- | ----------------------- | | 1 second | 1,000,000,000 ns | | 1 millisecond | 1,000,000 ns | | 1 microsecond | 1,000 ns | ### Converting Nanoseconds in Python ```python import time # Current time in nanoseconds now_ns = time.time_ns() # Convert nanoseconds to seconds timestamp_ns = 1701900000000000000 timestamp_seconds = timestamp_ns / 1_000_000_000 # Convert to datetime from datetime import datetime dt = datetime.utcfromtimestamp(timestamp_seconds) print(dt) # 2023-12-07 00:00:00 ``` If you are constantly getting an error, please get in contact ### Example of an error response: ```jsonc { "success": false, "error_code": 1000, "error_message": "JSON parsing error: The provided input is not valid JSON.", "error_source": "core" } ``` ### Input Validation Errors (1000–1099) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | --------------------- | | **1000** | "JSON parsing error: The provided input is not valid JSON." | The input could not be parsed as JSON, indicating malformed or invalid JSON data. | 400 Bad Request | | **1001** | "Missing key: '[]' is not present in the input." | A required key (e.g., 'uuid', 'instrument') is missing from the input data. | 400 Bad Request | | **1002** | "Type error: '[]' must be a '[]', but found '[]' instead." | The data type of the provided value does not match the expected type. | 400 Bad Request | | **1003** | "Format error: '[]' does not match the expected format." | The value does not adhere to the expected format (e.g., invalid market string, decimal format, invalid character, etc). | 400 Bad Request | | **1004** | "Invalid instrument: '[]' is fiat, but crypto is required." | The specified instrument is fiat (e.g., 'brl'), but a cryptocurrency is required. | 400 Bad Request | | **1005** | "Sequence error: You must set the 'account_type' key first." | The operation requires the 'account_type' to be set before proceeding. | 400 Bad Request | | **1006** | "Range error: Value '[]' for '[]' must be between '[]' and '[]'. Provided value: '[]'." | The provided value is outside the acceptable range (e.g., for integers or decimals). | 400 Bad Request | | **1007** | "Invalid value: '[]' is not an acceptable value for '[]'. Valid choices are: []." | The value provided is not among the acceptable choices. | 400 Bad Request | | **1008** | "Missing keys: [] are not present in the input." | Multiple required keys are missing from the input data. | 400 Bad Request | | **1009** | "Empty array: '[]' has an empty array and no default value is provided." | The given key is present, it is an array, but it's empty. | 400 Bad Request | | **1010** | "Empty string: '[]' is set and is of type string, but has an empty value." | An empty string ("") triggers this error. | 400 Bad Request | | **1011** | "Type error: Element at index [] in '[]' must be a '[]', but found '[]' instead." | Occurs when not all elements within an array are of the expected type. | 400 Bad Request | ### Resource Not Found Errors (1100–1199) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ------------------- | | **1100** | "Not found: No user exists with UUID '[]' in the system." | Specific to user not found by UUID. | 404 Not Found | | **1101** | "Not found: No user exists with user ID '[]' in the system." | Specific to user not found by ID. | 404 Not Found | | **1102** | "Not Open: Cannot cancel order ID '[]', it's already closed in the 'completed|cancelled' state." | For orders that are already cancelled. | 404 Not Found | | **1103** | "Not found: Order with ID '[]' was not found in the main system." | Specific to orders not found in the main system. | 404 Not Found | | **1104** | "Not found: Instrument with symbol '[]' was not found in the system." | For instruments not found by symbol. | 404 Not Found | | **1105** | "Not found: Market '[]' was not found in the system." | For markets not found by name. | 404 Not Found | | **1106** | "Not found: Market with ID '[]' was not found in the system." | For markets not found by ID. | 404 Not Found | | **1107** | "Not found: No user exists with referrer ID '[]'." | Specific to referrer ID not found. | 404 Not Found | | **1108** | "Not found: No user exists with referrer UUID '[]'." | Specific to referrer UUID not found. | 404 Not Found | | **1109** | "Not found: No user exists with default referrer ID '[]'." | Specific to default referrer ID not found. | 404 Not Found | | **1110** | "Not found: The requested file is not available yet. Please try again later." | The requested file (e.g., transaction file) is not yet available, possibly still being generated. | 404 Not Found | | **1111** | "Not found: The requested API Key was not found in the system." | The requested API Key was not found in the system. | 404 Not Found | | **1112** | "Not found: No deposit exists with ID '[]' in the system." | The requested deposit couldn't be found. | 404 Not Found | ### Conflict Errors (1200–1299) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | ----------------------------------------------- | -------------------------------------------------------------------------- | ------------------- | | **1200** | "Conflict: The '[]' '[]' has already been set." | Parameterized for entity conflicts (e.g., 'user', 'instrument', 'market'). | 409 Conflict | ### Authentication/Authorization Errors (1300–1399) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ------------------- | | **1300** | "Access denied: This endpoint is not accessible via API calls." | The endpoint cannot be accessed using API calls. | 403 Forbidden | | **1301** | "Identification required: Please complete account verification in Dashboard > Verification." | The account lacks necessary identification; verification is required. It may also trigger when the User Agent is incompatible. | 401 Unauthorized | | **1302** | "Unauthorized: Please sign in again." | The user is unauthorized; typically due to session issues. | 401 Unauthorized | | **1303** | "Missing header: 'x-api-key-secret' is required." | The 'x-api-key-secret' header is missing from the request. | 401 Unauthorized | | **1304** | "Missing header: 'X-API-KEY' is required." | The 'X-API-KEY' header is missing from the request. | 401 Unauthorized | | **1305** | "Invalid credentials: The provided API Key or Secret is incorrect." | The provided API Key or Secret is invalid. | 401 Unauthorized | | **1306** | "Missing cookie: 'session-x' cookie is required. Please sign in." | The 'session-x' cookie is missing; the user must sign in. | 401 Unauthorized | | **1307** | "Missing headers: 'x-signature' and 'x-timestamp' are required." | Both 'x-signature' and 'x-timestamp' headers are missing. | 400 Bad Request | | **1308** | "Missing header: 'x-signature' is required." | The 'x-signature' header is missing from the request. | 400 Bad Request | | **1309** | "Missing header: 'x-timestamp' is required." | The 'x-timestamp' header is missing from the request. | 400 Bad Request | | **1310** | "Invalid signature or timestamp." | The provided signature or timestamp is invalid. | 400 Bad Request | | **1311** | "Access denied: You are not authorized to access this transaction." | The user is not authorized to access the specified transaction. | 403 Forbidden | | **1312** | "Access denied: The provided API Key cannot be accessed via your IP address." | The request was done by an IP address that's not in the permissions ip addresses. | 403 Forbidden | ### Resource Limitation Errors (1400–1499) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------- | --------------------------- | | **1400** | "Insufficient funds: Unable to lock '[]' [] for withdrawal from sub-wallet ID '[]' for user UUID '[]'." | For withdrawal failures due to insufficient funds. | 422 Unprocessable Entity | | **1401** | "Limit exceeded: Your requested range ([] ms to [] ms) with a timeframe of [] ms produces [] candles, exceeding the maximum allowed 1440 by []. Adjust the start, end, or timeframe accordingly." | For exceeding OHLCV candle limits. | 422 Unprocessable Entity | ### Database Operation Errors (2000–2099) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | ------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------- | --------------------------- | | **2000** | "Database error: Failed to create user with UUID '[]', referrer UUID '[]', referrer ID '[]', fee '[]'." | A database error occurred while attempting to create a new user. | 500 Internal Server Error | | **2001** | "Database error: Failed to create instrument '[]'." | A database error occurred while attempting to create a new instrument. | 500 Internal Server Error | | **2002** | "Database error: Failed to create market '[]'." | A database error occurred while attempting to create a new market. | 500 Internal Server Error | | **2003** | "Database error: Failed to create deposit of '[]' [] on sub-wallet ID '[]' for user UUID '[]'." | A database error occurred while attempting to create a deposit. | 500 Internal Server Error | ### Trading Operation Errors (3000–3099) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- | --------------------------- | | **3000** | "Access error: User with UUID '[]' does not own order ID '[]'." | The user is not the owner of the specified order. | 403 Forbidden | | **3001** | "Funds error: Insufficient balance for transfer of '[]' [] from sub-wallet ID '[]' for user UUID '[]'. Current: '[]' [], post-transfer: '[]' []." | Insufficient funds for the transfer request. | 422 Unprocessable Entity | | **3002** | "Order error: Total '[]' [] is below minimum quote volume '[]' [] by '[]' []." | The order total is below the minimum quote volume required. | 422 Unprocessable Entity | | **3003** | "Funds error: Cannot withdraw '[]' [] from sub-wallet ID '[]' for a '[] []' order of '[]'." | Insufficient funds to withdraw for the order. | 422 Unprocessable Entity | | **3004** | "Liquidity error: Failed to issue '[] []' order on '[]' market due to insufficient '[]' liquidity." | There is insufficient liquidity in the market to issue the order. | 422 Unprocessable Entity | ### Security-Specific Errors (5000–5099) | Error Code | Rewritten Error Message | Notes | HTTP Status Code | | ---------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------------------------ | | **5000** | "OTP Code is not correct, please try again." | The provided OTP code is incorrect. | 400 Bad Request | | **5001** | "OTP Token is not valid." | The provided OTP token is invalid. | 400 Bad Request | | **5002** | "OTP creation limit: You have valid OTP codes or have reached the request limit. Please wait before requesting again." | OTP creation is limited; either existing OTP codes are still valid or the request limit has been reached. | 429 Too Many Requests | | **5003** | "OTP creation error: We couldn't create the OTP code, it's on our side, we are going to investigate what happened." | We couldn't sent the OTP code, probably because of an email problem. | 500 Internal Server Error | ## Base URL ``` https://blocky.com.br/api/v1 https://craft.blocky.com.br/api/v1 (BlockyCRAFT) ``` ## Authentication Private endpoints (🔒) require the `x-api-key` header. ## Endpoints ### Instruments (Public) | Method | Endpoint | Description | | ------ | ------------------------- | --------------------- | | GET | `/instruments` | Get all instruments | | GET | `/instruments/:symbol` | Get instrument | ### Markets (Public) | Method | Endpoint | Description | | ------ | ------------------------------------- | ------------------- | | GET | `/markets` | Get all markets | | GET | `/markets/:symbol` | Get market | | GET | `/markets/:symbol/ticker` | Get 24h ticker | | GET | `/markets/:symbol/transactions` | Get recent trades | | GET | `/markets/:symbol/orderbook` | Get orderbook | | GET | `/markets/:symbol/ohlcv` | Get candlesticks | ### Wallets (Private) | Method | Endpoint | Description | | ------ | ----------------------- | ------------------- | | GET | `/wallets` | Get all balances | | GET | `/wallets/:instrument` | Get balance | ### Orders (Private) | Method | Endpoint | Description | | ------ | --------------------- | --------------------- | | POST | `/orders` | Create order | | GET | `/orders` | Get orders | | GET | `/orders/:id` | Get order | | DELETE | `/orders/:id` | Cancel order | | DELETE | `/orders` | Cancel orders | ### Trades (Private) | Method | Endpoint | Description | | ------ | ----------- | ------------------- | | GET | `/trades` | Get trade history | ### Transfers (Private) | Method | Endpoint | Description | | ------ | ------------ | ------------------- | | POST | `/transfers` | Create transfer | | GET | `/transfers` | Get transfers | ### Deposits & Withdrawals (Private) | Method | Endpoint | Description | | ------ | ------------------------------ | ------------------- | | GET | `/deposits` | Get deposits | | GET | `/withdrawals` | Get withdrawals | | GET | `/deposits-and-withdrawals` | Get combined | ## Get Instruments ### GET `/api/v1/instruments` **Description**: Retrieves a list of all available instruments (assets). **Authentication**: Public 🔓 **Query Parameters**: None ### Python (blockypy) ```python from blocky import Blocky client = Blocky() instruments = client.get_instruments() for inst in instruments["instruments"]: print(f"{inst['instrument']}: {inst['name']}") ``` ### Response **Success (200 OK)**: `/api/v1/instruments` ```json { "success": true, "instruments": [ { "id": 1, "instrument": "xbrl", "name": "X Brazilian Real" }, { "id": 2, "instrument": "xno", "name": "Nano" }, { "id": 3, "instrument": "btc", "name": "Bitcoin" } ] } ``` ### Notes - Instruments are the base assets that can be traded on markets. ## Get Instrument ### GET `/api/v1/instruments/:instrument` **Description**: Retrieves information about a specific instrument. **Authentication**: Public 🔓 **Path Parameters**: | Name | Type | Description | Example | | ---------- | ------ | ------------------------------------ | ------- | | instrument | string | Instrument symbol, case insensitive. | `btc` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky() instrument = client.get_instrument("btc") print(f"{instrument['instrument']}: {instrument['name']}") ``` ### Response **Success (200 OK)**: `/api/v1/instruments/irbtcon` ```json { "success": true, "id": 1, "instrument": "btc", "name": "Bitcoin" } ``` ### Error Examples `404 Not Found` – When the instrument doesn't exist: ```json { "success": false, "error_code": 1104, "error_message": "Not found: Instrument 'invalid' was not found." } ``` ## Get Markets ### GET `/api/v1/markets` **Description**: Retrieves data about all available markets. **Authentication**: Public 🔓 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ----------- | ------ | -------- | ------- | ---------------------------------------------- | ------- | | get_tickers | string | No | `false` | Retrieve 24h tickers. `true` or `1` to enable. | `true` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky() # Without tickers markets = client.get_markets() # With tickers markets = client.get_markets(get_tickers=True) ``` ### Response **Success (200 OK)**: `/api/v1/markets` ```jsonc { "success": true, "markets": [ { "market": "btc_usdt", // The market symbol "market_id": 1, // Unique market ID "base_instrument": "btc", // The base instrument (asset being bought/sold) "base_instrument_id": 2, // ID of the base instrument "base_precision": 8, // Maximum decimal precision for quantity "quote_instrument": "usdt", // The quote instrument (used to price the base) "quote_instrument_id": 1, // ID of the quote instrument "quote_precision": 8, // Maximum decimal precision for price "minimum_base_volume": "0.00100000", // Minimum quantity per order "minimum_quote_volume": "0.01000000", // Minimum total (price * quantity) per order "tick_size": "0.00100000", // Minimum price increment "is_latest_tx_buy": true // Whether the latest trade was a buy } ] } ``` ### Response with `get_tickers=true` **Success (200 OK)**: `/api/v1/markets?get_tickers=true` ```jsonc { "success": true, "markets": [ { "market": "btc_usdt", "market_id": 1, "base_instrument": "btc", "base_instrument_id": 2, "base_precision": 8, "quote_instrument": "usdt", "quote_instrument_id": 1, "quote_precision": 8, "minimum_base_volume": "0.00100000", "minimum_quote_volume": "0.01000000", "tick_size": "0.00100000", "is_latest_tx_buy": false, "ticker": { "open": "80142.50000000", // Price 24 hours ago "high": "80853.25000000", // Highest price in 24h "low": "79999.00000000", // Lowest price in 24h "close": "80243.25000000", // Latest trade price "base_volume": "12.50000000", // Base volume traded in 24h "quote_volume": "62500.00000000", // Quote volume traded in 24h "change": "101.00000000", // Price change (close - open) "change_percentage": "0.12571357" // Percentage change (percentage differente between close - open) } } ] } ``` ### Notes - Use the `get_tickers` query parameter if you require 24-hour ticker data. ## Get Market ### GET `/api/v1/markets/:market_symbol` **Description**: Retrieves information about a specific market. **Authentication**: Public 🔓 **Path Parameters**: | Name | Type | Description | Example | | ------------- | ------ | -------------------------------- | ----------- | | market_symbol | string | Market symbol, case insensitive. | `btc_xbrl` | **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ----------- | ------ | -------- | ------- | ---------------------------------------------- | ------- | | get_tickers | string | No | `false` | Retrieve 24h tickers. `true` or `1` to enable. | `true` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky() # Without tickers market = client.get_market("btc_xbrl") # With tickers market = client.get_market("btc_xbrl", get_tickers=True) ``` ### Response **Success (200 OK)**: `/api/v1/markets/btc_xbrl` ```jsonc { "success": true, "market": "btc_xbrl", // The market symbol "market_id": 1, // The market ID "tick_size": "0.00100000", // Minimum price increment "base_instrument": "btc", // The base instrument "base_instrument_id": 2, // The ID of the base instrument "base_precision": 8, // Maximum precision for quantity "minimum_base_volume": "0.00100000", // Minimum quantity per order "quote_instrument": "xbrl", // The quote instrument "quote_instrument_id": 1, // The ID of the quote instrument "quote_precision": 8, // Maximum precision for price "minimum_quote_volume": "0.01000000", // Minimum total per order "is_latest_tx_buy": false // Whether the latest trade was a buy } ``` ### Response with `get_tickers=true` **Success (200 OK)**: `/api/v1/markets/btc_xbrl?get_tickers=true` ```jsonc { "success": true, "market": "btc_xbrl", "market_id": 1, "tick_size": "0.00100000", "base_instrument": "btc", "base_instrument_id": 2, "base_precision": 8, "minimum_base_volume": "0.00100000", "quote_instrument": "xbrl", "quote_instrument_id": 1, "quote_precision": 8, "minimum_quote_volume": "0.01000000", "is_latest_tx_buy": false, "ticker": { "open": "48.50000000", // Price 24 hours ago "high": "52.00000000", // Highest price in 24h "low": "47.00000000", // Lowest price in 24h "close": "50.25000000", // Latest trade price "base_volume": "1250.50000000", // Base volume traded in 24h "quote_volume": "62500.00000000", // Quote volume traded in 24h "change": "1.75000000", // Price change (close - open) "change_percentage": "3.60824742" // Percentage change } } ``` ### Error Examples `404 Not Found` – When the market doesn't exist: ```jsonc { "success": false, "error_code": 1105, "error_message": "Not found: Market 'invalid' was not found." } ``` ### Notes - The **base_precision** and **quote_precision** fields are critical when specifying `quantity` and `price` in orders. ## Get Wallets ### GET `/api/v1/wallets` **Description**: Returns available balances for all instruments. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | -------------- | ------- | -------- | ------- | ----------------------------------------------------------- | ------- | | sub_wallet_id | integer | No | `0` | Sub-wallet ID to query (range: 0-16384) | `1` | | get_frozen | string | No | `false` | Include frozen balance for the queried sub-wallet. `true`/`1`. | `true` | | get_all_frozen | string | No | `false` | Include frozen balances across all sub-wallets. `true`/`1`. | `true` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get balances for default wallet (0) wallets = client.get_wallets() # Get balances for sub-wallet 1 wallets = client.get_wallets(sub_wallet_id=1) # Include frozen amounts wallets = client.get_wallets(get_frozen=True) ``` ### Response **Success (200 OK)**: `/api/v1/wallets` ```jsonc { "success": true, "wallets": [ { "instrument": "xbrl", // Instrument symbol "instrument_id": 1, // Instrument ID "balance": "1000.00000000" // Available balance }, { "instrument": "btc", "instrument_id": 2, "balance": "50.00000000" } ] } ``` ### Response with `get_frozen=true` **Success (200 OK)**: `/api/v1/wallets?get_frozen=true` ```jsonc { "success": true, "wallets": [ { "instrument": "xbrl", "instrument_id": 1, "balance": "1000.00000000", "frozen": "250.00000000" // Amount locked in open orders }, { "instrument": "btc", "instrument_id": 2, "balance": "50.00000000", "frozen": "5.00000000" } ] } ``` ### Notes - The `frozen` field represents funds locked in open orders. ## Sub-Wallets Each user can have up to **16,384 sub-wallets** (IDs 0-16383) per instrument. - **Sub-wallet 0** is the **main wallet** - this is what you see in the site UI - All **deposits** go into sub-wallet 0 - All **withdrawals** come from sub-wallet 0 - Use sub-wallets 1-16383 to segregate funds for different trading strategies To move funds between sub-wallets, use the [Create Transfer](./27-create-transfer) endpoint. ## Get Wallet ### GET `/api/v1/wallets/:instrument` **Description**: Retrieves wallet balance for a specific instrument. **Authentication**: Private 🔒 **Path Parameters**: | Name | Type | Description | Example | | ---------- | ------ | ------------------------------------ | ------- | | instrument | string | Instrument symbol, case insensitive. | `iron` | **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ------------- | ------- | -------- | ------- | ----------------------------------- | ------- | | sub_wallet_id | integer | No | `0` | Sub-wallet ID to query | `1` | | get_frozen | string | No | `false` | Include frozen balance. `true`/`1`. | `true` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get iron balance from default wallet wallet = client.get_wallet("xbrl") # Get iron balance from sub-wallet 1 wallet = client.get_wallet("xbrl", sub_wallet_id=1) ``` ### Response **Success (200 OK)**: `/api/v1/wallets/iron` ```jsonc { "success": true, "instrument": "xbrl", // Instrument symbol "instrument_id": 1, // Instrument ID "balance": "1000.00000000" // Available balance } ``` ### Response with `get_frozen=true` **Success (200 OK)**: `/api/v1/wallets/iron?get_frozen=true` ```jsonc { "success": true, "instrument": "xbrl", "instrument_id": 1, "balance": "1000.00000000", "frozen": "250.00000000" // Amount locked in open orders } ``` ### Error Examples `404 Not Found` – When the instrument doesn't exist: ```jsonc { "success": false, "error_code": 1104, "error_message": "Not found: Instrument 'invalid' was not found." } ``` ### Notes - Balances are returned with 8 decimal places precision. - The `frozen` field represents funds locked in open orders. ## Sub-Wallets Each user can have up to **16,384 sub-wallets** (IDs 0-16383) per instrument. - **Sub-wallet 0** is the **main wallet** - this is what you see in the site UI - All **deposits** go into sub-wallet 0 - All **withdrawals** come from sub-wallet 0 - Use sub-wallets 1-16383 to segregate funds for different trading strategies To move funds between sub-wallets, use the [Create Transfer](./27-create-transfer) endpoint. ## Get Ticker ### GET `/api/v1/markets/:market_symbol/ticker` **Description**: Retrieves 24-hour ticker data for a specific market. **Authentication**: Public 🔓 **Path Parameters**: | Name | Type | Description | Example | | ------------- | ------ | -------------------------------- | ----------- | | market_symbol | string | Market symbol, case insensitive. | `btc_xbrl` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky() ticker = client.get_ticker("btc_xbrl") print(f"Last price: {ticker['close']}") print(f"24h change: {ticker['change_percentage']}%") ``` ### Response **Success (200 OK)**: `/api/v1/markets/btc_xbrl/ticker` ```jsonc { "success": true, "market": "btc_xbrl", "open": "48.50000000", // Price 24 hours ago "high": "52.00000000", // Highest price in 24h "low": "47.00000000", // Lowest price in 24h "close": "50.25000000", // Latest trade price "base_volume": "1250.50000000", // Base volume traded in 24h "quote_volume": "62500.00000000", // Quote volume traded in 24h "change": "1.75000000", // Absolute price change "change_percentage": "3.60824742" // Percentage change } ``` ### Error Examples `404 Not Found`: ```jsonc { "success": false, "error_code": 1105, "error_message": "Not found: Market 'invalid' was not found." } ``` ## Get Transactions ### GET `/api/v1/markets/:market_symbol/transactions` **Description**: Retrieves recent public trades for a specific market. **Authentication**: Public 🔓 **Path Parameters**: | Name | Type | Description | Example | | ------------- | ------ | -------------------------------- | ----------- | | market_symbol | string | Market symbol, case insensitive. | `btc_xbrl` | **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ----- | ------- | -------- | ------- | --------------------------------- | ------- | | count | integer | No | `128` | Number of trades to return (max: 1024) | `50` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky() # Get last 128 transactions (default) transactions = client.get_transactions("btc_xbrl") # Get last 50 transactions transactions = client.get_transactions("btc_xbrl", count=50) for tx in transactions["transactions"]: side = "BUY" if tx["is_buying_tx"] else "SELL" print(f"{side}: {tx['quantity']} @ {tx['price']}") ``` ### Response **Success (200 OK)**: `/api/v1/markets/btc_xbrl/transactions` ```jsonc { "success": true, "count": 1, "transactions": [ { "price": "50.25000000", // Execution price "quantity": "2.50000000", // Quantity traded "is_buying_tx": true, // true if taker was buyer "created_at": "1701900000000000000" // Timestamp (nanoseconds UTC) } ] } ``` ### Error Examples `404 Not Found`: ```jsonc { "success": false, "error_code": 1105, "error_message": "Not found: Market 'invalid' was not found." } ``` ### Notes - All timestamps are in **nanoseconds UTC Unix**. ## Get Orderbook ### GET `/api/v1/markets/:market_symbol/orderbook` **Description**: Retrieves the current orderbook for a specific market, showing all bids (buy orders) and asks (sell orders). **Authentication**: Public 🔓 **Path Parameters**: | Name | Type | Description | Example | | ------------- | ------ | -------------------------------- | ----------- | | market_symbol | string | Market symbol, case insensitive. | `btc_xbrl` | **Query Parameters**: | Name | Type | Required | Default | Description | Example | | --------- | ------- | -------- | ------- | ------------------------------------------------- | ------- | | depth | integer | No | `0` | Number of price levels per side (0 = all levels) | `20` | | tick_size | string | No | - | Aggregate orders into price buckets of this size | `1.0` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky() # Get full orderbook orderbook = client.get_orderbook("btc_xbrl") # Get top 20 levels orderbook = client.get_orderbook("btc_xbrl", depth=20) # Aggregated orderbook (1.0 price buckets) orderbook = client.get_orderbook("btc_xbrl", depth=10, tick_size=1.0) ``` ### Response **Success (200 OK)**: `/api/v1/markets/btc_xbrl/orderbook` ```jsonc { "depth": 0, "market": "btc_xbrl", "orderbook": { "asks": { "base_volume": "8.04900000", "count": 5, "price": [ "47.61100000", "47.51700000", "47.42300000", "47.32900000", "47.23500000" ], "quantity": [ "2.52200000", "2.07000000", "1.81000000", "1.09200000", "0.55500000" ], "quote_volume": "382.16945500" }, "bids": { "base_volume": "2.13600000", "count": 5, "price": [ "46.76500000", "46.67100000", "46.57700000", "46.48300000", "46.38900000" ], "quantity": [ "0.12700000", "0.28600000", "0.39600000", "0.59000000", "0.73700000" ], "quote_volume": "99.34521600" }, "spread": "0.47000000", "spread_percentage": "1.00502500" }, "success": true, "tick_size": "0.00000001" } ``` ### Response with `depth=5` **Success (200 OK)**: `/api/v1/markets/btc_xbrl/orderbook?depth=2` Returns only the top 5 price levels on each side. ```jsonc { "depth": 2, "market": "btc_xbrl", "orderbook": { "asks": { "base_volume": "8.04900000", "count": 2, "price": [ "47.32900000", "47.23500000" ], "quantity": [ "1.09200000", "0.55500000" ], "quote_volume": "382.16945500" }, "bids": { "base_volume": "2.13600000", "count": 2, "price": [ "46.76500000", "46.67100000" ], "quantity": [ "0.12700000", "0.28600000" ], "quote_volume": "99.34521600" }, "spread": "0.47000000", "spread_percentage": "1.00502500" }, "success": true, "tick_size": "0.00000001" } ``` ### Response with `tick_size=0.1` **Success (200 OK)**: `/api/v1/markets/btc_xbrl/orderbook?tick_size=0.1` Orders are aggregated into 0.1 price buckets: ```jsonc { "depth": 0, "market": "btc_xbrl", "orderbook": { "asks": { "base_volume": "8.04900000", "count": 3, "price": [ "47.61100000", "47.42300000", "47.23500000" ], "quantity": [ "2.52200000", "3.88000000", "1.64700000" ], "quote_volume": "382.16945500" }, "bids": { "base_volume": "2.13600000", "count": 3, "price": [ "46.76500000", "46.57700000", "46.38900000" ], "quantity": [ "0.41300000", "0.98600000", "0.73700000" ], "quote_volume": "99.34521600" }, "spread": "0.47000000", "spread_percentage": "1.00502500" }, "success": true, "tick_size": "0.10000000" } ``` **Error Examples**: `404 Not Found` – When the market doesn't exist: ```jsonc { "success": false, "error_code": 1105, "error_message": "Not found: Market 'invalid' was not found in the system.", "error_source": "core" } ``` ### Notes - **Bids** are buy orders, sorted by price descending (highest/best price first). - **Asks** are sell orders, sorted by price ascending (lowest/best price first). - Use `depth` to limit the number of price levels returned. - Use `tick_size` to aggregate orders into price buckets for easier visualization. ## Get OHLCV (Candlestick Data) ### GET `/api/v1/markets/:market_symbol/ohlcv` **Description**: Retrieves candlestick (Open, High, Low, Close, Volume) data for a specific market. **Authentication**: Public 🔓 **Path Parameters**: | Name | Type | Description | Example | | ------------- | ------ | -------------------------------- | ----------- | | market_symbol | string | Market symbol, case insensitive. | `btc_xbrl` | **Query Parameters**: | Name | Type | Required | Default | Description | Example | | --------- | -------------- | -------- | ------------------- | ------------------------------- | --------------------- | | timeframe | integer | No | `60000000000` | Candle interval | `3600000000000` | | end | integer | No | now (nanoseconds) | End timestamp in nanoseconds | `1701900000000000000` | | start | integer | No | end - 1440 candles | Start timestamp in nanoseconds | `1701864000000000000` | > **Note:** The default `start` is calculated as `end - (1440 * timeframe)`. For the default 1-minute timeframe, this means `start = end - 86400000000000000` (1440 minutes = 24 hours). **Timeframe Values**: | Value | Description | Nanoseconds | | ----- | ------------ | ------------------- | | `1m` | 1 minute | 60000000000 | | `5m` | 5 minutes | 300000000000 | | `15m` | 15 minutes | 900000000000 | | `1H` | 1 hour | 3600000000000 | | `4H` | 4 hours | 14400000000000 | | `1D` | 1 day | 86400000000000 | ### Python (blockypy) ```python from blocky import Blocky import time client = Blocky() # Get 1-minute candles for the last 24 hours, totaling 1440 candles(default) ohlcv = client.get_ohlcv("btc_xbrl") # Get hourly candles ohlcv = client.get_ohlcv("btc_xbrl", timeframe="1H") # converts into 3600000000000 # Get daily candles ohlcv = client.get_ohlcv("btc_xbrl", timeframe="1D") # converts into 86400000000000 # Get specific time range end = time.time_ns() # now in nanoseconds start = end - (24 * 60 * 60 * 1_000_000_000) # 24 hours ago ohlcv = client.get_ohlcv("btc_xbrl", start=start, end=end, timeframe="1H") # Access data for i in range(len(ohlcv["timestamps"])): print(f"O:{ohlcv['opens'][i]} H:{ohlcv['highs'][i]} L:{ohlcv['lows'][i]} C:{ohlcv['closes'][i]}") ``` ### Response **Success (200 OK)**: `/api/v1/markets/btc_xbrl/ohlcv?timeframe=3600000000000` ```jsonc { "success": true, "market": "btc_xbrl", "timeframe": "3600000000000", // Candle interval in nanoseconds "timestamps": [ "1701864000000000000", // Candle open timestamp (nanoseconds UTC) "1701867600000000000", "1701871200000000000" ], "opens": [ "50.00000000", // Opening price "50.25000000", "50.50000000" ], "highs": [ "50.50000000", // Highest price during candle "50.75000000", "51.00000000" ], "lows": [ "49.75000000", // Lowest price during candle "50.00000000", "50.25000000" ], "closes": [ "50.25000000", // Closing price "50.50000000", "50.75000000" ], "volumes": [ "150.50000000", // Base asset volume "200.00000000", "175.25000000" ] } ``` **Error Examples**: `404 Not Found` – When the market doesn't exist: ```jsonc { "success": false, "error_code": 1105, "error_message": "Not found: Market 'invalid' was not found in the system.", "error_source": "core" } ``` ### Notes - All arrays are aligned by index: `timestamps[0]` corresponds to `opens[0]`, `highs[0]`, etc. - All timestamps are in **nanoseconds UTC Unix**. - The `timeframe` in the response is the candle interval in nanoseconds. - Default returns the last 1440 candles (24 hours for 1-minute candles). ## Get Order by ID ### GET `/api/v1/orders/:order_id` **Description**: Retrieves detailed information for a specific order. **Authentication**: Private 🔒 **Path Parameters**: | Name | Type | Description | Example | | -------- | ------- | ----------------- | ------------- | | order_id | integer | The order ID. | `123456789` | **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ---------- | ------ | -------- | ------- | -------------------------------------- | ------- | | get_trades | string | No | `false` | Include trades for this order. `true`/`1` | `true` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get order details order = client.get_order(123456789) print(f"Status: {order['status']}") print(f"Filled: {order['fulfilled']}/{order['quantity']}") # Include associated trades order = client.get_order(123456789, get_trades=True) for trade in order.get("trades", []): print(f"Trade: {trade['quantity']} @ {trade['price']}") ``` ### Response **Success (200 OK)**: `/api/v1/orders/123456789` ```jsonc { "success": true, "order_id": 123456789, // Unique order ID "market": "btc_xbrl", // Market symbol "side": "buy", // Order side "type": "limit", // Order type "price": "50.00000000", // Price per unit "quantity": "10.00000000", // Total quantity ordered "status": "open", // Order status "fulfilled": "2.50000000", // Quantity executed so far "fulfilled_percentage": "25.00000000", // Percentage filled (0-100) "remaining": "375.00000000", // Quote remaining (buy) or base remaining (sell) "sub_wallet_id": 0, // Sub-wallet ID "maker_fee": "0.15000000", // Maker fee percentage "taker_fee": "0.30000000", // Taker fee percentage "created_at": "1701900000000000000", // Creation timestamp (nanoseconds UTC) "finalized_at": "0" // Finalization timestamp ("0" if open) } ``` ### Response with `get_trades=true` **Success (200 OK)**: `/api/v1/orders/123456789?get_trades=true` ```jsonc { "success": true, "order_id": 123456789, "market": "btc_xbrl", "side": "buy", "type": "limit", "price": "50.00000000", "quantity": "10.00000000", "status": "open", "fulfilled": "2.50000000", "fulfilled_percentage": "25.00000000", "remaining": "375.00000000", "sub_wallet_id": 0, "created_at": "1701900000000000000", "finalized_at": "0", "trades": [ { "id": 987654, // Unique trade ID "price": "50.00000000", // Execution price "quantity": "2.50000000", // Quantity filled in this trade "fee": "0.00375000", // Fee charged "created_at": "1701900100000000000" // Trade timestamp (nanoseconds UTC) } ] } ``` ### Order Status Values | Status | Description | | ---------- | ---------------------------------------- | | open | Order is active in the orderbook | | completed | Order has been fully filled | | cancelled | Order was cancelled (may be partial) | **Error Examples**: `404 Not Found` – When the order doesn't exist: ```jsonc { "success": false, "error_code": 3001, "error_message": "Order not found.", "error_source": "core" } ``` ### Notes - The `fulfilled` field shows how much of the order has been executed. - The `remaining` field shows quote asset remaining for buy orders, or base asset remaining for sell orders. - All timestamps are in **nanoseconds UTC Unix**. ## Get Orders ### GET `/api/v1/orders` **Description**: Retrieves a paginated list of orders with optional filtering. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ---------------- | -------- | -------- | ------- | -------------------------------------------------- | ----------- | | limit | integer | No | `10` | Maximum orders to return | `50` | | cursor | integer | No | - | Pagination cursor from previous response | `123456788` | | start | integer | No | - | Start timestamp in nanoseconds | `1701800000000000000` | | end | integer | No | - | End timestamp in nanoseconds | `1701900000000000000` | | sort_order | string | No | `desc` | Sort order: `asc` or `desc` | `asc` | | get_trades | string | No | `false` | Include trades for each order. `true`/`1` | `true` | | with_trades_only | string | No | `false` | Only return orders that have trades. `true`/`1` | `true` | | types | string[] | No | - | Filter by order types: `limit`, `market` | `limit` | | markets | string[] | No | - | Filter by market symbols | `btc_xbrl` | | sides | string[] | No | - | Filter by sides: `buy`, `sell` | `buy` | | statuses | string[] | No | - | Filter by statuses: `open`, `completed`, `cancelled` | `open` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get recent orders orders = client.get_orders() # Get more orders orders = client.get_orders(limit=50) # Filter by market orders = client.get_orders(markets=["btc_xbrl"]) # Filter by status orders = client.get_orders(statuses=["open"]) # Filter by side orders = client.get_orders(sides=["buy"]) # Get only filled orders orders = client.get_orders(with_trades_only=True) # Include trade details orders = client.get_orders(get_trades=True) # Pagination orders = client.get_orders(limit=50) if orders.get("cursor"): next_page = client.get_orders(limit=50, cursor=orders["cursor"]) ``` ### Response **Success (200 OK)**: `/api/v1/orders` ```jsonc { "success": true, "orders": [ { "order_id": 123456789, // Unique order ID "market": "btc_xbrl", // Market symbol "side": "buy", // Order side "type": "limit", // Order type "price": "50.00000000", // Price per unit "quantity": "10.00000000", // Quantity ordered "status": "open", // Order status "fulfilled": "0.00000000", // Quantity filled "fulfilled_percentage": "0.00000000", // Percentage filled "sub_wallet_id": 0, // Sub-wallet ID "created_at": "1701900000000000000", // Creation timestamp (nanoseconds UTC) "finalized_at": "0" // Finalization timestamp }, { "order_id": 123456788, "market": "btc_xbrl", "side": "sell", "type": "limit", "price": "55.00000000", "quantity": "5.00000000", "status": "completed", "fulfilled": "5.00000000", "fulfilled_percentage": "100.00000000", "sub_wallet_id": 0, "created_at": "1701899000000000000", "finalized_at": "1701899500000000000" } ], "cursor": 123456787 // Use for pagination } ``` ### Pagination Use the `cursor` value from the response to fetch the next page: ```python # First page result = client.get_orders(limit=50) # Next page if result.get("cursor"): next_page = client.get_orders(limit=50, cursor=result["cursor"]) ``` **Error Examples**: This endpoint does not yield error responses for valid requests. ### Notes - Orders are sorted by `created_at` descending by default (most recent first). - Use filters to narrow down results for specific markets, sides, or statuses. - All timestamps are in **nanoseconds UTC Unix**. ## Create Order ### POST `/api/v1/orders` **Description**: This endpoint allows you to create a new order. You can issue either a **limit order** or a **market order**. **Authentication**: Private 🔒 ## Order Types There are two main types of orders: 1. **Limit Order** - **Limit Buy Order**: Specifies the `price` at which you want to buy and the `quantity` of the base asset. The total amount (i.e., `price * quantity`) in the quote asset is locked from your wallet. - **Limit Sell Order**: Specifies the `price` at which you want to sell and the `quantity` of the base asset to sell. The specified `quantity` of the base asset is locked from your wallet. 2. **Market Order** - **Market Buy Order**: Instead of specifying a price, you specify the `total` (in the quote asset) you are willing to spend. The system will use the best available prices until your total is consumed. - **Market Sell Order**: You specify the `quantity` of the base asset to sell. The order will execute against available buy orders in the orderbook. *Note:* You can also include the optional `sub_wallet_id` field to specify which sub-wallet (default is `0`) the funds should be deducted from and received to. Each instrument supports up to 16,384 sub-wallet IDs, which is useful if you want to segregate funds for different strategies. ## Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Limit Buy Order order = client.create_order( type_="limit", market="btc_xbrl", side="buy", price="50.0", quantity="10.0", sub_wallet_id=0 ) # Limit Sell Order order = client.create_order( type_="limit", market="btc_xbrl", side="sell", price="55.0", quantity="5.0" ) # Market Buy Order (spend 500 quote asset) order = client.create_order( type_="market", market="btc_xbrl", side="buy", total="500.0" ) # Market Sell Order (sell 10 base asset) order = client.create_order( type_="market", market="btc_xbrl", side="sell", quantity="10.0" ) ``` ## Request Body Anatomy ### Limit Order Example #### Limit Buy Order This example places a buy limit order to purchase 10 DIAM at a price of 50 IRON per DIAM. The total cost will be `50 * 10 = 500 IRON`. ```jsonc { "market": "btc_xbrl", // The market where the order is placed "side": "buy", // Order side: "buy" or "sell" "type": "limit", // Order type: "limit" indicates a limit order "price": "50.00000000", // Price per unit of the base asset (in quote asset) "quantity": "10.0", // Quantity of the base asset to buy "sub_wallet_id": 0 // (Optional) Sub-wallet ID to use (default is 0) } ``` #### Limit Sell Order This example places a sell limit order to sell 5 DIAM at a price of 55 IRON per DIAM. The order locks 5 DIAM from your wallet. ```jsonc { "market": "btc_xbrl", "side": "sell", "type": "limit", "price": "55.00000000", "quantity": "5.0", "sub_wallet_id": 0 } ``` ### Market Order Example #### Market Buy Order For a market buy order, you specify the total amount (in quote asset) you wish to spend. In this example, 500 IRON will be used to buy DIAM at the best available prices. ```jsonc { "market": "btc_xbrl", "side": "buy", "type": "market", "total": "500.00000000", // Total amount in quote asset to spend "sub_wallet_id": 0 } ``` #### Market Sell Order For a market sell order, you specify the quantity of the base asset to sell. In this example, 10 DIAM will be sold at the best available prices. ```jsonc { "market": "btc_xbrl", "side": "sell", "type": "market", "quantity": "10.0", // Quantity of the base asset to sell "sub_wallet_id": 0 } ``` ## Important Considerations - **Funds Locking**: - **Buy Orders**: For limit and market buy orders, funds equal to the order's total (price * quantity for limit orders, or the specified `total` for market orders) are locked in your wallet. - **Sell Orders**: For limit and market sell orders, the specified quantity of the base asset is locked. - **Order Execution**: - Limit orders can execute immediately if matching orders exist. They might execute fully, partially, or not at all. Partially filled orders will have their `fulfilled` and `fulfilled_percentage` fields updated accordingly. - Market orders execute against the available liquidity in the orderbook. A market buy order will effectively have an "infinite" price (i.e., it will consume orders until the total is reached), while a market sell order will execute at progressively lower prices if needed. - **Minimum Order Volume**: Ensure that for both order types, the total (price * quantity for buy orders) meets or exceeds the minimum quote volume defined for the market. Otherwise, you may encounter error **3002**. - **Sub-Wallet Segregation**: Use the `sub_wallet_id` field to manage funds separately for different strategies. If not provided, it defaults to `0`. ## Possible Errors - **Error 3002 – Minimum Quote Volume Not Met** _Message_: "Order error: Total '[]' [] is below minimum quote volume '[]' [] by '[]' []." _Cause_: The calculated total (price * quantity) for the order is below the market's minimum quote volume requirement. _Solution_: Increase the price or quantity. - **Error 3003 – Insufficient Funds** _Message_: "Funds error: Cannot withdraw '[]' [] from sub-wallet ID '[]' for a '[][]' order of '[]' []." _Cause_: Insufficient funds in the specified sub-wallet to cover the order. _Solution_: Ensure you have enough funds available in your wallet. - **Error 3004 – Insufficient Liquidity** _Message_: "Liquidity error: Failed to issue '[][]' order on '[]' market due to insufficient '[]' liquidity." _Cause_: For market orders, the orderbook does not have enough liquidity to fulfill the order. _Solution_: Consider using a limit order or adjusting your order parameters. ## Sample Successful Response Below is an example response for a successful **buy limit order**: ```jsonc { "success": true, // Indicates the order was successfully placed "order_id": 123456789, // Unique order ID for reference "market": "btc_xbrl", // Market where the order was issued "side": "buy", // Order side ("buy" or "sell") "type": "limit", // Order type ("limit" or "market") "price": "50.00000000", // Price per unit (in quote asset) "quantity": "10.00000000", // Quantity of the base asset "status": "open", // Order status: "open", "completed", or "cancelled" "fulfilled": "0.00000000", // Amount of base asset executed so far "fulfilled_percentage": "0.00000000", // Percentage of the order fulfilled "remaining": "500.00000000", // For buy: remaining quote locked; for sell: remaining base locked "sub_wallet_id": 0, // Sub-wallet ID from which funds were deducted "maker_fee": "0.15000000", // Fee percentage if executed as a maker order "taker_fee": "0.30000000", // Fee percentage if executed as a taker order "created_at": "1701900000000000000", // Timestamp (nanoseconds UTC) when order was created "finalized_at": "0" // Timestamp when finalized; "0" if still open } ``` *Note:* A partially filled order that is later cancelled will have a `status` of `"cancelled"`, and the `fulfilled` and `fulfilled_percentage` fields will reflect the executed portion of the order. ## Cancel Order ### DELETE `/api/v1/orders/:order_id` **Description**: Cancels a specific open order. If the order has been partially filled, only the unfilled portion will be cancelled. **Authentication**: Private 🔒 **Path Parameters**: | Name | Type | Description | Example | | -------- | ------- | ----------------- | ------------- | | order_id | integer | The order ID. | `123456789` | **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ---------- | ------ | -------- | ------- | ---------------------------------------------- | ------- | | get_trades | string | No | `false` | Include trades for the cancelled order. `true`/`1` | `true` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Cancel a specific order result = client.cancel_order(123456789) print(f"Cancelled order: {result['order_id']}") print(f"Filled before cancel: {result['fulfilled']}") # Cancel and get trade history result = client.cancel_order(123456789, get_trades=True) for trade in result.get("trades", []): print(f"Trade: {trade['quantity']} @ {trade['price']}") ``` ### Response **Success (200 OK)**: `/api/v1/orders/123456789` ```jsonc { "success": true, "order_id": 123456789, // Order ID that was cancelled "market": "btc_xbrl", // Market symbol "side": "buy", // Order side "type": "limit", // Order type "price": "50.00000000", // Price per unit "quantity": "10.00000000", // Original quantity "status": "cancelled", // Now "cancelled" "fulfilled": "2.50000000", // Quantity filled before cancellation "fulfilled_percentage": "25.00000000", // Percentage filled "remaining": "0.00000000", // Now 0 (funds released) "sub_wallet_id": 0, // Sub-wallet ID "created_at": "1701900000000000000", // Creation timestamp (nanoseconds UTC) "finalized_at": "1701900500000000000" // Cancellation timestamp (nanoseconds UTC) } ``` ### Response with `get_trades=true` ```jsonc { "success": true, "order_id": 123456789, "status": "cancelled", "fulfilled": "2.50000000", "finalized_at": "1701900500000000000", "trades": [ { "id": 987654, "price": "50.00000000", "quantity": "2.50000000", "fee": "0.00375000", // If buy side, the fee is in base asset; if sell side, the fee is in quote asset "role": "maker", // Either taker or maker "created_at": "1701900100000000000" } ] } ``` **Error Examples**: `404 Not Found` – When the order doesn't exist: ```jsonc { "success": false, "error_code": 3001, "error_message": "Order not found.", "error_source": "core" } ``` ### Notes - Partially filled orders can be cancelled; the `fulfilled` field shows what was filled. - Unfilled funds are immediately returned to your wallet. - All timestamps are in **nanoseconds UTC Unix**. ## Cancel Orders ### DELETE `/api/v1/orders` **Description**: Cancels all open orders, optionally filtered by market or side. This is useful for quickly exiting all positions. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ---------- | -------- | -------- | ------- | -------------------------------------------- | ----------- | | markets | string[] | No | - | Filter by market symbols | `btc_xbrl` | | sides | string[] | No | - | Filter by sides: `buy`, `sell` | `buy` | | get_trades | string | No | `false` | Include trades for each order. `true`/`1` | `true` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Cancel ALL open orders result = client.cancel_orders() print(f"Cancelled {len(result['orders'])} orders") # Cancel orders for a specific market result = client.cancel_orders(markets=["btc_xbrl"]) # Cancel only buy orders result = client.cancel_orders(sides=["buy"]) # Cancel only sell orders result = client.cancel_orders(sides=["sell"]) # Cancel sells on multiple markets result = client.cancel_orders( markets=["btc_xbrl", "xno_xbrl"], sides=["sell"] ) # Get trade history for cancelled orders result = client.cancel_orders(markets=["btc_xbrl"], get_trades=True) ``` ### Response **Success (200 OK)**: `/api/v1/orders` ```jsonc { "orders": [ { "created_at": "1765218389908520000", "finalized_at": "1765218394968971000", "fulfilled": "0.00000000", "fulfilled_percentage": "0.00000000", "maker_fee": "0.00000000", "order_id": 24625, "price": "47.32900000", "quantity": "0.82400000", "remaining": "0.82400000", "side": "sell", "status": "cancelled", "sub_wallet_id": 1, "taker_fee": "0.00000000", "type": "limit" }, { "created_at": "1765218389917510000", "finalized_at": "1765218394969213000", "fulfilled": "0.00000000", "fulfilled_percentage": "0.00000000", "maker_fee": "0.00000000", "order_id": 24630, "price": "47.23500000", "quantity": "0.36600000", "remaining": "0.36600000", "side": "sell", "status": "cancelled", "sub_wallet_id": 1, "taker_fee": "0.00000000", "type": "limit" }, { "created_at": "1765218389917008000", "finalized_at": "1765218394969522000", "fulfilled": "0.00000000", "fulfilled_percentage": "0.00000000", "maker_fee": "0.00000000", "order_id": 24629, "price": "46.48300000", "quantity": "0.42600000", "remaining": "0.42600000", "side": "buy", "status": "cancelled", "sub_wallet_id": 1, "taker_fee": "0.00000000", "type": "limit" } ], "success": true } ``` ### Common Use Cases ```python # Market maker: cancel all orders before shutdown client.cancel_orders() # Rebalance: cancel one side only client.cancel_orders(sides=["buy"]) client.cancel_orders(sides=["sell"]) # Cancel all open orders of a specific market client.cancel_orders(markets=["old_market"]) ``` **Error Examples**: This endpoint does not yield error responses for valid requests. ### Notes - If no filters are provided, ALL open orders across all markets are cancelled. - Partially filled orders can be cancelled; check the `fulfilled` field for each order. - All timestamps are in **nanoseconds UTC Unix**. ## Get Trades ### GET `/api/v1/trades` **Description**: Retrieves a paginated list of trades where your orders were executed as counterparties. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ---------- | -------- | -------- | ------- | ------------------------------------------------ | ----------- | | limit | integer | No | `10` | Maximum trades to return | `50` | | cursor | integer | No | - | Pagination cursor from previous response | `987653` | | start | integer | No | - | Start timestamp in nanoseconds | `1701800000000000000` | | end | integer | No | - | End timestamp in nanoseconds | `1701900000000000000` | | sort_order | string | No | `desc` | Sort order: `asc` or `desc` | `asc` | | types | string[] | No | - | Filter by order types: `limit`, `market` | `limit` | | markets | string[] | No | - | Filter by market symbols | `btc_xbrl` | | sides | string[] | No | - | Filter by sides: `buy`, `sell` | `buy` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get recent trades trades = client.get_trades() # Get more trades trades = client.get_trades(limit=100) # Filter by market trades = client.get_trades(markets=["btc_xbrl"]) # Filter by side trades = client.get_trades(sides=["buy"]) # Calculate total volume total_volume = sum(float(t['quantity']) for t in trades['trades']) ``` ### Response **Success (200 OK)**: `/api/v1/trades` ```jsonc { "success": true, "trades": [ { "created_at": "1765218396787076000", // Trade timestamp (nanoseconds UTC) "fee": "0.00000000", // Fee charged. Base Asset if buy side, Quote asset if sell side "id": 2281, // Unique trade ID "market": "btc_xbrl", // Market symbol "price": "46.99500000", // Execution price "quantity": "0.06400000", // Quantity filled "role": "maker", // Role of the trade "side": "buy", // Order side "total": "3.00768000" // Total in quote asset }, { "created_at": "1765217574058755000", "fee": "0.00000000", "id": 2280, "market": "btc_xbrl", "price": "47.04200000", "quantity": "0.02400000", "role": "maker", "side": "buy", "total": "1.12900800" }, { "created_at": "1765215279821865000", "fee": "0.00000000", "id": 2279, "market": "btc_xbrl", "price": "47.02800000", "quantity": "0.06800000", "role": "maker", "side": "buy", "total": "3.19790400" }, { "created_at": "1765215256424077000", "fee": "0.00000000", "id": 2278, "market": "btc_xbrl", "price": "46.91700000", "quantity": "0.04600000", "role": "maker", "side": "buy", "total": "2.15818200" }, ], "cursor": 987652 // Use for pagination } ``` ### Pagination ```python # First page result = client.get_trades(limit=50) # Next page if result.get("cursor"): next_page = client.get_trades(limit=50, cursor=result["cursor"]) ``` **Error Examples**: This endpoint does not yield error responses for valid requests. ### Notes - Trades are your order executions, not all market transactions. - The `fee` is charged in the asset you received (base for buy, quote for sell). - All timestamps are in **nanoseconds UTC Unix**. ## Get Transfers ### GET `/api/v1/transfers` **Description**: Retrieves a paginated list of transfers between your sub-wallets. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | -------------- | --------- | -------- | ------- | ------------------------------------------------ | ----------- | | limit | integer | No | `10` | Maximum transfers to return | `50` | | cursor | integer | No | - | Pagination cursor from previous response | `12344` | | start | integer | No | - | Start timestamp in nanoseconds | `1701800000000000000` | | end | integer | No | - | End timestamp in nanoseconds | `1701900000000000000` | | sort_order | string | No | `desc` | Sort order: `asc` or `desc` | `asc` | | sub_wallet_ids | integer[] | No | - | Filter by sub-wallet IDs (source or dest) | `1` | | instruments | string[] | No | - | Filter by instrument symbols | `iron` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get recent transfers transfers = client.get_transfers() # Get more transfers transfers = client.get_transfers(limit=50) # Filter by instrument transfers = client.get_transfers(instruments=["xbrl"]) # Filter by sub-wallet transfers = client.get_transfers(sub_wallet_ids=[1]) ``` ### Response **Success (200 OK)**: `/api/v1/transfers` ```jsonc { "success": true, "transfers": [ { "transfer_id": 12345, // Unique transfer ID "instrument": "xbrl", // Instrument transferred "quantity": "100.00000000", // Amount transferred "source_sub_wallet_id": 0, // Source sub-wallet ID "destination_sub_wallet_id": 1, // Destination sub-wallet ID "memo": "Fund trading account", // Optional memo "created_at": "1701900000000000000" // Transfer timestamp (nanoseconds UTC) }, { "transfer_id": 12344, "instrument": "btc", "quantity": "10.00000000", "source_sub_wallet_id": 1, "destination_sub_wallet_id": 0, "memo": "Consolidate profits", "created_at": "1701899000000000000" } ], "cursor": 12343 // Use for pagination } ``` **Error Examples**: This endpoint does not yield error responses for valid requests. ### Notes - Transfers are between your own sub-wallets, not external transfers. - All timestamps are in **nanoseconds UTC Unix**. ## Create Transfer ### POST `/api/v1/transfers` **Description**: Transfers funds between your sub-wallets. This is useful for segregating funds for different trading strategies. **Authentication**: Private 🔒 **Request Body**: | Field | Type | Required | Description | | ------------------------- | ------- | -------- | -------------------------------------- | | instrument | string | Yes | Instrument symbol to transfer | | quantity | string | Yes | Amount to transfer | | source_sub_wallet_id | integer | Yes | Source sub-wallet ID (0-16384) | | destination_sub_wallet_id | integer | Yes | Destination sub-wallet ID (0-16384) | | memo | string | No | Optional note for the transfer | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Transfer funds to a trading sub-wallet result = client.create_transfer( instrument="xbrl", quantity="1000.0", source_sub_wallet_id=0, destination_sub_wallet_id=1 ) print(f"Transfer ID: {result['transfer_id']}") # Transfer with memo result = client.create_transfer( instrument="xbrl", quantity="100.0", source_sub_wallet_id=0, destination_sub_wallet_id=1, memo="Fund trading account" ) # Consolidate profits back to main wallet result = client.create_transfer( instrument="btc", quantity="50.0", source_sub_wallet_id=1, destination_sub_wallet_id=0, memo="Weekly profit consolidation" ) ``` ### Request Body Example ```jsonc { "instrument": "xbrl", // Instrument to transfer "quantity": "100.00000000", // Amount to transfer "source_sub_wallet_id": 0, // Source wallet "destination_sub_wallet_id": 1, // Destination wallet "memo": "Fund trading account" // Optional note } ``` ### Response **Success (200 OK)**: ```jsonc { "success": true, "transfer_id": 12345, // Unique transfer ID "instrument": "xbrl", // Instrument transferred "quantity": "100.00000000", // Amount transferred "source_sub_wallet_id": 0, // Source sub-wallet ID "destination_sub_wallet_id": 1, // Destination sub-wallet ID "memo": "Fund trading account", // Optional memo "created_at": "1701900000000000000" // Transfer timestamp (nanoseconds UTC) } ``` **Error Examples**: `400 Bad Request` – Insufficient balance: ```jsonc { "success": false, "error_code": 4001, "error_message": "Insufficient balance in source sub-wallet.", "error_source": "core" } ``` ### Common Use Cases ```python # Fund a market making sub-wallet client.create_transfer( instrument="xbrl", quantity="1000.0", source_sub_wallet_id=0, destination_sub_wallet_id=1, memo="Market making capital" ) # Consolidate all profits for instrument in ["btc", "xno", "xbrl"]: wallet = client.get_wallet(instrument, sub_wallet_id=1) if float(wallet["balance"]) > 0: client.create_transfer( instrument=instrument, quantity=wallet["balance"], source_sub_wallet_id=1, destination_sub_wallet_id=0, memo="Consolidation" ) ``` ### Notes - Transfers are instant and have no fees. - Sub-wallet IDs range from 0 to 16384. - All timestamps are in **nanoseconds UTC Unix**. ## Get Deposits ### GET `/api/v1/deposits` **Description**: Retrieves a paginated list of your deposits. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ----------- | -------- | -------- | ------- | ------------------------------------------ | ----------- | | limit | integer | No | `15` | Maximum deposits to return | `50` | | cursor | integer | No | - | Pagination cursor from previous response | `5677` | | start | integer | No | - | Start timestamp in nanoseconds | `1701800000000000000` | | end | integer | No | - | End timestamp in nanoseconds | `1701900000000000000` | | sort_order | string | No | `desc` | Sort order: `asc` or `desc` | `asc` | | instruments | string[] | No | - | Filter by instrument symbols | `iron` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get recent deposits deposits = client.get_deposits() # Get more deposits deposits = client.get_deposits(limit=50) # Filter by instrument deposits = client.get_deposits(instruments=["xbrl"]) ``` ### Response **Success (200 OK)**: `/api/v1/deposits` ```jsonc { "success": true, "deposits": [ { "id": 5678, // Unique deposit ID "instrument": "xbrl", // Instrument deposited "quantity": "500.00000000", // Amount deposited "tx_id": "abc123...", // Blockchain transaction hash "created_at": "1701900000000000000", // Deposit creation (nanoseconds UTC) "address_id": "abcd..." // Address ID it came from } ], "cursor": 5677 // Use for pagination } ``` ### Notes - All timestamps are in **nanoseconds UTC Unix**. ## Get Withdrawals ### GET `/api/v1/withdrawals` **Description**: Retrieves a paginated list of your withdrawals. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ----------- | -------- | -------- | ------- | ------------------------------------------ | ----------- | | limit | integer | No | `15` | Maximum withdrawals to return | `50` | | cursor | integer | No | - | Pagination cursor from previous response | `9011` | | start | integer | No | - | Start timestamp in nanoseconds | `1701800000000000000` | | end | integer | No | - | End timestamp in nanoseconds | `1701900000000000000` | | sort_order | string | No | `desc` | Sort order: `asc` or `desc` | `asc` | | instruments | string[] | No | - | Filter by instrument symbols | `iron` | ### Python (blockypy) ```python from blocky import Blocky client = Blocky(api_key="YOUR_API_KEY") # Get recent withdrawals withdrawals = client.get_withdrawals() # Get more withdrawals withdrawals = client.get_withdrawals(limit=50) # Filter by instrument withdrawals = client.get_withdrawals(instruments=["xbrl"]) ``` ### Response **Success (200 OK)**: `/api/v1/withdrawals` ```jsonc { "success": true, "withdrawals": [ { "id": 9012, // Unique withdrawal ID "instrument": "xbrl", // Instrument withdrawn "quantity": "200.00000000", // Amount withdrawn "address_id": "...", // Destination blockchain address "tx_id": "def456...", // Blockchain transaction hash "created_at": "1701900000000000000", // Withdrawal creation (nanoseconds UTC) } ], "cursor": 9011 // Use for pagination } ``` ### Notes - All timestamps are in **nanoseconds UTC Unix**. ## Get Deposits and Withdrawals ### GET `/api/v1/deposits-and-withdrawals` **Description**: Retrieves a combined paginated list of both deposits and withdrawals. **Authentication**: Private 🔒 **Query Parameters**: | Name | Type | Required | Default | Description | Example | | ----------- | -------- | -------- | ------- | ------------------------------------------ | ----------- | | limit | integer | No | `15` | Maximum records to return | `50` | | cursor | integer | No | - | Pagination cursor from previous response | `5677` | | start | integer | No | - | Start timestamp in nanoseconds | `1701800000000000000` | | end | integer | No | - | End timestamp in nanoseconds | `1701900000000000000` | | sort_order | string | No | `desc` | Sort order: `asc` or `desc` | `asc` | | instruments | string[] | No | - | Filter by instrument symbols | `iron` | ### Python ```python import requests headers = {"x-api-key": "YOUR_API_KEY"} response = requests.get( "https://blocky.com.br/api/v1/deposits-and-withdrawals", headers=headers, params={"limit": 50} ) data = response.json() for record in data["records"]: if "deposit_id" in record: print(f"Deposit: {record['quantity']} {record['instrument']}") else: print(f"Withdrawal: {record['quantity']} {record['instrument']}") ``` ### Response **Success (200 OK)**: `/api/v1/deposits-and-withdrawals` ```jsonc { "success": true, "deposits_and_withdrawals": [ { "address_id": "null", "created_at": "1764715067729991680", "instrument": "cobl", "memo": "null", "quantity": "1.00000000", "tx_id": "b393508b5e2748ef0bfa7f8edd98e0a4e24cc0258faf91e0671fce347bc2d036", "type": "withdrawal" }, { "address_id": "", "created_at": "1764693660238985728", "instrument": "xbrl", "memo": "optional memo here", "quantity": "9.00000000", "tx_id": "4766a3f18bf7272f951fb434d7666760_0", "type": "deposit" }, ], "cursor": 5677 // Use for pagination } ``` ### Notes - Records are sorted by `created_at` in the specified order. - All timestamps are in **nanoseconds UTC Unix**. ## WebSocket Connection Overview Our WebSocket API provides real-time data updates for markets. Establish a WebSocket connection to: ``` wss://blocky.com.br/api/v1/ws/ wss://craft.blocky.com.br/api/v1/ws/ (BlockyCRAFT) ``` All messages are in JSON format and must include the following fields: - **action**: The operation to perform. Valid values are `"subscribe"` and `"unsubscribe"`. - **message_id**: An unsigned 64-bit integer to correlate your request with the server's response. - **channel**: The channel you wish to subscribe or unsubscribe to. ## Channel Naming Convention Channels are defined by concatenating a market symbol and a data stream with a colon (`:`). There are two main streams available: - **transactions**: Receives a message for every transaction (trade) that occurs in the specified market. - **orderbook**: Receives a snapshot of the orderbook whenever it changes (new orders, trades, or cancellations). **Examples:** - `"btc_xbrl:transactions"` - `"xno_xbrl:orderbook"` > **Tip:** To unsubscribe from all channels at once, set the channel to `"all"` when using the `"unsubscribe"` action. ## Message Structure for Subscriptions When sending a subscription message, your JSON should look similar to: ```json { "action": "subscribe", "message_id": 123456, "channel": "btc_xbrl:transactions" } ``` The server will reply using the same `message_id` so you can match responses to your requests. ## Python (blockypy) ```python from blocky import BlockyWebSocket import asyncio async def handle_orderbook(data): print(f"Bids: {data['bids']['count']}, Asks: {data['asks']['count']}") async def handle_transaction(data): side = "BUY" if data.get("is_buying_tx") else "SELL" print(f"{side}: {data['quantity']} @ {data['price']}") async def main(): ws = BlockyWebSocket(endpoint="wss://craft.blocky.com.br/api/v1/ws/") await ws.connect() await ws.subscribe_orderbook("btc_xbrl", callback=handle_orderbook) await ws.subscribe_transactions("btc_xbrl", callback=handle_transaction) await ws.run_forever() asyncio.run(main()) ``` For more detailed information on each stream, see the [Transactions Stream](./transactions) and [Orderbook Stream](./orderbook) documentation. ## Transactions Stream Overview The **transactions** stream provides real-time updates each time a transaction (trade) occurs in a market. When you subscribe to a channel like `"btc_xbrl:transactions"`, you will receive a message for every executed trade in that market. ## Sample Subscription Message To subscribe to the transactions stream for a specific market, send: ```json { "action": "subscribe", "message_id": 123456, "channel": "btc_xbrl:transactions" } ``` ## Python (blockypy) ```python from blocky import BlockyWebSocket import asyncio async def handle_transaction(data): side = "BUY" if data.get("is_buying_tx") else "SELL" print(f"{side}: {data['quantity']} @ {data['price']}") async def main(): ws = BlockyWebSocket(endpoint="wss://craft.blocky.com.br/api/v1/ws/") await ws.connect() await ws.subscribe_transactions("btc_xbrl", callback=handle_transaction) await ws.run_forever() asyncio.run(main()) ``` ## Receiving Transaction Messages Once subscribed, you will receive messages similar to the following: ```json { "channel": "btc_xbrl:transactions", "price": "50.50000000", "quantity": "2.50000000", "is_buying_tx": true, "created_at": "1701900000000000000" } ``` ### Field Descriptions | Field | Description | | ------------ | ---------------------------------------------------------- | | channel | Source channel (e.g., `"btc_xbrl:transactions"`) | | price | Execution price of the trade | | quantity | Quantity traded (in the base asset) | | is_buying_tx | `true` if the taker order was buying, `false` if selling | | created_at | Timestamp (nanoseconds UTC) | > **Note:** Multiply `price` and `quantity` to compute the total traded volume in the quote asset. ## Orderbook Stream Overview The **orderbook** stream provides real-time snapshots of a market's orderbook. Every time the orderbook changes due to a new order, trade, or cancellation, a full snapshot is broadcast. ## Sample Subscription Message To subscribe to the orderbook stream for a market, send a message like: ```json { "action": "subscribe", "message_id": 654321, "channel": "btc_xbrl:orderbook" } ``` ## Python (blockypy) ```python from blocky import BlockyWebSocket import asyncio async def handle_orderbook(data): print(f"Spread: {data['spread']} ({data['spread_percentage']}%)") print(f"Bids: {data['bids']['count']}, Asks: {data['asks']['count']}") # Best bid/ask if data['bids']['price']: print(f"Best bid: {data['bids']['price'][0]}") if data['asks']['price']: print(f"Best ask: {data['asks']['price'][-1]}") async def main(): ws = BlockyWebSocket(endpoint="wss://craft.blocky.com.br/api/v1/ws/") await ws.connect() await ws.subscribe_orderbook("btc_xbrl", callback=handle_orderbook) await ws.run_forever() asyncio.run(main()) ``` ## Receiving Orderbook Messages Once subscribed, you will receive messages similar to this: ```json { "channel": "btc_xbrl:orderbook", "spread": "0.50000000", "spread_percentage": "1.00000000", "asks": { "base_volume": "25.50000000", "quote_volume": "1287.50000000", "count": 5, "price": [ "52.00000000", "51.50000000", "51.00000000", "50.75000000", "50.50000000" ], "quantity": [ "5.00000000", "3.50000000", "8.00000000", "4.00000000", "5.00000000" ] }, "bids": { "base_volume": "30.00000000", "quote_volume": "1485.00000000", "count": 4, "price": [ "50.00000000", "49.50000000", "49.00000000", "48.50000000" ], "quantity": [ "10.00000000", "8.00000000", "7.00000000", "5.00000000" ] } } ``` ### Field Descriptions | Field | Description | | ----------------- | -------------------------------------------------------- | | channel | Source channel (e.g., `"btc_xbrl:orderbook"`) | | spread | Difference between best ask and best bid prices | | spread_percentage | Percentage difference between best ask and bid | #### Asks (Sell Orders) | Field | Description | | ------------ | --------------------------------------------- | | base_volume | Total base asset available for sale | | quote_volume | Total value (in quote asset) of all asks | | count | Number of ask orders | | price | Array of ask prices (highest to lowest) | | quantity | Array of quantities for each price level | #### Bids (Buy Orders) | Field | Description | | ------------ | --------------------------------------------- | | base_volume | Total base asset desired for purchase | | quote_volume | Total value (in quote asset) of all bids | | count | Number of bid orders | | price | Array of bid prices (highest to lowest) | | quantity | Array of quantities for each price level | > **Note:** The `price` and `quantity` arrays are parallel. Index `0` represents one order level, index `1` another, etc.