88 | >(({ className, ...props }, ref) => (
89 | [role=checkbox]]:translate-y-[2px]",
93 | className
94 | )}
95 | {...props}
96 | />
97 | ))
98 | TableCell.displayName = "TableCell"
99 |
100 | const TableCaption = React.forwardRef<
101 | HTMLTableCaptionElement,
102 | React.HTMLAttributes
103 | >(({ className, ...props }, ref) => (
104 |
109 | ))
110 | TableCaption.displayName = "TableCaption"
111 |
112 | export {
113 | Table,
114 | TableHeader,
115 | TableBody,
116 | TableFooter,
117 | TableHead,
118 | TableRow,
119 | TableCell,
120 | TableCaption,
121 | }
122 |
--------------------------------------------------------------------------------
/pages/platform/self-trade-prevention.mdx:
--------------------------------------------------------------------------------
1 | # Self Trade Prevention (STP)
2 |
3 | Self Trade Prevention (STP) is a mechanism that prevents traders from executing trades against their own orders. This feature helps maintain market integrity and avoid unintentional self-trading scenarios.
4 |
5 | ## Overview
6 |
7 | When placing [orders](./../glossary.md#orders), you can specify how the system should handle potential self-trades using the `stp` parameter. This parameter determines the behavior when an order would match against another order from the same account.
8 |
9 | ## STP Modes
10 |
11 | The following STP modes are available:
12 |
13 | | Mode | Description |
14 | |------|-------------|
15 | | `no` | Default mode. Self-trades are allowed |
16 | | `cancel_both` | Both the new order and the existing order will be canceled |
17 | | `cancel_new` | The new order will be canceled, and the existing order will remain |
18 | | `cancel_old` | The existing order will be canceled, and the new order will be placed |
19 |
20 | ## Usage
21 |
22 | You can specify the STP mode when placing any type of order:
23 | - [Limit orders](./../glossary.md#limit-order)
24 | - [Market orders](./../glossary.md#market-order)
25 | - [Stop-limit orders](./../glossary.md#stop-limit-order)
26 | - [Stop-market orders](./../glossary.md#stop-market-order)
27 | - [OCO orders](./../glossary.md#oco-orders) (One Cancels Other)
28 | - [Collateral orders](./../glossary.md#collateral)
29 |
30 | ### Example
31 |
32 | ```json
33 | {
34 | "market": "BTC_USDT",
35 | "side": "buy",
36 | "amount": "0.001",
37 | "price": "40000",
38 | "stp": "cancel_both" // STP mode specification
39 | }
40 | ```
41 |
42 | ## Supported Endpoints
43 |
44 | STP is supported on the following API endpoints:
45 |
46 | - [POST `/api/v4/order/new`](../private/http-trade-v4#create-limit-order) (Limit orders)
47 | - [POST `/api/v4/order/market`](../private/http-trade-v4#create-market-order) (Market orders)
48 | - [POST `/api/v4/order/stock_market`](../private/http-trade-v4#create-buy-stock-market-order) (Stock market orders)
49 | - [POST `/api/v4/order/stop_limit`](../private/http-trade-v4#create-stop-limit-order) (Stop-limit orders)
50 | - [POST `/api/v4/order/stop_market`](../private/http-trade-v4#create-stop-market-order) (Stop-market orders)
51 | - [POST `/api/v4/order/collateral/market`](../private/http-trade-v4#collateral-market-order) (Collateral market orders)
52 | - [POST `/api/v4/order/collateral/stop-limit`](../private/http-trade-v4#collateral-stop-limit-order) (Collateral stop-limit orders)
53 | - [POST `/api/v4/order/collateral/trigger-market`](../private/http-trade-v4#collateral-trigger-market-order) (Collateral trigger market orders)
54 | - [POST `/api/v4/order/collateral/oco`](../private/http-trade-v4#create-collateral-oco-order) (Collateral OCO orders)
55 |
56 | ## Best Practices
57 |
58 | 1. **Default Behavior**: If you don't specify an STP mode, the system defaults to `no`, allowing self-trades.
59 | 2. **Risk Management**: For automated trading systems or when running multiple strategies, it's recommended to use `cancel_both` or `cancel_new` to prevent unintentional self-trades.
60 | 3. **Market Making**: When running [market making](./../glossary.md#maker) strategies, consider using `cancel_new` to maintain existing orders' queue position.
61 |
62 | ## Response Handling
63 |
64 | When an order is affected by STP rules, the order response will include the `stp` field indicating the mode that was applied. The order status will reflect the outcome based on the STP mode:
65 | - Orders canceled due to STP will have a "CANCELED" status
66 | - Successfully placed orders will proceed with normal status progression
67 |
--------------------------------------------------------------------------------
/public/sitemap-0.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | https://docs.whitebit.com/ 2025-07-10T15:41:21.175Z weekly 0.8
4 | https://docs.whitebit.com/changelog/ 2025-07-10T15:41:21.176Z weekly 0.8
5 | https://docs.whitebit.com/faq/ 2025-07-10T15:41:21.176Z weekly 0.8
6 | https://docs.whitebit.com/glossary/ 2025-07-10T15:41:21.176Z weekly 0.8
7 | https://docs.whitebit.com/guides/client-order-id/ 2025-07-10T15:41:21.176Z weekly 0.8
8 | https://docs.whitebit.com/guides/fireblocks/ 2025-07-10T15:41:21.176Z weekly 0.8
9 | https://docs.whitebit.com/platform/colocation/ 2025-07-10T15:41:21.176Z weekly 0.8
10 | https://docs.whitebit.com/platform/oauth/overview/ 2025-07-10T15:41:21.176Z weekly 0.8
11 | https://docs.whitebit.com/platform/oauth/usage/ 2025-07-10T15:41:21.176Z weekly 0.8
12 | https://docs.whitebit.com/platform/overview/ 2025-07-10T15:41:21.176Z weekly 0.8
13 | https://docs.whitebit.com/platform/self-trade-prevention/ 2025-07-10T15:41:21.177Z weekly 0.8
14 | https://docs.whitebit.com/platform/webhook/ 2025-07-10T15:41:21.177Z weekly 0.8
15 | https://docs.whitebit.com/private/http-auth/ 2025-07-10T15:41:21.177Z weekly 0.8
16 | https://docs.whitebit.com/private/http-main-v4/ 2025-07-10T15:41:21.177Z weekly 0.8
17 | https://docs.whitebit.com/private/http-trade-v1/ 2025-07-10T15:41:21.177Z weekly 0.8
18 | https://docs.whitebit.com/private/http-trade-v4/ 2025-07-10T15:41:21.177Z weekly 0.8
19 | https://docs.whitebit.com/private/websocket/ 2025-07-10T15:41:21.177Z weekly 0.8
20 | https://docs.whitebit.com/public/http-v1/ 2025-07-10T15:41:21.177Z weekly 0.8
21 | https://docs.whitebit.com/public/http-v2/ 2025-07-10T15:41:21.177Z weekly 0.8
22 | https://docs.whitebit.com/public/http-v4/ 2025-07-10T15:41:21.177Z weekly 0.8
23 | https://docs.whitebit.com/public/websocket/ 2025-07-10T15:41:21.177Z weekly 0.8
24 | https://docs.whitebit.com/sdks/ 2025-07-10T15:41:21.177Z weekly 0.8
25 |
--------------------------------------------------------------------------------
/components/ui/data-table.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import {
4 | ColumnDef,
5 | Row,
6 | flexRender,
7 | getCoreRowModel,
8 | useReactTable,
9 | } from "@tanstack/react-table";
10 |
11 | import { TableCell, TableHead, TableRow } from "@/components/ui/table";
12 | import { HTMLAttributes, forwardRef } from "react";
13 | import { TableVirtuoso } from "react-virtuoso";
14 | import { cn } from "@/lib/utils";
15 |
16 | // Original Table is wrapped with a (see https://ui.shadcn.com/docs/components/table#radix-:r24:-content-manual),
17 | // but here we don't want it, so let's use a new component with only
tag
18 | const TableComponent = forwardRef<
19 | HTMLTableElement,
20 | React.HTMLAttributes
21 | >(({ className, ...props }, ref) => (
22 |
27 | ));
28 | TableComponent.displayName = "TableComponent";
29 |
30 | const TableRowComponent = (rows: Row[]) =>
31 | function getTableRow(props: HTMLAttributes) {
32 | const index = props["data-index"];
33 | const row = rows[index];
34 |
35 | if (!row) return null;
36 |
37 | return (
38 |
43 | {row.getVisibleCells().map((cell) => (
44 |
45 | {flexRender(cell.column.columnDef.cell, cell.getContext())}
46 |
47 | ))}
48 |
49 | );
50 | };
51 |
52 | interface DataTableProps {
53 | columns: ColumnDef[];
54 | data: TData[];
55 | height: string;
56 | className?: string;
57 | }
58 |
59 | export function DataTable({
60 | columns,
61 | data,
62 | height,
63 | className,
64 | }: DataTableProps) {
65 | const table = useReactTable({
66 | data,
67 | columns,
68 | getCoreRowModel: getCoreRowModel(),
69 | });
70 |
71 | const { rows } = table.getRowModel();
72 |
73 | return (
74 |
75 |
83 | table.getHeaderGroups().map((headerGroup) => (
84 | // Change header background color to non-transparent
85 |
86 | {headerGroup.headers.map((header) => {
87 | return (
88 |
95 | {header.isPlaceholder ? null : (
96 |
108 | {flexRender(
109 | header.column.columnDef.header,
110 | header.getContext()
111 | )}
112 |
113 | )}
114 |
115 | );
116 | })}
117 |
118 | ))
119 | }
120 | />
121 |
122 | );
123 | }
124 |
--------------------------------------------------------------------------------
/pages/private/http-auth.mdx:
--------------------------------------------------------------------------------
1 | import { RegionalUrl } from "@/components/regional-url";
2 | import { URL_TYPE_MAIN } from "@/constants";
3 |
4 | # Private HTTP API Authentication
5 |
6 | ## Overview
7 |
8 | This guide explains how to authenticate with WhiteBit's private HTTP API endpoints, which require authentication for security purposes.
9 |
10 | ## Getting Started
11 |
12 | ### Setting Up API Keys
13 |
14 | 1. Navigate to WhiteBIT API Settings
15 | 2. Select the appropriate configuration tab for your API keys
16 | > Different API keys provide access to different API endpoints
17 | 3. Generate a new API key
18 | 4. **Recommended Security Measures:**
19 | - Enable IP restrictions (specify up to 50 trusted IPs)
20 | - Enable Endpoint access restrictions (select only necessary endpoints)
21 |
22 | ## Authentication Requirements
23 |
24 | All authenticated requests must:
25 | 1. Use the `POST` HTTP method
26 | 2. Include specific body data
27 | 3. Contain required headers
28 |
29 | ### Body Data Format
30 |
31 | Your request body must be a JSON object containing:
32 |
33 | | Field | Description | Example |
34 | |-------|-------------|---------|
35 | | `request` | Request path without domain name | `'/api/v4/trade-account/balance'` |
36 | | `nonce` | An incrementing number larger than previous requests | `'1594297865'` |
37 | | `nonceWindow` | Optional boolean to enable time-based nonce validation | `true` |
38 | | Request-specific parameters | Additional parameters required by the endpoint | `"ticker": "BTC"` |
39 |
40 | **Example Request Body:**
41 | ```json
42 | {
43 | "request": "/api/v4/trade-account/balance",
44 | "nonce": 1594297865,
45 | "nonceWindow": true,
46 | "ticker": "BTC"
47 | }
48 | ```
49 |
50 | #### About Nonce Values
51 |
52 | - A good practice is to use the Unix timestamp in milliseconds
53 | - Ensure each nonce is larger than previous requests
54 | - When `nonceWindow` is enabled:
55 | - Provide Unix timestamp in milliseconds as the nonce
56 | - Timestamp must be within ±5 seconds of server time
57 | - Each nonce must be unique to prevent double processing
58 | - Useful for high-frequency trading systems with concurrent requests
59 |
60 | ### Required Headers
61 |
62 | Every authenticated request requires these headers:
63 |
64 | | Header | Value | Description |
65 | |--------|-------|-------------|
66 | | `Content-type` | `application/json` | Specifies JSON format |
67 | | `X-TXC-APIKEY` | `your_api_key` | Your public WhiteBit API key |
68 | | `X-TXC-PAYLOAD` | `base64_encoded_payload` | Base64-encoded request body |
69 | | `X-TXC-SIGNATURE` | `signature` | HMAC-SHA512 signature (hex encoded) |
70 |
71 | The signature is created using: `hex(HMAC_SHA512(payload, key=api_secret))`
72 |
73 | ## Implementation Examples
74 |
75 | We provide the [API Quick Start Helper](https://github.com/whitebit-exchange/api-quickstart) library with examples in multiple languages:
76 |
77 | - Python
78 | - PHP
79 | - NodeJS
80 | - Go
81 | - JavaScript
82 | - Kotlin
83 | - DotNet
84 | - Ruby
85 | - C++
86 | - Rust
87 |
88 | ## Common Errors
89 |
90 | | Error Message | Cause | Solution |
91 | |---------------|-------|----------|
92 | | "Too many requests." | Nonce value is not greater than previous request | Use incrementing nonce values |
93 | | "This action is unauthorized. Enable your key in API settings" | Using disabled API key | Enable key in API settings or check IP restrictions |
94 | | "You don't have permission to use this endpoint." | Endpoint access is restricted | Update endpoint access in API key settings |
95 | | "Invalid payload" | Payload doesn't match decoded value | Ensure proper base64 encoding of request body |
96 | | "Unauthorized request." | Request signed incorrectly | Verify signature creation process |
97 | | "Nonce not provided." | Missing nonce in request body | Include nonce in all requests |
98 | | "Your nonce is more than 5 seconds lesser than the current nonce" | Invalid timestamp when using nonceWindow | Use current timestamp in milliseconds |
99 | | "Invalid nonceWindow." | nonceWindow is not a boolean | Ensure nonceWindow is set to true or false |
100 | | "Request not provided." | Missing request path in body | Include request path in all requests |
101 |
--------------------------------------------------------------------------------
/pages/platform/oauth/overview.mdx:
--------------------------------------------------------------------------------
1 | # OAuth 2.0 on WhiteBIT
2 |
3 | OAuth 2.0 integration with WhiteBIT enables secure, token-based authorization for third-party applications to access the WhiteBIT platform on behalf of users. This implementation follows industry standards while providing specialized features tailored for cryptocurrency trading and account management.
4 |
5 | ## Key Benefits
6 |
7 | - **Enhanced Security**: Users grant access without sharing their WhiteBIT credentials with third-party applications
8 | - **Granular Permissions**: Fine-grained scope controls allow users to limit what third-party applications can access
9 | - **Streamlined Trading Experience**: Enables seamless integration with portfolio trackers, trading bots, and analysis tools
10 | - **Developer-Friendly**: Standardized implementation familiar to developers across the ecosystem
11 |
12 | ## How WhiteBIT OAuth Works
13 |
14 | 1. **Authorization**: Users approve access for a third-party application through WhiteBIT's authorization page
15 | 2. **Token Exchange**: The application exchanges the authorization code for an access token
16 | 3. **API Access**: The application uses the access token to make API calls to WhiteBIT on the user's behalf
17 | 4. **Token Refresh**: When the access token expires, the application can use a refresh token to obtain a new one
18 |
19 | ## Use Cases
20 |
21 | ### Trading Bots & Algorithmic Trading
22 | Enable automated trading strategies without storing WhiteBIT credentials in third-party systems. Trading bots can place, modify, and cancel orders while only having the specific permissions they need.
23 |
24 | ### Portfolio Trackers & Analytics
25 | Allow portfolio management applications to read balance and trade history information without write access to trading functions, providing users with consolidated views of their assets across multiple platforms.
26 |
27 | ### Mobile Applications
28 | Provide a seamless authentication experience in mobile apps without requiring users to enter and store their WhiteBIT credentials in the app.
29 |
30 | ### Enterprise Integrations
31 | Enable business systems to securely connect to WhiteBIT for institutional trading, reporting, and compliance purposes with proper audit controls.
32 |
33 | ## Available Scopes
34 |
35 | WhiteBIT OAuth 2.0 implementation supports a wide range of permission scopes that can be requested during client setup, including:
36 |
37 | - Account information access
38 | - Balance and transaction history
39 | - Order management (read, create, delete)
40 | - Market data access
41 | - Trading history
42 | - And more specialized permissions
43 |
44 | For a complete list of available scopes and detailed implementation instructions, please visit the [Usage](/platform/oauth/usage/#available-scopes) page.
45 |
46 | ## Getting Started
47 |
48 | **WhiteBIT OAuth 2.0 integration is exclusively offered to close ecosystem partners.** To integrate WhiteBIT OAuth 2.0 with your application, please contact our support team at [support@whitebit.com](mailto:support@whitebit.com). Our team will guide you through the OAuth integration process, including:
49 |
50 | 1. Application registration and approval
51 | 2. Scope request and configuration
52 | 3. Implementation support for the OAuth 2.0 authorization flow
53 | 4. Technical documentation for token exchange and API authentication
54 |
55 | Once your integration request is approved, you'll receive detailed instructions for implementing the OAuth 2.0 flow with your application.
56 |
57 | ## Next Steps: Implementation Details
58 |
59 | Now that you understand the benefits and use cases of WhiteBIT's OAuth 2.0 implementation, you're ready to dive into the technical details. Our [Usage](/platform/oauth/usage) page provides comprehensive documentation on:
60 |
61 | - Endpoint specifications for authorization and token exchange
62 | - Request and response formats with example code
63 | - Complete list of available scopes and their permissions
64 | - Step-by-step implementation guides for common scenarios
65 | - Troubleshooting tips and best practices
66 |
67 | The usage documentation will guide you through the entire implementation process from initial setup to making your first authenticated API calls.
68 |
69 | ## Support
70 |
71 | If you have questions or need assistance with OAuth 2.0 integration, please contact our developer support team at [support@whitebit.com](mailto:support@whitebit.com).
72 |
--------------------------------------------------------------------------------
/components/playground/message-composer.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { useState, useCallback } from "react";
4 | import { Label } from "@/components/ui/label";
5 | import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
6 | import { Textarea } from "@/components/ui/textarea";
7 | import { Button } from "@/components/ui/button";
8 | import { Send } from "lucide-react";
9 | import { WebSocketProvider } from "@/types/websocket";
10 |
11 | interface MessageComposerProps {
12 | selectedProvider: WebSocketProvider | null;
13 | isConnected: boolean;
14 | onSend: (message: string) => void;
15 | className?: string;
16 | }
17 |
18 | export function MessageComposer({
19 | selectedProvider,
20 | isConnected,
21 | onSend,
22 | className,
23 | }: MessageComposerProps) {
24 | const [payload, setPayload] = useState("");
25 | const hasSamples = selectedProvider?.samples && selectedProvider.samples.length > 0;
26 |
27 | // Handle JSON paste with cleanup
28 | const handleJsonPaste = (e: React.ClipboardEvent) => {
29 | e.preventDefault();
30 | const text = e.clipboardData.getData("text");
31 | // Remove comments and trailing commas from JSON
32 | const cleanJson = text
33 | // Remove single-line comments
34 | .replace(/\/\/.*$/gm, "")
35 | // Remove multi-line comments
36 | .replace(/\/\*[\s\S]*?\*\//g, "")
37 | // Remove trailing commas
38 | .replace(/,(\s*[}\]])/g, "$1")
39 | // Remove whitespace
40 | .trim();
41 | try {
42 | // Verify it's valid JSON and format it
43 | const parsed = JSON.parse(cleanJson);
44 | setPayload(JSON.stringify(parsed, null, 2));
45 | } catch {
46 | // If not valid JSON, just paste the cleaned text
47 | setPayload(cleanJson);
48 | }
49 | };
50 |
51 | const handleSend = useCallback(() => {
52 | if (payload.trim()) {
53 | onSend(payload);
54 | }
55 | }, [onSend, payload]);
56 |
57 | const handleClear = useCallback(() => {
58 | setPayload("");
59 | }, []);
60 |
61 | const handleSampleSelect = useCallback((value: string) => {
62 | setPayload(value);
63 | }, []);
64 |
65 | return (
66 |
67 | {hasSamples && selectedProvider?.samples && (
68 |
69 |
70 |
Sample Messages
71 |
72 |
73 |
74 |
75 |
76 | {selectedProvider.samples.map((sample) => (
77 |
78 | {sample.label}
79 |
80 | ))}
81 |
82 |
83 |
84 |
85 | )}
86 |
87 |
88 |
89 | Message Payload
90 |
91 |
109 |
110 | );
111 | }
112 |
--------------------------------------------------------------------------------
/global.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | :root {
6 | --nextra-sidebar-bg: 0 0% 100%;
7 | --background: var(--nextra-bg);
8 | --foreground: 0 0% 3.9%;
9 | --font-inter: 'Inter', sans-serif;
10 | --font-manrope: 'Manrope', sans-serif;
11 | --card: 0 0% 100%;
12 | --card-foreground: 0 0% 3.9%;
13 | --popover: 0 0% 100%;
14 | --popover-foreground: 0 0% 3.9%;
15 | --primary: 0 0% 9%;
16 | --primary-foreground: 0 0% 98%;
17 | --secondary: 0 0% 96.1%;
18 | --secondary-foreground: 0 0% 9%;
19 | --muted: 0 0% 96.1%;
20 | --muted-foreground: 0 0% 45.1%;
21 | --accent: 0 0% 96.1%;
22 | --accent-foreground: 0 0% 9%;
23 | --destructive: 0 84.2% 60.2%;
24 | --destructive-foreground: 0 0% 98%;
25 | --border: 0 0% 89.8%;
26 | --input: 0 0% 89.8%;
27 | --ring: 0 0% 3.9%;
28 | --chart-1: 12 76% 61%;
29 | --chart-2: 173 58% 39%;
30 | --chart-3: 197 37% 24%;
31 | --chart-4: 43 74% 66%;
32 | --chart-5: 27 87% 67%;
33 | --radius: 0.5rem;
34 | }
35 |
36 | .dark {
37 | --nextra-sidebar-bg: 0 0% 0%;
38 | --background: var(--nextra-bg);
39 | --foreground: 0 0% 98%;
40 | --card: 0 0% 3.9%;
41 | --card-foreground: 0 0% 98%;
42 | --popover: 0 0% 3.9%;
43 | --popover-foreground: 0 0% 98%;
44 | --primary: 0 0% 98%;
45 | --primary-foreground: 0 0% 9%;
46 | --secondary: 0 0% 14.9%;
47 | --secondary-foreground: 0 0% 98%;
48 | --muted: 0 0% 14.9%;
49 | --muted-foreground: 0 0% 63.9%;
50 | --accent: 0 0% 14.9%;
51 | --accent-foreground: 0 0% 98%;
52 | --destructive: 0 62.8% 30.6%;
53 | --destructive-foreground: 0 0% 98%;
54 | --border: 0 0% 14.9%;
55 | --input: 0 0% 14.9%;
56 | --ring: 0 0% 83.1%;
57 | --chart-1: 220 70% 50%;
58 | --chart-2: 160 60% 45%;
59 | --chart-3: 30 80% 55%;
60 | --chart-4: 280 65% 60%;
61 | --chart-5: 340 75% 55%
62 | }
63 |
64 | @media (max-width: 768px) {
65 | .nextra-sidebar-container {
66 | background-color: hsl(var(--nextra-sidebar-bg));
67 | }
68 | }
69 |
70 | @layer base {
71 | * {
72 | @apply border-border;
73 | }
74 |
75 | html {
76 | scrollbar-gutter: stable;
77 | font-family: var(--font-inter);
78 | }
79 |
80 | body {
81 | @apply bg-background text-foreground;
82 | }
83 |
84 | h1,
85 | h2,
86 | h3,
87 | h4,
88 | h5,
89 | h6 {
90 | font-family: var(--font-manrope);
91 | }
92 | }
93 |
94 | /* Regional link components - match Nextra's theme link color */
95 | .regional-link {
96 | color: hsl(43, 79%, 61%);
97 | text-decoration: none;
98 | transition: opacity 0.2s;
99 | }
100 |
101 | .regional-link:hover {
102 | text-decoration: underline;
103 | opacity: 0.8;
104 | }
105 |
106 | /* Position region toggle between Contact and Search */
107 | /* Using !important to override Nextra's default navbar flexbox styling */
108 | nav > div:has(.region-toggle-wrapper) {
109 | display: flex !important;
110 | align-items: center;
111 | gap: 0.75rem;
112 | }
113 |
114 | /* Region toggle - order after meta items but before search/github */
115 | nav .region-toggle-wrapper {
116 | order: 1 !important;
117 | }
118 |
119 | /* Search and GitHub - push to end */
120 | nav div:has(input[type="search"]),
121 | nav a[href*="github.com"] {
122 | order: 2 !important;
123 | }
124 |
125 | .neon-border-button {
126 | border: 2px solid #3b82f6;
127 | box-shadow:
128 | 0 0 5px rgba(59, 130, 246, 0.4),
129 | 0 0 10px rgba(59, 130, 246, 0.3),
130 | 0 0 15px rgba(59, 130, 246, 0.2);
131 | animation: neon-glow 3s ease-in-out infinite;
132 | }
133 |
134 | .neon-border-button:hover {
135 | box-shadow:
136 | 0 0 8px rgba(59, 130, 246, 0.6),
137 | 0 0 16px rgba(59, 130, 246, 0.4),
138 | 0 0 24px rgba(59, 130, 246, 0.3);
139 | }
140 |
141 | @keyframes neon-glow {
142 |
143 | 0%,
144 | 100% {
145 | border-color: #3b82f6;
146 | box-shadow:
147 | 0 0 5px rgba(59, 130, 246, 0.4),
148 | 0 0 10px rgba(59, 130, 246, 0.3),
149 | 0 0 15px rgba(59, 130, 246, 0.2);
150 | }
151 |
152 | 25% {
153 | border-color: #60a5fa;
154 | box-shadow:
155 | 0 0 8px rgba(96, 165, 250, 0.5),
156 | 0 0 16px rgba(96, 165, 250, 0.4),
157 | 0 0 24px rgba(96, 165, 250, 0.3);
158 | }
159 |
160 | 50% {
161 | border-color: #93c5fd;
162 | box-shadow:
163 | 0 0 12px rgba(147, 197, 253, 0.6),
164 | 0 0 24px rgba(147, 197, 253, 0.5),
165 | 0 0 36px rgba(147, 197, 253, 0.4);
166 | }
167 |
168 | 75% {
169 | border-color: #60a5fa;
170 | box-shadow:
171 | 0 0 8px rgba(96, 165, 250, 0.5),
172 | 0 0 16px rgba(96, 165, 250, 0.4),
173 | 0 0 24px rgba(96, 165, 250, 0.3);
174 | }
175 | }
--------------------------------------------------------------------------------
/components/playground/connection-controls.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import { Label } from "@/components/ui/label";
4 | import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
5 | import { Input } from "@/components/ui/input";
6 | import { Button } from "@/components/ui/button";
7 | import { Play, Square, Wifi, WifiOff } from "lucide-react";
8 | import { config } from "@/config/websocket";
9 | import { useRegionConfig } from "@/lib/region-context";
10 |
11 | interface ConnectionControlsProps {
12 | isConnected: boolean;
13 | selectedProvider: string;
14 | customUrl: string;
15 | onConnect: (url: string) => void;
16 | onDisconnect: () => void;
17 | onProviderChange: (provider: keyof typeof config.providers | "custom" | "") => void;
18 | onCustomUrlChange: (url: string) => void;
19 | className?: string;
20 | }
21 |
22 | export function ConnectionControls({
23 | isConnected,
24 | selectedProvider,
25 | customUrl,
26 | onConnect,
27 | onDisconnect,
28 | onProviderChange,
29 | onCustomUrlChange,
30 | className,
31 | }: ConnectionControlsProps) {
32 | const regionConfig = useRegionConfig();
33 |
34 | const handleProviderChange = (value: string) => {
35 | if (value !== "custom") {
36 | onCustomUrlChange("");
37 | }
38 | onProviderChange(value as keyof typeof config.providers | "custom" | "");
39 | };
40 |
41 | const handleConnect = () => {
42 | if (selectedProvider === "custom") {
43 | if (customUrl) {
44 | onConnect(customUrl);
45 | }
46 | } else if (selectedProvider) {
47 | // Use dynamic region-aware URL instead of static config
48 | const dynamicUrl = `${regionConfig.wsBaseUrl}/ws`;
49 | onConnect(dynamicUrl);
50 | }
51 | };
52 |
53 | return (
54 |
55 |
56 | {isConnected ? (
57 |
58 |
59 | Connected
60 |
61 | ) : (
62 |
63 |
64 | Disconnected
65 |
66 | )}
67 |
68 |
69 |
70 |
71 |
WebSocket Provider
72 |
73 |
74 |
75 |
76 |
77 | {Object.entries(config.providers).map(([key, provider]) => (
78 |
79 |
80 |
{provider.label}
81 | {provider.description && (
82 |
{provider.description}
83 | )}
84 |
85 |
86 | ))}
87 |
88 | Custom URL
89 |
90 |
91 |
92 |
93 |
94 | {selectedProvider === "custom" && (
95 |
96 | Custom WebSocket URL
97 | onCustomUrlChange(e.target.value)}
100 | placeholder="wss://example.com/ws"
101 | className="font-mono"
102 | />
103 |
104 | )}
105 |
106 |
107 |
112 |
113 | Connect
114 |
115 |
121 |
122 | Disconnect
123 |
124 |
125 |
126 |
127 | );
128 | }
129 |
--------------------------------------------------------------------------------
/components/ui/select.tsx:
--------------------------------------------------------------------------------
1 | "use client"
2 |
3 | import * as React from "react"
4 | import * as SelectPrimitive from "@radix-ui/react-select"
5 | import { Check, ChevronDown } from "lucide-react"
6 |
7 | import { cn } from "@/lib/utils"
8 |
9 | const Select = SelectPrimitive.Root
10 |
11 | const SelectGroup = SelectPrimitive.Group
12 |
13 | const SelectValue = SelectPrimitive.Value
14 |
15 | const SelectTrigger = React.forwardRef<
16 | React.ElementRef,
17 | React.ComponentPropsWithoutRef
18 | >(({ className, children, ...props }, ref) => (
19 |
27 | {children}
28 |
29 |
30 |
31 |
32 | ))
33 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
34 |
35 | const SelectContent = React.forwardRef<
36 | React.ElementRef,
37 | React.ComponentPropsWithoutRef
38 | >(({ className, children, position = "popper", ...props }, ref) => (
39 |
40 |
51 |
58 | {children}
59 |
60 |
61 |
62 | ))
63 | SelectContent.displayName = SelectPrimitive.Content.displayName
64 |
65 | const SelectLabel = React.forwardRef<
66 | React.ElementRef,
67 | React.ComponentPropsWithoutRef
68 | >(({ className, ...props }, ref) => (
69 |
74 | ))
75 | SelectLabel.displayName = SelectPrimitive.Label.displayName
76 |
77 | const SelectItem = React.forwardRef<
78 | React.ElementRef,
79 | React.ComponentPropsWithoutRef
80 | >(({ className, children, ...props }, ref) => (
81 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | {children}
96 |
97 | ))
98 | SelectItem.displayName = SelectPrimitive.Item.displayName
99 |
100 | const SelectSeparator = React.forwardRef<
101 | React.ElementRef,
102 | React.ComponentPropsWithoutRef
103 | >(({ className, ...props }, ref) => (
104 |
109 | ))
110 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName
111 |
112 | export {
113 | Select,
114 | SelectGroup,
115 | SelectValue,
116 | SelectTrigger,
117 | SelectContent,
118 | SelectLabel,
119 | SelectItem,
120 | SelectSeparator,
121 | }
122 |
--------------------------------------------------------------------------------
/components/regional-disclaimer.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useMemo, useState } from "react";
4 | import { useRegion } from "@/lib/region-context";
5 | import { Region } from "@/config/regions";
6 | import { cn } from "@/lib/utils";
7 | import { Alert } from "@/components/alert";
8 |
9 | type DisclaimerVariant = "availability" | "modification" | "requirement" | "restriction";
10 | type DisclaimerSeverity = "info" | "warning" | "critical";
11 |
12 | interface RegionalDisclaimerProps {
13 | regions: Region[] | { include?: Region[]; exclude?: Region[] };
14 | variant: DisclaimerVariant;
15 | severity?: DisclaimerSeverity;
16 | title?: string;
17 | children: React.ReactNode;
18 | collapsible?: boolean;
19 | defaultCollapsed?: boolean;
20 | persistState?: boolean;
21 | showAfter?: string;
22 | showBefore?: string;
23 | className?: string;
24 | }
25 |
26 | const variantStyles: Record = {
27 | availability: { icon: "ℹ️" },
28 | modification: { icon: "⚠️" },
29 | requirement: { icon: "📋" },
30 | restriction: { icon: "🚫" },
31 | };
32 |
33 | const severityMap: Record = {
34 | info: "info",
35 | warning: "warning",
36 | critical: "error",
37 | };
38 |
39 | export function RegionalDisclaimer({
40 | regions,
41 | variant,
42 | severity = "info",
43 | title,
44 | children,
45 | collapsible = false,
46 | defaultCollapsed = false,
47 | persistState = false,
48 | showAfter,
49 | showBefore,
50 | className,
51 | }: RegionalDisclaimerProps) {
52 | const { region } = useRegion();
53 | const [isCollapsed, setIsCollapsed] = useState(defaultCollapsed);
54 |
55 | React.useEffect(() => {
56 | if (persistState && typeof window !== "undefined") {
57 | const key = `disclaimer-${variant}-${region}`;
58 | const saved = localStorage.getItem(key);
59 | if (saved !== null) {
60 | setIsCollapsed(saved === "true");
61 | }
62 | }
63 | }, [persistState, variant, region]);
64 |
65 | // Determine if this disclaimer should be visible
66 | const isVisible = useMemo(() => {
67 | // Time-based visibility
68 | if (showAfter || showBefore) {
69 | const now = new Date();
70 | if (showAfter && now < new Date(showAfter)) return false;
71 | if (showBefore && now > new Date(showBefore)) return false;
72 | }
73 |
74 | // Region-based visibility
75 | if (Array.isArray(regions)) {
76 | return regions.includes(region);
77 | } else {
78 | // Object with include/exclude
79 | if (regions.include) {
80 | return regions.include.includes(region);
81 | }
82 | if (regions.exclude) {
83 | return !regions.exclude.includes(region);
84 | }
85 | return true; // Default to visible if no conditions
86 | }
87 | }, [regions, region, showAfter, showBefore]);
88 |
89 | if (!isVisible) {
90 | return null;
91 | }
92 |
93 | const variantStyle = variantStyles[variant];
94 | const alertType = severityMap[severity];
95 |
96 | const handleToggle = () => {
97 | setIsCollapsed(!isCollapsed);
98 | if (persistState && typeof window !== "undefined") {
99 | const key = `disclaimer-${variant}-${region}`;
100 | localStorage.setItem(key, String(!isCollapsed));
101 | }
102 | };
103 |
104 | const regionBadge = (
105 |
106 | {region}
107 |
108 | );
109 |
110 | const headerContent = (
111 | <>
112 | {title && {title} }
113 | {!title && variantStyle.icon && {variantStyle.icon} }
114 | {regionBadge}
115 | >
116 | );
117 |
118 | return (
119 |
120 |
129 | {headerContent}
130 |
131 | {isCollapsed ? "▼" : "▲"}
132 |
133 |
134 | ) : headerContent}
135 | message={!collapsible || !isCollapsed ? (
136 |
137 | {children}
138 |
139 | ) : null}
140 | className={cn(
141 | collapsible && isCollapsed && "cursor-pointer",
142 | )}
143 | />
144 |
145 | );
146 | }
147 |
--------------------------------------------------------------------------------
/config/websocket.ts:
--------------------------------------------------------------------------------
1 | import { WebSocketConfig } from "@/types/websocket";
2 | import { REGIONS, DEFAULT_REGION } from "./regions";
3 |
4 | export const config: WebSocketConfig = {
5 | providers: {
6 | public: {
7 | label: "Public WebSocket API",
8 | url: `${REGIONS[DEFAULT_REGION].wsBaseUrl}/ws`,
9 | description: "Public WebSocket API for market data",
10 | samples: [
11 | {
12 | label: "Ping",
13 | payload: JSON.stringify({
14 | id: 1,
15 | method: "ping",
16 | params: [],
17 | }, null, 2)
18 | },
19 | {
20 | label: "Time",
21 | payload: JSON.stringify({
22 | id: 2,
23 | method: "time",
24 | params: [],
25 | }, null, 2)
26 | },
27 | {
28 | label: "Subscribe to Klines",
29 | payload: JSON.stringify({
30 | id: 4,
31 | method: "candles_subscribe",
32 | params: ["BTC_USDT", 900],
33 | }, null, 2)
34 | },
35 | {
36 | label: "Unsubscribe from Klines",
37 | payload: JSON.stringify({
38 | id: 5,
39 | method: "candles_unsubscribe",
40 | params: ["BTC_USDT", 900],
41 | }, null, 2)
42 | },
43 | {
44 | label: "Subscribe to Market Price",
45 | payload: JSON.stringify({
46 | id: 7,
47 | method: "lastprice_subscribe",
48 | params: ["BTC_USDT"],
49 | }, null, 2)
50 | },
51 | {
52 | label: "Unsubscribe from Market Price",
53 | payload: JSON.stringify({
54 | id: 8,
55 | method: "lastprice_unsubscribe",
56 | params: [],
57 | }, null, 2)
58 | },
59 | {
60 | label: "Get Market Price",
61 | payload: JSON.stringify({
62 | id: 9,
63 | method: "lastprice_request",
64 | params: ["BTC_USDT"],
65 | }, null, 2)
66 | },
67 | {
68 | label: "Subscribe to Market Stats",
69 | payload: JSON.stringify({
70 | id: 10,
71 | method: "market_subscribe",
72 | params: ["BTC_USDT"],
73 | }, null, 2)
74 | },
75 | {
76 | label: "Unsubscribe from Market Stats",
77 | payload: JSON.stringify({
78 | id: 11,
79 | method: "market_unsubscribe",
80 | params: [],
81 | }, null, 2)
82 | },
83 | {
84 | label: "Get Market Stats",
85 | payload: JSON.stringify({
86 | id: 12,
87 | method: "market_request",
88 | params: ["BTC_USDT", 86400],
89 | }, null, 2)
90 | },
91 | {
92 | label: "Subscribe to Trades",
93 | payload: JSON.stringify({
94 | id: 13,
95 | method: "trades_subscribe",
96 | params: ["BTC_USDT"],
97 | }, null, 2)
98 | },
99 | {
100 | label: "Unsubscribe from Trades",
101 | payload: JSON.stringify({
102 | id: 14,
103 | method: "trades_unsubscribe",
104 | params: ["BTC_USDT"],
105 | }, null, 2)
106 | },
107 | {
108 | label: "Get Recent Trades",
109 | payload: JSON.stringify({
110 | id: 15,
111 | method: "trades_request",
112 | params: ["BTC_USDT", 100, 41358445],
113 | }, null, 2)
114 | },
115 | {
116 | label: "Subscribe to Book Ticker",
117 | payload: JSON.stringify({
118 | id: 16,
119 | method: "bookTicker_subscribe",
120 | params: ["BTC_USDT"],
121 | }, null, 2)
122 | },
123 | {
124 | label: "Unsubscribe from Book Ticker",
125 | payload: JSON.stringify({
126 | id: 17,
127 | method: "bookTicker_unsubscribe",
128 | params: ["BTC_USDT"],
129 | }, null, 2)
130 | },
131 | {
132 | label: "Subscribe to Order Book",
133 | payload: JSON.stringify({
134 | id: 18,
135 | method: "depth_subscribe",
136 | params: ["BTC_USDT", 100, "0"],
137 | }, null, 2)
138 | },
139 | {
140 | label: "Unsubscribe from Order Book",
141 | payload: JSON.stringify({
142 | id: 19,
143 | method: "depth_unsubscribe",
144 | params: ["BTC_USDT", 100, "0"],
145 | }, null, 2)
146 | },
147 | {
148 | label: "Get Order Book",
149 | payload: JSON.stringify({
150 | id: 20,
151 | method: "depth_request",
152 | params: ["BTC_USDT", 100, "0"],
153 | }, null, 2)
154 | }
155 | ]
156 | }
157 | }
158 | };
159 |
160 | export type WebSocketProviderKey = keyof (typeof config)["providers"];
161 |
--------------------------------------------------------------------------------
/components/playground/index.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useCallback, useEffect, useState } from "react";
4 | import { cn } from "@/lib/utils";
5 | import { Card } from "@/components/ui/card";
6 | import { ConnectionControls } from "./connection-controls";
7 | import { MessageComposer } from "./message-composer";
8 | import { MessageLog } from "./message-log";
9 | import { config } from "@/config/websocket";
10 | import { WebSocketProvider } from "@/types/websocket";
11 | import { RegionSelector } from "@/components/region-selector";
12 |
13 | interface PlaygroundProps {
14 | defaultProvider?: string;
15 | className?: string;
16 | }
17 |
18 | interface Message {
19 | id: string;
20 | type: "sent" | "received" | "error";
21 | data: string;
22 | timestamp: Date;
23 | }
24 |
25 | export function Playground({ defaultProvider, className }: PlaygroundProps) {
26 | const [isConnected, setIsConnected] = useState(false);
27 | const [ws, setWs] = useState(null);
28 | const [messages, setMessages] = useState([]);
29 | const [autoScroll, setAutoScroll] = useState(true);
30 | const [selectedProviderKey, setSelectedProviderKey] = useState("");
31 | const [selectedProvider, setSelectedProvider] = useState(
32 | defaultProvider && config.providers[defaultProvider]
33 | ? config.providers[defaultProvider]
34 | : null
35 | );
36 | const [customUrl, setCustomUrl] = useState("");
37 |
38 | const addMessage = useCallback((type: Message["type"], data: string) => {
39 | setMessages((prev) => [
40 | ...prev,
41 | {
42 | id: Date.now().toString() + Math.random().toString(36).substr(2, 9),
43 | type,
44 | data,
45 | timestamp: new Date(),
46 | },
47 | ]);
48 | }, []);
49 |
50 | const connect = useCallback(
51 | (url: string) => {
52 | if (ws) {
53 | ws.close();
54 | }
55 |
56 | try {
57 | const newWs = new WebSocket(url);
58 |
59 | newWs.onopen = () => {
60 | setIsConnected(true);
61 | addMessage("received", "Connected");
62 | };
63 |
64 | newWs.onclose = () => {
65 | setIsConnected(false);
66 | addMessage("received", "Disconnected");
67 | };
68 |
69 | newWs.onerror = () => {
70 | addMessage("error", "WebSocket error occurred");
71 | };
72 |
73 | newWs.onmessage = (event) => {
74 | addMessage("received", event.data);
75 | };
76 |
77 | setWs(newWs);
78 | } catch (error) {
79 | addMessage("error", `Failed to connect: ${error}`);
80 | }
81 | },
82 | [ws, addMessage]
83 | );
84 |
85 | const disconnect = useCallback(() => {
86 | if (ws) {
87 | ws.close();
88 | setWs(null);
89 | }
90 | }, [ws]);
91 |
92 | const sendMessage = useCallback(
93 | (message: string) => {
94 | if (ws && ws.readyState === WebSocket.OPEN) {
95 | ws.send(message);
96 | addMessage("sent", message);
97 | }
98 | },
99 | [ws, addMessage]
100 | );
101 |
102 | const clearLog = useCallback(() => {
103 | setMessages([]);
104 | }, []);
105 |
106 | const handleProviderChange = useCallback((providerId: string) => {
107 | setSelectedProviderKey(providerId);
108 | if (providerId && providerId !== "custom" && config.providers[providerId]) {
109 | setSelectedProvider(config.providers[providerId]);
110 | } else {
111 | setSelectedProvider(null);
112 | }
113 | }, []);
114 |
115 | const handleCopy = useCallback((message: string) => {
116 | navigator.clipboard.writeText(message);
117 | }, []);
118 |
119 | useEffect(() => {
120 | if (defaultProvider && config.providers[defaultProvider]) {
121 | handleProviderChange(defaultProvider);
122 | }
123 | }, [defaultProvider, handleProviderChange]);
124 |
125 | useEffect(() => {
126 | return () => {
127 | if (ws) {
128 | ws.close();
129 | }
130 | };
131 | }, [ws]);
132 |
133 | return (
134 |
135 |
136 |
137 |
138 | connect(customUrl || selectedProvider?.url || "")}
145 | onDisconnect={disconnect}
146 | />
147 |
152 |
159 |
160 | );
161 | }
162 |
--------------------------------------------------------------------------------
/pages/faq.mdx:
--------------------------------------------------------------------------------
1 | import { RegionalUrl } from "@/components/regional-url";
2 | import { URL_TYPE_API } from "@/constants";
3 |
4 | # Frequently Asked Questions (FAQ)
5 |
6 | ## Rate Limits and Errors
7 |
8 | ### Q: Why am I getting a 429 error and can't make requests?
9 |
10 | If the rate limit for an endpoint is exceeded, you will receive a 429 error. To resolve this:
11 | - Wait for the rate limit window to reset
12 | - Check the specific rate limit value in the endpoint description
13 | - Consider implementing rate limiting in your code
14 |
15 | ### Q: Why do I get a CORS error when requesting the whitebit.com/api/v4/public/ticker method?
16 |
17 | CORS requests to this endpoint are forbidden for security reasons. Make the request from your backend instead of client-side.
18 |
19 | ### Q: Why am I getting a 403 error when making requests to the Smartplan endpoints?
20 |
21 | These endpoints are restricted to B2B partner services only. To gain access:
22 | - Contact [support@whitebit.com](mailto:support@whitebit.com)
23 | - Request permissions for Smartplan endpoints
24 | - Provide your use case details
25 |
26 | ### Q: How can I check if WhiteBIT services are operational?
27 |
28 | For real-time service status and incident updates, visit our [Status Page](https://status.whitebit.com/). You can also check the [Platform Status endpoint](/public/http-v4#maintenance-status) (`/api/v4/public/platform/status`) for programmatic status checks.
29 |
30 | ## WebSocket and Data Access
31 |
32 | ### Q: Can I get information on pairs for multiple time periods in the WebSocket Kline method?
33 |
34 | There are two solutions:
35 | 1. Open multiple WebSocket connections
36 | 2. Use the equivalent HTTP method instead
37 |
38 | For more details, see the [Kline documentation](https://whitebit-exchange.github.io/api-docs/public/websocket/#kline).
39 |
40 | ### Q: Why can't I see trade history for a pair in the last 24 hours?
41 |
42 | The system only shows the last 100 deals by default. To see more:
43 | - Subscribe to our WebSocket feed
44 | - Accumulate and store the data on your side
45 | - Process the data as needed for your use case
46 |
47 | ## Authentication and Nonce
48 |
49 | ### Q: Why am I getting nonce errors?
50 |
51 | To resolve nonce errors:
52 | 1. Debug your code implementation
53 | 2. Recreate your API keys
54 | 3. Ensure your system time is properly synchronized
55 |
56 | ## Balance and Transfers
57 |
58 | ### Q: Why doesn't the transfer between balances occur when transferring assets via API?
59 |
60 | When making transfers and withdrawals:
61 | - Wait for transfers to complete (approximately 2 seconds)
62 | - Don't initiate withdrawals before transfer completion
63 | - Implement proper error handling for transfer states
64 |
65 | ### Q: I get an error that there are insufficient funds for withdrawal, even though there are assets on the balance.
66 |
67 | This occurs when the withdrawal amount plus the fee exceeds your available balance. Remember to:
68 | - Account for withdrawal fees in your calculations
69 | - Check the [fees documentation](https://github.com/whitebit-exchange/api-docs/blob/main/docs/Private/http-main-v4.md#fees)
70 | - Ensure sufficient balance for both amount and fees
71 |
72 | ## Technical Requirements
73 |
74 | ### Q: Is https required for Webhook API communication? What port is used?
75 |
76 | Yes, HTTPS is required:
77 | - Communication occurs over port 443
78 | - SSL/TLS encryption is mandatory
79 | - HTTP connections are not supported
80 |
81 | ### Q: How can I check if deposits and withdrawals are available for a specific currency?
82 |
83 | You can check currency status through the assets endpoint:
84 | - URL: /api/v4/public/assets
85 | - This endpoint provides real-time status of all currencies
86 | - Check the currency-specific enabled/disabled flags
87 |
88 | ## API Keys and Security
89 |
90 | ### Q: How do I ensure my API keys are secure?
91 |
92 | To maintain API key security:
93 | - Never share your API keys
94 | - Store keys securely (use environment variables)
95 | - Restrict IP addresses when possible
96 | - Use the minimum required permissions
97 | - Rotate keys periodically
98 |
99 | ### Q: What should I do if I suspect my API key is compromised?
100 |
101 | If you suspect key compromise:
102 | 1. Immediately delete the compromised key
103 | 2. Review account activity for unauthorized actions
104 | 3. Create new API keys with fresh permissions
105 | 4. Update your application with the new keys
106 | 5. Contact support if unauthorized activity is detected
107 |
108 |
109 | ## Performance and Optimization
110 |
111 | ### Q: How can I optimize my API usage for high-frequency trading?
112 |
113 | For optimal API performance:
114 | - Use WebSocket for real-time data
115 | - Batch requests when possible
116 | - Implement proper rate limiting
117 | - Cache frequently accessed data
118 | - Monitor network latency
119 |
120 | ### Q: What's the best way to handle API disconnections?
121 |
122 | To handle disconnections effectively:
123 | - Implement automatic reconnection logic
124 | - Use exponential backoff for retries
125 | - Maintain local order state
126 | - Log all critical operations
127 | - Set up monitoring and alerts
128 |
--------------------------------------------------------------------------------
/components/playground/message-log.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import React, { useEffect, useRef } from "react";
4 | import { ScrollArea } from "@/components/ui/scroll-area";
5 | import { cn } from "@/lib/utils";
6 |
7 | import { Badge } from "@/components/ui/badge";
8 | import { Button } from "@/components/ui/button";
9 | import { WebSocketMessage } from "@/types/websocket";
10 | import { Wifi, Trash2, Copy } from "lucide-react";
11 |
12 | interface MessageLogProps {
13 | messages: WebSocketMessage[];
14 | autoScroll: boolean;
15 | onAutoScrollChange: (value: boolean) => void;
16 | onClear: () => void;
17 | onCopy: (message: string) => void;
18 | className?: string;
19 | }
20 |
21 | export function MessageLog({
22 | messages,
23 | autoScroll,
24 | onAutoScrollChange,
25 | onClear,
26 | onCopy,
27 | className,
28 | }: MessageLogProps) {
29 | const messagesEndRef = useRef(null);
30 |
31 | useEffect(() => {
32 | if (autoScroll && messagesEndRef.current) {
33 | messagesEndRef.current.scrollIntoView({ behavior: "smooth" });
34 | }
35 | }, [messages, autoScroll]);
36 |
37 | const formatTimestamp = (timestamp: Date) => {
38 | return timestamp.toLocaleTimeString();
39 | };
40 |
41 | const formatSize = (size: number) => {
42 | if (size < 1024) return `${size}B`;
43 | if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}KB`;
44 | return `${(size / (1024 * 1024)).toFixed(1)}MB`;
45 | };
46 |
47 | return (
48 |
49 |
50 |
51 |
Message Log
52 |
53 |
54 | onAutoScrollChange(e.target.checked)}
58 | className="rounded border-border text-primary bg-card"
59 | />
60 | Auto-scroll
61 |
62 |
63 |
64 | Clear
65 |
66 |
67 |
68 |
69 | {messages.length} message{messages.length !== 1 ? "s" : ""}
70 |
71 |
72 |
73 |
74 |
75 | {messages.length === 0 ? (
76 |
77 |
78 |
No messages yet
79 |
Connect to a WebSocket server to see messages
80 |
81 | ) : (
82 | messages.map((message) => (
83 |
95 |
96 |
97 |
109 | {message.type.toUpperCase()}
110 |
111 | {formatTimestamp(message.timestamp)}
112 | {message.size && (
113 | {formatSize(message.size)}
114 | )}
115 |
116 |
onCopy(message.data)}
118 | variant="ghost"
119 | size="sm"
120 | className="h-6 w-6 p-0 hover:bg-accent hover:text-accent-foreground"
121 | >
122 |
123 |
124 |
125 |
{message.data}
126 |
127 | ))
128 | )}
129 |
130 |
131 |
132 |
133 | );
134 | }
135 |
--------------------------------------------------------------------------------
/components/api-endpoint.tsx:
--------------------------------------------------------------------------------
1 | "use client";
2 |
3 | import { MethodLabel } from "@/components/method-label";
4 | import { CopyToClipboard } from "nextra/components";
5 | import { cn } from "@/lib/utils";
6 | import { useRegion } from "@/lib/region-context";
7 | import { getApiUrl } from "@/lib/urls";
8 | import { Region } from "@/config/regions";
9 | import { PROTOCOL_HTTPS } from "@/constants";
10 | import { useMemo } from "react";
11 | import { Badge } from "@/components/ui/badge";
12 |
13 | interface ApiEndpointProps {
14 | method: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
15 | path: string;
16 | regionOverride?: Region;
17 | showRegionBadge?: boolean;
18 | regionalPaths?: Partial>;
19 | regionalNotes?: Partial>;
20 | fullUrl?: boolean;
21 | deprecated?: boolean;
22 | }
23 |
24 | const methodBackgrounds: Record = {
25 | GET: "bg-green-50/50 dark:bg-green-900/20",
26 | POST: "bg-blue-50/50 dark:bg-blue-900/20",
27 | PUT: "bg-yellow-50/50 dark:bg-yellow-900/20",
28 | DELETE: "bg-red-50/50 dark:bg-red-900/20",
29 | PATCH: "bg-purple-50/50 dark:bg-purple-900/20",
30 | };
31 |
32 | export function ApiEndpoint({
33 | method,
34 | path,
35 | regionOverride,
36 | showRegionBadge = false,
37 | regionalPaths,
38 | regionalNotes,
39 | fullUrl = true,
40 | deprecated = false,
41 | }: ApiEndpointProps) {
42 | const { region, config } = useRegion();
43 | const effectiveRegion = regionOverride ?? region;
44 |
45 | // Get the effective path (use regional path if available)
46 | const effectivePath = useMemo(() => {
47 | if (regionalPaths?.[effectiveRegion]) {
48 | return regionalPaths[effectiveRegion]!;
49 | }
50 | return path;
51 | }, [path, regionalPaths, effectiveRegion]);
52 |
53 | // Construct full URL if needed
54 | const fullEndpointUrl = useMemo(() => {
55 | // If path already contains a full HTTPS URL, return as-is (security: only allow HTTPS)
56 | if (effectivePath.startsWith(PROTOCOL_HTTPS)) {
57 | return effectivePath;
58 | }
59 |
60 | if (fullUrl) {
61 | return getApiUrl(effectivePath, effectiveRegion);
62 | }
63 |
64 | return effectivePath;
65 | }, [effectivePath, fullUrl, effectiveRegion]);
66 |
67 | // Split the path into base path and query parameters for display
68 | const [basePath, queryString] = fullEndpointUrl.split('?');
69 |
70 | // Parse dynamic path parameters
71 | const renderPath = (pathPart: string) => {
72 | const methodTextColors: Record = {
73 | GET: "text-green-700 dark:text-green-400",
74 | POST: "text-blue-700 dark:text-blue-400",
75 | PUT: "text-yellow-700 dark:text-yellow-400",
76 | DELETE: "text-red-700 dark:text-red-400",
77 | PATCH: "text-purple-700 dark:text-purple-400",
78 | };
79 |
80 | return pathPart.split(/({[^}]+})/).map((part, index) => {
81 | if (part.match(/^{[^}]+}$/)) {
82 | return (
83 |
84 | {part}
85 |
86 | );
87 | }
88 | return {part} ;
89 | });
90 | };
91 |
92 | // Parse query parameters with formatting
93 | const renderQueryParams = (queryStr: string) => {
94 | if (!queryStr) return null;
95 |
96 | // Split by & but keep the & symbol for display
97 | return queryStr.split('&').map((param, index) => {
98 | const [key, value] = param.split('=').map((part) => part.trim());
99 |
100 | return (
101 |
102 | {index > 0 && & }
103 |
104 | {key}
105 | {value && (
106 | <>
107 | {'='}{value}
108 | >
109 | )}
110 |
111 |
112 | );
113 | });
114 | };
115 |
116 | // Check if there are regional differences
117 | const hasRegionalDifferences = regionalPaths !== undefined || regionalNotes !== undefined;
118 |
119 | return (
120 |
125 |
126 |
127 |
128 | {showRegionBadge && (
129 |
130 | {config.shortName}
131 |
132 | )}
133 | {hasRegionalDifferences && !showRegionBadge && (
134 |
135 | ⚠️
136 |
137 | )}
138 | {deprecated && (
139 |
140 | Deprecated
141 |
142 | )}
143 |
144 |
145 | {renderPath(basePath)}
146 | {queryString && (
147 | <>
148 | ?
149 | {renderQueryParams(queryString)}
150 | >
151 | )}
152 |
153 | {regionalNotes?.[effectiveRegion] && (
154 |
155 | {regionalNotes[effectiveRegion]}
156 |
157 | )}
158 |
159 |
fullEndpointUrl}
161 | className="absolute sm:relative right-3 top-3 sm:right-auto sm:top-auto sm:self-start"
162 | />
163 |
164 | );
165 | }
166 |
--------------------------------------------------------------------------------
/scripts/generate-og-images.tsx:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { join, dirname } from "path";
4 | import { ImageResponse } from "next/og";
5 | import seoConfig from "../seo.config";
6 | import React from "react";
7 | import { fileURLToPath } from "url";
8 | import fs from "fs";
9 |
10 | const __dirname = dirname(fileURLToPath(import.meta.url));
11 | const rootDir = join(__dirname, "..");
12 | const outputDir = join(rootDir, "public/og-images");
13 |
14 | // Define brand colors and theme
15 | const BRAND_COLORS = {
16 | dark: "#17172E", // Deep purple background
17 | muted: "hsl(250, 20%, 85%)", // Lighter muted text for better contrast
18 | };
19 |
20 | async function loadFont(fontPath: string): Promise {
21 | try {
22 | return await fs.promises.readFile(join(rootDir, fontPath));
23 | } catch (error) {
24 | console.error(`Error loading font from ${fontPath}:`, error);
25 | throw new Error(`Failed to load font: ${fontPath}`);
26 | }
27 | }
28 |
29 | async function generateOGImage(
30 | title: string,
31 | description: string,
32 | path: string,
33 | outputPath: string
34 | ) {
35 | // Read the logo file
36 | const logoPath = join(rootDir, "public", "img", "logo.svg");
37 | const logo = await fs.promises.readFile(logoPath);
38 | const logoBase64 = `data:image/svg+xml;base64,${logo.toString("base64")}`;
39 |
40 | // Read the pattern file
41 | const patternPath = join(rootDir, "public", "img", "pattern.png");
42 | const pattern = await fs.promises.readFile(patternPath);
43 | const patternBase64 = `data:image/png;base64,${pattern.toString("base64")}`;
44 |
45 | // Load fonts
46 | const interBold = await loadFont("public/fonts/Inter-Bold.ttf");
47 | const interRegular = await loadFont("public/fonts/Inter-Regular.ttf");
48 |
49 | if (!interBold || !interRegular) {
50 | throw new Error("Failed to load required fonts");
51 | }
52 |
53 | const image = new ImageResponse(
54 | (
55 |
66 | {/* Background Pattern */}
67 |
78 |
79 | {/* Main Content Wrapper */}
80 |
88 | {/* Logo */}
89 |
90 |
98 |
99 |
100 | {/* Content Container */}
101 |
109 | {/* Title */}
110 |
120 | {title}
121 |
122 |
123 | {/* Description */}
124 |
134 | {description}
135 |
136 |
137 |
138 |
139 | {/* Neon Line */}
140 |
152 |
153 | ),
154 | {
155 | width: 1200,
156 | height: 630,
157 | fonts: [
158 | {
159 | name: "Inter",
160 | data: interBold,
161 | weight: 700,
162 | style: "normal",
163 | },
164 | {
165 | name: "Inter",
166 | data: interRegular,
167 | weight: 400,
168 | style: "normal",
169 | },
170 | ],
171 | }
172 | );
173 |
174 | const buffer = Buffer.from(await image.arrayBuffer());
175 | await Bun.write(outputPath, buffer);
176 | console.log(`Generated OG image: ${outputPath}`);
177 | }
178 |
179 | async function generateAllOGImages() {
180 | console.log("Starting OG image generation...");
181 |
182 | try {
183 | // Ensure output directory exists
184 | await fs.promises.mkdir(outputDir, { recursive: true });
185 |
186 | // Generate OG images for each route in parallel using Promise.all
187 | await Promise.all(
188 | Object.entries(seoConfig).map(([route, config]) => {
189 | const outputPath = join(
190 | outputDir,
191 | `${route === "index" ? "home" : route}.png`
192 | );
193 |
194 | return generateOGImage(
195 | config.ogImage.title,
196 | config.ogImage.description,
197 | route,
198 | outputPath
199 | );
200 | })
201 | );
202 |
203 | console.log("OG image generation complete!");
204 | } catch (error) {
205 | console.error("Error generating OG images:", error);
206 | process.exit(1);
207 | }
208 | }
209 |
210 | generateAllOGImages();
211 |
--------------------------------------------------------------------------------
/pages/public/http-v2.mdx:
--------------------------------------------------------------------------------
1 | import { ApiEndpoint } from "@/components/api-endpoint";
2 | import { RegionalBaseUrl } from "@/components/regional-base-url";
3 | import { InlineRegionSelector } from "@/components/inline-region-selector";
4 | import { RegionalUrl } from "@/components/regional-url";
5 | import { RegionalBaseUrlText } from "@/components/regional-base-url-text";
6 | import { Code } from "nextra/components";
7 |
8 | # Public HTTP API V2
9 |
10 | - [Error messages V2 format](#error-messages-v2-format)
11 | - [Market Info](#market-info)
12 | - [Market activity](#market-activity)
13 | - [Recent Trades](#recent-trades)
14 | - [Fee](#fee)
15 | - [Asset Status List](#asset-status-list)
16 | - [Orderbook](#orderbook)
17 |
18 |
19 |
20 | Example how to use: /api/v2/public/{"{endpoint}"}
21 |
22 | All endpoints return time in Unix-time format.
23 |
24 | All endpoints return either a **JSON** object array.
25 |
26 | For receiving responses from API calls please use http method **GET**
27 |
28 | If endpoint required parameters you will need to send them as `query string`
29 |
30 | ---
31 |
32 | ### Error messages V2 format
33 |
34 | ```json
35 | {
36 | "success": false,
37 | "message": "ERROR MESSAGE",
38 | "params": []
39 | }
40 | ```
41 |
42 | ---
43 |
44 | ### Market Info
45 |
46 |
47 |
48 | This endpoint retrieves all information about available [markets](./../glossary.md#market).
49 |
50 | **Response is cached for:**
51 | _1 second_
52 |
53 | **Parameters:**
54 | NONE
55 |
56 | **Response:**
57 |
58 | ```json
59 | {
60 | "success": true,
61 | "message": null,
62 | "result": [
63 | {
64 | "name": "SON_USD", // Market pair name
65 | "stock": "SON", // Ticker of stock currency
66 | "money": "USD", // Ticker of money currency
67 | "stockPrec": "3", // Stock currency precision
68 | "moneyPrec": "2", // Precision of money currency
69 | "feePrec": "4", // Fee precision
70 | "makerFee": "0.1", // Default maker fee percent
71 | "takerFee": "0.1", // Default taker fee percent
72 | "minAmount": "0.001", // Minimal amount of stock to trade
73 | "minTotal": "0.001", // Minimal amount of money to trade
74 | "tradesEnabled": true // Is trading enabled
75 | },
76 | ]
77 | }
78 | ```
79 |
80 | ---
81 |
82 | ### Market activity
83 |
84 |
85 |
86 | This endpoint retrieves information on recent trading activity on all [markets](./../glossary.md#market).
87 |
88 | **Response is cached for:**
89 | _1 second_
90 |
91 | **Parameters:**
92 | NONE
93 |
94 | **Response:**
95 |
96 | ```json
97 | {
98 | "success": true,
99 | "message": null,
100 | "result": [
101 | {
102 | "lastUpdateTimestamp": "2020-07-10T13:37:27.000Z", // ISO 8601 time format of last update
103 | "tradingPairs": "ETH_BTC", // Name of market pair
104 | "lastPrice": "0.026014", // Last deal price
105 | "lowestAsk": "0.026027", // Lowest ask price
106 | "highestBid": "0.026001", // Highest bid price
107 | "baseVolume24h": "13445.988", // Volume in stock currency
108 | "quoteVolume24h": "350.113082102", // Volume in money currency
109 | "tradesEnabled": true // Is trading enabled on exchange
110 | },
111 | ]
112 | }
113 | ```
114 |
115 | ---
116 |
117 | ### Recent Trades
118 |
119 |
120 |
121 | This endpoint retrieves the [trades](./../glossary.md#deal-trade) that have been executed recently on the requested [market](./../glossary.md#market)
122 |
123 | **Response is cached for:**
124 | _1 second_
125 |
126 | **Parameters:**
127 | NONE
128 |
129 | **Response:**
130 |
131 | ```json
132 | {
133 | "success": true,
134 | "message": null,
135 | "result": [
136 | {
137 | "tradeId": 157257950, // A unique ID associated with the trade for the currency pair transaction Note: Unix timestamp does not qualify as trade_id.
138 | "price": "9371.69", // Price.
139 | "volume": "0.145642", // Amount.
140 | "time": "2020-07-09T14:13:01.000Z", // Time.
141 | "isBuyerMaker": true // Sell order is taker: true, Buy order is taker: false
142 | },
143 | ]
144 | }
145 | ```
146 |
147 | ---
148 |
149 | ### Fee
150 |
151 |
152 |
153 | This endpoint retrieves the trading [fee](./../glossary.md#fee).
154 |
155 | **Response is cached for:**
156 | _1 second_
157 |
158 | **Parameters:**
159 | NONE
160 |
161 | **Response:**
162 |
163 | ```json
164 | {
165 | "success": true,
166 | "message": null,
167 | "result": {
168 | "makerFee": "0.1", // Default maker fee (percent of trading amount in money currency)
169 | "takerFee": "0.1" // Default taker fee (percent of trading amount in money currency)
170 | }
171 | }
172 | ```
173 |
174 | ---
175 |
176 | ### Asset Status List
177 |
178 |
179 |
180 | This endpoint retrieves the [assets](./../glossary.md#assets) status.
181 |
182 | **Response is cached for:**
183 | _1 second_
184 |
185 | **Parameters:**
186 | NONE
187 |
188 | **Response:**
189 |
190 | ```json
191 | {
192 | "success": true,
193 | "message": null,
194 | "result": {
195 | "BTC": {
196 | "id": "4f37bc79-f612-4a63-9a81-d37f7f9ff622", // Asset id
197 | "lastUpdateTimestamp": "2020-07-10T13:20:07.000Z", // ISO 8601 time format of last update
198 | "name": "Bitcoin", // Name of currency
199 | "canWithdraw": true, // Is currency withdrawable
200 | "canDeposit": true, // Is currency depositable
201 | "minWithdrawal": "0.001", // Minimal amount to withdraw
202 | "maxWithdrawal": "0", // Maximum amount to withdraw
203 | "makerFee": "0.1", // Maker fee for currency
204 | "takerFee": "0.1" // Taker fee for currency
205 | },
206 | }
207 | }
208 | ```
209 |
210 | ---
211 |
212 | ### Orderbook
213 |
214 |
215 |
216 | This endpoint retrieves the current [order book](../glossary.md#order-book) as two arrays ([bids](./../glossary.md#bid) / [asks](./../glossary.md#ask)).
217 |
218 | **Response is cached for:**
219 | _100 ms_
220 |
221 | **Parameters:**
222 | NONE
223 |
224 | **Response:**
225 |
226 | ```json
227 | {
228 | "success": true,
229 | "message": null,
230 | "result": {
231 | "lastUpdateTimestamp": "2020-07-09T14:49:12.000Z", // Timestamp of last update
232 | "asks": [
233 | [
234 | "9431.9", // Price of lowest ask
235 | "0.705088" // Amount of lowest ask
236 | ],
237 | [
238 | "9433.67", // Price of the next ask
239 | "0.324509" // Amount of the next ask
240 | ],
241 | ],
242 | "bids": [
243 | [
244 | "9427.65", // Price of highest bid
245 | "0.547909" // Amount of highest bid
246 | ],
247 | [
248 | "9427.3", // Price of next bid
249 | "0.669249" // Amount of next bid
250 | ],
251 | ],
252 | },
253 | }
254 | ```
255 |
256 | ---
257 |
--------------------------------------------------------------------------------
/theme.config.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { DocsThemeConfig } from "nextra-theme-docs";
3 | import { useRouter } from "next/router";
4 | import seoConfig from "./seo.config";
5 | import dynamic from "next/dynamic";
6 |
7 | // Dynamically import to prevent SSR hydration mismatch (useRegion depends on client-side localStorage)
8 | const RegionSegmentedControl = dynamic(() => import("@/components/region-segmented-control").then(mod => ({ default: mod.RegionSegmentedControl })), {
9 | ssr: false,
10 | loading: () =>
// Placeholder to prevent layout shift
11 | });
12 |
13 | const config: DocsThemeConfig = {
14 | logo: (
15 |
16 | ),
17 | project: {
18 | link: "https://github.com/whitebit-exchange/api-docs",
19 | },
20 | docsRepositoryBase: "https://github.com/whitebit-exchange/api-docs/blob/main",
21 | color: { hue: 43, saturation: 79, lightness: 61 },
22 | backgroundColor: {
23 | light: "hsl(220, 14%, 98%)",
24 | dark: "hsl(240, 10%, 4%)",
25 | },
26 | footer: {
27 | component:
,
28 | },
29 | navbar: {
30 | extraContent: (
31 |
32 |
33 |
34 | ),
35 | },
36 | head: function useHead() {
37 | const { route, locale } = useRouter();
38 | const pagePath = route === "/" ? "index" : route.replace(/^\//, "");
39 |
40 | const pageConfig = seoConfig[pagePath] || {
41 | title: "WhiteBIT API Documentation",
42 | description:
43 | "Official WhiteBIT API documentation: integrate, trade, and access market data with ease using our developer-friendly guides and endpoints.",
44 | keywords: [
45 | "api",
46 | "cryptocurrency",
47 | "exchange",
48 | "trading",
49 | "documentation",
50 | ],
51 | category: "Documentation",
52 | section: "Overview",
53 | og: {
54 | title: "WhiteBIT API Docs",
55 | description: "Complete guide to WhiteBIT's cryptocurrency exchange API",
56 | },
57 | };
58 |
59 | const canonicalUrl = `https://docs.whitebit.com${route}`;
60 | const ogImageUrl = `/og-images/${
61 | pagePath === "index" ? "home" : pagePath
62 | }.png`;
63 | const currentDate = new Date().toISOString();
64 |
65 | return (
66 | <>
67 | {pageConfig.title}
68 |
72 |
73 | {/* Primary Meta Tags */}
74 |
75 |
76 |
83 |
84 |
85 |
86 |
87 | {/* Open Graph / Facebook */}
88 |
89 |
90 |
94 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | {pageConfig.keywords?.map((keyword, index) => (
105 |
106 | ))}
107 |
108 | {/* Twitter */}
109 |
110 |
111 |
112 |
116 |
120 |
121 |
122 |
123 | {/* PWA */}
124 |
125 |
126 |
127 |
131 |
135 |
136 |
137 |
138 | {/* Icons */}
139 |
140 |
141 |
147 |
153 |
154 |
155 | {/* Preconnect to important origins */}
156 |
157 |
158 |
159 | {/* Performance optimization */}
160 |
161 |
162 |
163 | {/* JSON-LD Structured Data */}
164 |
208 |
209 | {/* Breadcrumb Structured Data */}
210 |
244 | >
245 | );
246 | },
247 | navigation: false,
248 | };
249 |
250 | export default config;
251 |
--------------------------------------------------------------------------------
/components/pages/home-page.tsx:
--------------------------------------------------------------------------------
1 | import dynamic from "next/dynamic";
2 | import {
3 | ArrowRight,
4 | Zap,
5 | Shield,
6 | Globe,
7 | BarChart3,
8 | Code,
9 | Layers,
10 | Sparkles,
11 | Headphones,
12 | } from "lucide-react";
13 | import { Button } from "@/components/ui/button";
14 | import { motion } from "framer-motion";
15 | import Link from "next/link";
16 | import { getMainSiteUrl } from "@/lib/urls";
17 |
18 | const LogViewer = dynamic(() => import("@/components/log-viewer"), {
19 | ssr: false,
20 | loading: () => (
21 |
22 |
Loading API Activity...
23 |
24 | ),
25 | });
26 |
27 | export default function HomePage() {
28 | return (
29 |
30 | {/* Hero Section */}
31 |
32 |
33 |
34 |
35 |
41 |
42 | WhiteBIT API Platform
43 |
44 |
45 | Access the global cryptocurrency market with WhiteBIT's
46 | comprehensive trading APIs. Build powerful trading applications
47 | with our developer-friendly tools.
48 |
49 |
50 |
51 |
52 | Low Latency
53 |
54 |
55 |
56 | Global Access
57 |
58 |
59 |
60 | 24/7 Support
61 |
62 |
63 |
64 |
65 |
66 | Get Started
67 |
68 |
69 |
70 |
71 |
72 |
73 |
79 |
104 |
105 |
106 |
107 |
108 |
109 | {/* Features Grid */}
110 |
111 |
112 |
119 | Everything You Need
120 |
121 | Build powerful trading applications with our comprehensive suite
122 | of APIs and tools
123 |
124 |
125 |
126 | {[
127 | {
128 | icon: Zap,
129 | title: "Advanced Trading",
130 | description:
131 | "Full access to spot trading, margin trading, and futures markets with high-performance execution.",
132 | },
133 | {
134 | icon: BarChart3,
135 | title: "Real-time Data",
136 | description:
137 | "Live market data, order book updates, and trade execution via WebSocket with millisecond latency.",
138 | },
139 | {
140 | icon: Shield,
141 | title: "Enterprise Grade",
142 | description:
143 | "High-performance infrastructure with exceptional uptime and industry-leading security measures.",
144 | },
145 | {
146 | icon: Globe,
147 | title: "Global Compliance",
148 | description:
149 | "Built-in compliance tools and security features for safe trading across jurisdictions.",
150 | },
151 | {
152 | icon: Code,
153 | title: "Developer Tools",
154 | description:
155 | "Comprehensive SDKs, detailed documentation, and code examples to accelerate your development process.",
156 | },
157 | {
158 | icon: Layers,
159 | title: "Market Coverage",
160 | description:
161 | "Access to a wide range of spot and derivative markets with support for multiple trading pairs and instruments.",
162 | },
163 | ].map((feature, index) => (
164 |
172 |
173 |
174 |
175 | {feature.title}
176 | {feature.description}
177 |
178 | ))}
179 |
180 |
181 |
182 |
183 | {/* CTA Section */}
184 |
185 |
186 |
193 | Ready to Get Started?
194 |
195 | Join thousands of developers building with WhiteBIT's API platform
196 |
197 |
198 |
203 |
204 | Create API Key
205 |
206 |
207 |
208 |
209 |
210 | Read the Docs
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | );
219 | }
220 |
--------------------------------------------------------------------------------
/components/log-viewer.tsx:
--------------------------------------------------------------------------------
1 | 'use client'
2 |
3 | import { useEffect, useState } from "react";
4 | import { ApiLog, TradePayload } from "@/types/logs";
5 | import { ColumnDef } from "@tanstack/react-table";
6 | import { DataTable } from "@/components/ui/data-table";
7 | import { useQuery } from "@tanstack/react-query";
8 | import { Loader2 } from "lucide-react";
9 | import { useRegionConfig } from "@/lib/region-context";
10 |
11 | const ENDPOINTS = [
12 | "/api/v4/order/collateral/limit",
13 | "/api/v4/order/collateral/market",
14 | "/api/v4/order/collateral/stop-limit",
15 | "/api/v4/order/collateral/trigger-market",
16 | "/api/v4/order/modify",
17 | "/api/v4/order/new",
18 | "/api/v4/order/market",
19 | "/api/v4/order/stop_limit",
20 | "/api/v4/order/stop_market",
21 | "/api/v4/order/cancel",
22 | ];
23 |
24 | interface TickerData {
25 | [key: string]: {
26 | quote_volume: string;
27 | base_id: string;
28 | change: string;
29 | deal: string;
30 | high: string;
31 | last_price: string;
32 | low: string;
33 | open: string;
34 | quote_id: string;
35 | volume: string;
36 | };
37 | }
38 |
39 | async function fetchTopMarketsClient(apiBaseUrl: string): Promise<
40 | { market: string; last_price?: string }[]
41 | > {
42 | try {
43 | const response = await fetch(`${apiBaseUrl}/api/v4/public/ticker`, {
44 | method: 'GET',
45 | headers: {
46 | 'Accept': 'application/json',
47 | },
48 | signal: AbortSignal.timeout(5000), // 5 second timeout
49 | });
50 |
51 | if (!response.ok) {
52 | throw new Error(`HTTP error! status: ${response.status}`);
53 | }
54 |
55 | const data: TickerData = await response.json();
56 | // Sort and slice after collecting all markets
57 | return Object.entries(data)
58 | .reduce((acc, [market, marketData]) => {
59 | if (market.endsWith("_USDT") || market.endsWith("_PERP")) {
60 | acc.push({
61 | market,
62 | volume: parseFloat(marketData.quote_volume),
63 | last_price: marketData.last_price,
64 | });
65 | }
66 |
67 | return acc;
68 | }, [] as { market: string; last_price?: string; volume: number }[])
69 | .sort((a, b) => b.volume - a.volume)
70 | .slice(0, 20)
71 | .map((item) => ({ market: item.market, last_price: item.last_price }));
72 | } catch (error) {
73 | console.error("Failed to fetch markets:", error);
74 | return [
75 | { market: "BTC_USDT" },
76 | { market: "BTC_PERP" },
77 | { market: "WBT_USDT" },
78 | { market: "ETH_USDT" },
79 | { market: "ETH_PERP" },
80 | ]; // Updated fallback to only include USDT and PERP pairs
81 | }
82 | }
83 |
84 | function generatePayload(
85 | endpoint: string,
86 | markets: { market: string; last_price?: string }[]
87 | ): TradePayload {
88 | const marketData = markets[Math.floor(Math.random() * markets.length)];
89 | const side = Math.random() > 0.5 ? "buy" : "sell";
90 | const basePrice = parseFloat(marketData.last_price || "30000");
91 | const priceVariation = basePrice * 0.01; // 1% variation
92 | const randomPrice = basePrice + (Math.random() * 2 - 1) * priceVariation;
93 |
94 | const basePayload = {
95 | market: marketData.market,
96 | side,
97 | } as const;
98 |
99 | // Dynamic amount generation based on market and order type
100 | const getAmount = () => {
101 | const baseValue = Math.random() * 100000 + 5000; // Between 5k and 105k USDT
102 | const isLargeOrder = Math.random() < 0.1; // 10% chance of large order
103 | const multiplier = isLargeOrder ? Math.random() * 10 + 5 : 1; // 5x-15x for large orders
104 | return ((baseValue * multiplier) / basePrice).toFixed(6);
105 | };
106 |
107 | switch (endpoint) {
108 | case "/api/v4/order/collateral/limit":
109 | case "/api/v4/order/new":
110 | return {
111 | ...basePayload,
112 | amount: getAmount(),
113 | price: randomPrice.toFixed(2),
114 | timeInForce: Math.random() > 0.3 ? "GTC" : "IOC", // Mix of GTC and IOC orders
115 | };
116 | case "/api/v4/order/collateral/market":
117 | case "/api/v4/order/market":
118 | return {
119 | ...basePayload,
120 | amount: getAmount(),
121 | timeInForce: "IOC",
122 | };
123 | case "/api/v4/order/collateral/stop-limit":
124 | case "/api/v4/order/stop_limit":
125 | return {
126 | ...basePayload,
127 | amount: getAmount(),
128 | price: randomPrice.toFixed(2),
129 | stopPrice: (
130 | randomPrice *
131 | (1 + (side === "buy" ? 0.002 : -0.002))
132 | ).toFixed(2), // 0.2% stop distance
133 | type: "stop_limit",
134 | };
135 | case "/api/v4/order/collateral/trigger-market":
136 | case "/api/v4/order/stop_market":
137 | return {
138 | ...basePayload,
139 | amount: getAmount(),
140 | activation_price: (
141 | randomPrice *
142 | (1 + (side === "buy" ? 0.002 : -0.002))
143 | ).toFixed(2),
144 | type: "stop",
145 | };
146 | case "/api/v4/order/modify":
147 | return {
148 | ...basePayload,
149 | amount: getAmount(),
150 | price: randomPrice.toFixed(2),
151 | timeInForce: "GTC",
152 | };
153 | case "/api/v4/order/cancel":
154 | return {
155 | market: marketData.market,
156 | orderId: Math.floor(Math.random() * 1000000000).toString(),
157 | side,
158 | };
159 | default:
160 | return basePayload;
161 | }
162 | }
163 |
164 | function formatTime(date: string): string {
165 | const dateObj = new Date(date);
166 | const hours = dateObj.getHours().toString().padStart(2, "0");
167 | const minutes = dateObj.getMinutes().toString().padStart(2, "0");
168 | const seconds = dateObj.getSeconds().toString().padStart(2, "0");
169 | const ms = dateObj.getMilliseconds().toString().padStart(3, "0");
170 |
171 | return `${hours}:${minutes}:${seconds}.${ms}`;
172 | }
173 |
174 | function generateBatch(
175 | size: number,
176 | markets: { market: string; last_price?: string }[]
177 | ): ApiLog[] {
178 | const baseTime = new Date();
179 |
180 | return Array.from({ length: size }, () => {
181 | const endpoint = ENDPOINTS[Math.floor(Math.random() * ENDPOINTS.length)];
182 | const randomOffset = Math.random() * 100;
183 | const timestamp = new Date(baseTime.getTime() - randomOffset);
184 |
185 | // Add weighted random status code generation
186 | const random = Math.random();
187 | const statusCode =
188 | random < 0.97
189 | ? 200 // 97% success
190 | : random < 0.99
191 | ? 400 // 2% bad request
192 | : random < 0.995
193 | ? 429 // 0.5% rate limit
194 | : 401; // 0.5% unauthorized
195 |
196 | return {
197 | id: timestamp.toString(),
198 | timestamp: timestamp.toISOString(),
199 | path: endpoint,
200 | payload: generatePayload(endpoint, markets),
201 | statusCode,
202 | };
203 | }).sort(
204 | (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
205 | );
206 | }
207 |
208 | export default function LogViewer({ className }: { className?: string }) {
209 | const [logs, setLogs] = useState([]);
210 | const regionConfig = useRegionConfig();
211 |
212 | // Client-side detection to prevent SSR execution
213 | const [isClient, setIsClient] = useState(false);
214 |
215 | useEffect(() => {
216 | setIsClient(true);
217 | }, []);
218 |
219 | // Fetch markets only on client-side with fallback data for SSR
220 | const { data: markets = [], isLoading } = useQuery({
221 | queryKey: ["markets", regionConfig.apiBaseUrl],
222 | queryFn: () => fetchTopMarketsClient(regionConfig.apiBaseUrl),
223 | enabled: isClient, // Only run on client-side
224 | staleTime: 60000, // Cache for 1 minute
225 | refetchOnWindowFocus: false, // Don't refetch on tab focus
226 | initialData: [
227 | // Fallback data prevents query execution during SSR
228 | { market: "BTC_USDT" },
229 | { market: "BTC_PERP" },
230 | { market: "WBT_USDT" },
231 | { market: "ETH_USDT" },
232 | { market: "ETH_PERP" },
233 | ],
234 | });
235 |
236 | useEffect(() => {
237 | if (markets.length === 0) return;
238 |
239 | const interval = setInterval(() => {
240 | const batchSize = Math.floor(Math.random() * 8) + 6; // 6-14 orders per batch
241 |
242 | setLogs((prevLogs) => {
243 | const newBatch = generateBatch(batchSize, markets);
244 | return [...newBatch, ...prevLogs].slice(0, 1000);
245 | });
246 | }, 128);
247 |
248 | return () => {
249 | clearInterval(interval);
250 | };
251 | }, [markets]); // Fixed: removed logs from dependencies
252 |
253 | if (isLoading) {
254 | return (
255 |
256 |
257 |
258 | );
259 | }
260 |
261 | const columns: ColumnDef[] = [
262 | {
263 | accessorKey: "timestamp",
264 | header: "Time",
265 | cell: ({ row }) => {formatTime(row.getValue("timestamp"))} ,
266 | minSize: 100,
267 | size: 100,
268 | maxSize: 100,
269 | },
270 | {
271 | accessorKey: "statusCode",
272 | header: "Code",
273 | cell: ({ row }) => {
274 | const status = row.getValue("statusCode") as number;
275 | return (
276 |
287 | {status}
288 |
289 | );
290 | },
291 | minSize: 30,
292 | size: 30,
293 | maxSize: 30,
294 | },
295 | {
296 | accessorKey: "path",
297 | header: "Endpoint",
298 | cell: ({ row }) => (
299 |
300 |
301 | {row.original.path}
302 |
303 |
304 | ),
305 | minSize: 360,
306 | size: 360,
307 | maxSize: 360,
308 | },
309 | {
310 | accessorKey: "payload",
311 | header: "Payload",
312 | cell: ({ row }) => {
313 | const payload = row.original.payload;
314 | return (
315 |
316 |
323 | {payload.side.toUpperCase()}
324 |
325 | {JSON.stringify(payload)}
326 |
327 | );
328 | },
329 | minSize: 520,
330 | size: 520,
331 | maxSize: 520,
332 | },
333 | ];
334 |
335 | return (
336 |
342 | );
343 | }
344 |
--------------------------------------------------------------------------------
/pages/platform/webhook.mdx:
--------------------------------------------------------------------------------
1 | # Webhook HTTP API
2 |
3 | - [How to use](#how-to-use)
4 | - [Requirements](#requirements)
5 | - [Webhook methods](#webhook-methods)
6 | - [WhiteBIT withdraw from main balance](#whitebit-withdraw-from-main-balance)
7 |
8 | ## How to use
9 |
10 | 1. Go to your account on whitebit.com.
11 | 2. Click on the API keys tab.
12 | 3. Select the web-hook configuration tab for your API keys.
13 | 4. Paste correct URI to your web server which will process web-hook calls.
14 | 5. Press Generate a new key button and toggle the activation switcher to "Activated".
15 |
16 | ⚠️ Please pay attention that secret key will be shown only once, so make sure you save it in any secure key store
17 |
18 | ## Requirements:
19 |
20 | ### For web hook keys generation
21 |
22 | Before starting using webhooks, you'll be asked to verify ownership of the domain, you are set as webhook destination. You can do it in one of three ways
23 |
24 | 1. You can add TXT DNS record to your domain with your webhook public key.
25 | 2. You can add plain text file `whiteBIT-verification.txt` into your root domain folder and provide public web access to this file from your server. In this file should be placed your public webhook key.
26 | 3. You can implement `/whiteBIT-verification` endpoint. This endpoint should respond with 200 OK and return JSON array which contains your public webhook key. For example: `[""]`
27 |
28 | Passing just one of these checks will be able you to switch webhook on
29 |
30 | ### For processing web-hook requests
31 |
32 | All web hook requests are performing using POST method and with application/json content type. Consumer server should respond with 200 HTTP status code. If consumer was unable to handle web-hook, the request will be retry every 10 minutes but not more than 5 times.
33 |
34 | #### Body data
35 |
36 | All web-hook requests are performing with
37 |
38 | ```json
39 | {
40 | "method": "string",
41 | "params": {
42 | "nonce": 0
43 | },
44 | "id": "uniqueID"
45 | }
46 | ```
47 |
48 | **method** - string. The name of method which was evaluated. Web hooks API supports such web-hook methods:
49 |
50 | - **code.apply**. Performs when code owned by a customer was applied.
51 |
52 | **id** - string. Uuid to identify every request.
53 |
54 | **params** - the request payload. Here you can find useful data about passed actions, which triggered web hook call. Also in this field placed a [nonce](./../glossary.md#nonce). **'nonce'** - a number that is always **greater** than the previous request’s nonce number
55 |
56 | #### Request headers
57 |
58 | Also, all request contains additional data in headers:
59 |
60 | 1. `'Content-type': 'application/json'`
61 | 2. `'X-TXC-APIKEY': api_key` - where api_key is your WhiteBit webhook API key
62 | 3. `'X-TXC-PAYLOAD': payload'` - where payload is base64-encoded body data
63 | 4. `'X-TXC-SIGNATURE': signature` - where signature is `hex(HMAC_SHA512(payload), key=api_secret))`
64 |
65 | On consumer side you can process security headers to be sure request was performed by WhiteBIT.
66 |
67 | ## WebHook Methods
68 |
69 | ### WhiteBIT code apply
70 |
71 | Performed when [code](./../glossary.md#whitebit-codes) was applied. Request example:
72 |
73 | ```json
74 | {
75 | "method": "code.apply",
76 | "params": {
77 | "code": "",
78 | "nonce": 1
79 | },
80 | "id": "45a1d85d-2fdf-483e-8dfa-6d253148c730"
81 | }
82 | ```
83 |
84 | ### WhiteBIT deposit to main balance
85 |
86 | Performed when deposit was accepted. Request example:
87 |
88 | ```json
89 | {
90 | "method": "deposit.accepted",
91 | "params": {
92 | "address": "wallet address", // deposit address
93 | "amount": "0.000600000000000000", // amount of deposit
94 | "createdAt": 1593437922, // timestamp of deposit
95 | "currency": "Tether US", // deposit currency
96 | "description": "", // deposit description
97 | "fee": "0.000000000000000000", // deposit fee
98 | "memo": "", // deposit memo
99 | "method": 1, // called method 1 - deposit, 2 - withdraw
100 | "network": "ERC20", // if currency is multi network
101 | "status": 15, // transactions status
102 | "ticker": "USDT_ETH", // deposit currency ticker
103 | "transactionHash": "transaction hash", // deposit transaction hash
104 | "uniqueId": null, // unique Id of deposit
105 | "confirmations": {
106 | // if transaction has confirmations info it will display here
107 | "actual": 1, // current block confirmations
108 | "required": 2 // required block confirmation for successful deposit
109 | }
110 | },
111 | "id": "uuid"
112 | }
113 | ```
114 |
115 | Performed when deposit was update. Request example:
116 |
117 | ```json
118 | {
119 | "method": "deposit.updated",
120 | "params": {
121 | "address": "wallet address", // deposit address
122 | "amount": "0.000600000000000000", // amount of deposit
123 | "createdAt": 1593437922, // timestamp of deposit
124 | "currency": "Tether US", // deposit currency
125 | "description": "update", // deposit description
126 | "fee": "0.000000000000000000", // deposit fee
127 | "memo": "", // deposit memo
128 | "network": "ERC20", // if currency is multi network
129 | "status": 15, // transactions status
130 | "ticker": "USDT_ETH", // deposit currency ticker
131 | "transactionHash": "transaction hash", // deposit transaction hash
132 | "uniqueId": null, // unique Id of deposit
133 | "confirmations": {
134 | // if transaction has confirmations info it will display here
135 | "actual": 1, // current block confirmations
136 | "required": 2 // required block confirmation for successful deposit
137 | }
138 | },
139 | "id": "uuid"
140 | }
141 | ```
142 |
143 | Performed when deposit was processed, so it is available on your balance. Request example:
144 |
145 | ```json
146 | {
147 | "method": "deposit.processed",
148 | "params": {
149 | "address": "wallet address", // deposit address
150 | "amount": "0.000600000000000000", // amount of deposit
151 | "createdAt": 1593437922, // timestamp of deposit
152 | "currency": "Tether US", // deposit currency
153 | "description": "", // deposit description
154 | "fee": "0.000000000000000000", // deposit fee
155 | "memo": "", // deposit memo
156 | "method": 1, // called method 1 - deposit, 2 - withdraw
157 | "network": "ERC20", // if currency is multi network
158 | "status": 15, // transactions status
159 | "ticker": "USDT_ETH", // deposit currency ticker
160 | "transactionHash": "transaction hash", // deposit transaction hash
161 | "uniqueId": null, // unique Id of deposit
162 | "confirmations": {
163 | // if transaction has confirmations info it will display here
164 | "actual": 1, // current block confirmations
165 | "required": 2 // required block confirmation for successful deposit
166 | }
167 | },
168 | "id": "uuid"
169 | }
170 | ```
171 |
172 | Performed when deposit was canceled. Request example:
173 |
174 | ```json
175 | {
176 | "method": "deposit.canceled",
177 | "params": {
178 | "address": "wallet address", // deposit address
179 | "amount": "100.00", // amount of deposit
180 | "createdAt": 1593437922, // timestamp of deposit
181 | "currency": "Tether US", // deposit currency
182 | "description": "", // deposit description
183 | "fee": "0.000000000000000000", // deposit fee
184 | "memo": "", // deposit memo
185 | "method": 1, // called method 1 - deposit, 2 - withdraw
186 | "network": "ERC20", // if currency is multi network, "null" if no multi network
187 | "status": 15, // transactions status
188 | "ticker": "USDT_ETH", // deposit currency ticker
189 | "transactionHash": "transaction hash", // deposit transaction hash
190 | "uniqueId": null, // unique Id of deposit
191 | "confirmations": {
192 | // if transaction has confirmations info it will display here
193 | "actual": 1, // current block confirmations
194 | "required": 32 // required block confirmation for successful deposit
195 | }
196 | },
197 | "id": "uuid"
198 | }
199 | ```
200 |
201 | Deposit status codes:
202 |
203 | - Pending - 15
204 |
205 | ### WhiteBIT withdraw from main balance
206 |
207 | Performed when withdraw was created. Request example:
208 |
209 | ```json
210 | {
211 | "method": "withdraw.unconfirmed",
212 | "params": {
213 | "address": "wallet address", // withdraw address
214 | "amount": "100.00", // amount of withdraw
215 | "createdAt": 1593437922, // timestamp of withdraw
216 | "currency": "Tether US", // withdraw currency
217 | "ticker": "USDT", // withdraw currency ticker
218 | "description": null, // withdraw description
219 | "fee": "0.000000000000000000", // withdraw fee
220 | "memo": "", // withdraw memo
221 | "method": 2, // called method 1 - deposit, 2 - withdraw
222 | "network": "TRC20", // if currency is multi network, "null" if no multi network
223 | "status": 15, // transactions status
224 | "transactionHash": "transaction hash", // withdraw transaction hash
225 | "uniqueId": null // unique Id of withdraw
226 | },
227 | "id": "uuid"
228 | }
229 | ```
230 |
231 | Performed when withdraw is pending. Request example:
232 |
233 | ```json
234 | {
235 | "method": "withdraw.pending",
236 | "params": {
237 | "address": "wallet address", // withdraw address
238 | "amount": "100.00", // amount of withdraw
239 | "createdAt": 1593437922, // timestamp of withdraw
240 | "currency": "Tether US", // withdraw currency
241 | "ticker": "USDT", // withdraw currency ticker
242 | "description": null, // withdraw description
243 | "fee": "0.000000000000000000", // withdraw fee
244 | "memo": "", // withdraw memo
245 | "method": 2, // called method 1 - deposit, 2 - withdraw
246 | "network": "TRC20", // if currency is multi network, "null" if no multi network
247 | "status": 15, // transactions status
248 | "transactionHash": "transaction hash", // withdraw transaction hash
249 | "uniqueId": null // unique Id of withdraw
250 | },
251 | "id": "uuid"
252 | }
253 | ```
254 |
255 | Performed when withdraw was canceled. Request example:
256 |
257 | ```json
258 | {
259 | "method": "withdraw.canceled",
260 | "params": {
261 | "address": "wallet address", // withdraw address
262 | "amount": "100.00", // amount of withdraw
263 | "createdAt": 1593437922, // timestamp of withdraw
264 | "currency": "Tether US", // withdraw currency
265 | "ticker": "USDT", // withdraw currency ticker
266 | "description": null, // withdraw description
267 | "fee": "0.000000000000000000", // withdraw fee
268 | "memo": "", // withdraw memo
269 | "method": 2, // called method 1 - deposit, 2 - withdraw
270 | "network": "TRC20", // if currency is multi network, "null" if no multi network
271 | "status": 15, // transactions status
272 | "transactionHash": "transaction hash", // withdraw transaction hash
273 | "uniqueId": null // unique Id of withdraw
274 | },
275 | "id": "uuid"
276 | }
277 | ```
278 |
279 | Performed when withdraw was completed. Request example:
280 |
281 | ```json
282 | {
283 | "method": "withdraw.successful",
284 | "params": {
285 | "address": "wallet address", // withdraw address
286 | "amount": "100.00", // amount of withdraw
287 | "createdAt": 1593437922, // timestamp of withdraw
288 | "currency": "Tether US", // withdraw currency
289 | "ticker": "USDT", // withdraw currency ticker
290 | "description": null, // withdraw description
291 | "fee": "0.000000000000000000", // withdraw fee
292 | "memo": "", // withdraw memo
293 | "method": 2, // called method 1 - deposit, 2 - withdraw
294 | "network": "TRC20", // if currency is multi network, "null" if no multi network
295 | "status": 15, // transactions status
296 | "transactionHash": "transaction hash", // withdraw transaction hash
297 | "uniqueId": null // unique Id of withdraw
298 | },
299 | "id": "uuid"
300 | }
301 | ```
302 |
--------------------------------------------------------------------------------
/seo.config.ts:
--------------------------------------------------------------------------------
1 | interface SEOConfig {
2 | // used for the title of the page
3 | title: string;
4 | // used for the description of the page
5 | description: string;
6 | // used for generating the ogImage:image of the page
7 | ogImage: {
8 | title: string;
9 | description: string;
10 | };
11 | keywords?: string[];
12 | category?: string;
13 | section?: string;
14 | canonicalPath?: string;
15 | }
16 |
17 | interface PageSEOConfig {
18 | [key: string]: SEOConfig;
19 | }
20 |
21 | const seoConfig: PageSEOConfig = {
22 | index: {
23 | title: "WhiteBIT API Documentation | Cryptocurrency Exchange",
24 | description: "Comprehensive guide to WhiteBIT API. Access market data, trading endpoints, WebSockets, and authentication guides for seamless integration.",
25 | keywords: ["api", "cryptocurrency", "exchange", "trading", "documentation", "whitebit"],
26 | category: "Documentation",
27 | section: "Overview",
28 | ogImage: {
29 | title: "WhiteBIT API Docs",
30 | description: "Complete guide to cryptocurrency exchange API"
31 | }
32 | },
33 | changelog: {
34 | title: "API Changelog | WhiteBIT API Documentation",
35 | description: "Track all updates, improvements, and upcoming changes to the WhiteBIT API platform. Stay informed about new features, deprecations, and platform evolution.",
36 | keywords: ["changelog", "api updates", "release notes", "api changes", "platform updates", "whitebit"],
37 | category: "Platform",
38 | section: "Updates",
39 | ogImage: {
40 | title: "API Changelog",
41 | description: "Track API platform evolution"
42 | }
43 | },
44 | "guides/client-order-id": {
45 | title: "Client Order ID Guide | WhiteBIT API Documentation",
46 | description: "Complete guide to using clientOrderId for order tracking and strategy identification. Includes naming conventions and best practices.",
47 | keywords: ["client order id", "order tracking", "trading strategy", "api guide"],
48 | category: "Guides",
49 | section: "Trading",
50 | ogImage: {
51 | title: "Client Order ID Guide",
52 | description: "Track orders & identify trading strategies"
53 | }
54 | },
55 | "guides/fireblocks": {
56 | title: "Fireblocks Integration Guide | WhiteBIT API Documentation",
57 | description: "Step-by-step instructions for connecting your WhiteBIT account to Fireblocks via API, including API key generation and security best practices.",
58 | keywords: ["fireblocks", "integration", "api", "whitebit", "security", "api key"],
59 | category: "Guides",
60 | section: "Integrations",
61 | ogImage: {
62 | title: "Fireblocks Integration Guide",
63 | description: "Connect WhiteBIT to Fireblocks via API"
64 | }
65 | },
66 | "platform/overview": {
67 | title: "API Overview | WhiteBIT API Documentation",
68 | description: "Complete overview of WhiteBIT's API including public/private endpoints, WebSockets, webhooks, and authentication requirements.",
69 | keywords: ["api overview", "endpoints", "websockets", "webhooks", "authentication"],
70 | category: "Platform",
71 | section: "Overview",
72 | ogImage: {
73 | title: "API Overview",
74 | description: "Endpoints, WebSockets & authentication"
75 | }
76 | },
77 | // Platform features
78 | "platform/self-trade-prevention": {
79 | title: "Self Trade Prevention (STP) | WhiteBIT API Documentation",
80 | description: "Complete guide to implementing STP in your trading systems. Includes modes, supported endpoints, and best practices.",
81 | keywords: ["self trade prevention", "stp", "algorithmic trading", "trading modes"],
82 | category: "Platform",
83 | section: "Features",
84 | ogImage: {
85 | title: "Self Trade Prevention",
86 | description: "STP modes for algorithmic trading"
87 | }
88 | },
89 | "platform/colocation": {
90 | title: "Colocation Services | WhiteBIT API Documentation",
91 | description: "Complete guide to professional colocation for high-frequency trading. Execute trades with ultra-low latency through direct exchange connection.",
92 | keywords: ["colocation", "low latency", "high frequency trading", "hft"],
93 | category: "Platform",
94 | section: "Features",
95 | ogImage: {
96 | title: "Colocation Services",
97 | description: "Ultra-low latency trading"
98 | }
99 | },
100 | // Private API endpoints
101 | "private/http-main-v4": {
102 | title: "Main Balance API | WhiteBIT API Documentation",
103 | description: "Complete reference for private HTTP API V4 balance operations. Learn how to manage deposits, withdrawals, and account balances.",
104 | keywords: ["balance api", "deposits", "withdrawals", "v4 api"],
105 | category: "API Reference",
106 | section: "Private",
107 | ogImage: {
108 | title: "Main Balance API V4",
109 | description: "Deposits, withdrawals & balances"
110 | }
111 | },
112 | "private/http-trade-v4": {
113 | title: "Trading API V4 | WhiteBIT API Documentation",
114 | description: "Complete reference for V4 trading endpoints. Learn how to place, modify and cancel orders, retrieve history, and manage positions.",
115 | keywords: ["trading api", "order management", "v4 api", "positions"],
116 | category: "API Reference",
117 | section: "Private",
118 | ogImage: {
119 | title: "Trading API V4",
120 | description: "Order management & positions"
121 | }
122 | },
123 | "private/http-trade-v1": {
124 | title: "Trading API V1 | WhiteBIT API Documentation",
125 | description: "Complete documentation for legacy V1 trading API. Reference guide for maintaining existing integrations with code examples.",
126 | keywords: ["trading api", "legacy api", "v1 api", "integration"],
127 | category: "API Reference",
128 | section: "Private",
129 | ogImage: {
130 | title: "Trading API V1",
131 | description: "Legacy trading endpoints"
132 | }
133 | },
134 | "private/http-auth": {
135 | title: "API Authentication | WhiteBIT API Documentation",
136 | description: "Complete guide for implementing secure authentication. Learn to generate signatures, manage API keys, and secure requests.",
137 | keywords: ["authentication", "api keys", "signatures", "security"],
138 | category: "API Reference",
139 | section: "Private",
140 | ogImage: {
141 | title: "API Authentication",
142 | description: "Keys, signatures & secure requests"
143 | }
144 | },
145 | "private/websocket": {
146 | title: "Private WebSocket API | WhiteBIT API Documentation",
147 | description: "Complete guide for private WebSocket API implementation. Learn to subscribe to real-time account updates and order executions.",
148 | keywords: ["websocket", "real-time", "private api", "streaming"],
149 | category: "API Reference",
150 | section: "Private",
151 | ogImage: {
152 | title: "Private WebSocket API",
153 | description: "Real-time account updates"
154 | }
155 | },
156 | // Public API endpoints
157 | "public/http-v4": {
158 | title: "Public HTTP API V4 | WhiteBIT API Documentation",
159 | description: "Complete guide to accessing market data, tickers, orderbooks, and trading pairs through V4 public endpoints. No authentication required.",
160 | keywords: ["market data", "public api", "v4 api", "orderbook"],
161 | category: "API Reference",
162 | section: "Public",
163 | ogImage: {
164 | title: "Public HTTP API V4",
165 | description: "Market data & trading pairs"
166 | }
167 | },
168 | "public/http-v2": {
169 | title: "Public HTTP API V2 | WhiteBIT API Documentation",
170 | description: "Complete documentation for V2 public API endpoints. Reference guide for existing integrations with response formats.",
171 | keywords: ["public api", "v2 api", "legacy api", "integration"],
172 | category: "API Reference",
173 | section: "Public",
174 | ogImage: {
175 | title: "Public HTTP API V2",
176 | description: "Legacy public endpoints"
177 | }
178 | },
179 | "public/http-v1": {
180 | title: "Public HTTP API V1 | WhiteBIT API Documentation",
181 | description: "Complete documentation for legacy V1 public API. Historical reference for first-generation endpoints with examples.",
182 | keywords: ["public api", "v1 api", "legacy api", "reference"],
183 | category: "API Reference",
184 | section: "Public",
185 | ogImage: {
186 | title: "Public HTTP API V1",
187 | description: "First-generation endpoints"
188 | }
189 | },
190 | "public/websocket": {
191 | title: "Public WebSocket API | WhiteBIT API Documentation",
192 | description: "Complete guide for public WebSocket API. Learn to stream real-time market data including orderbooks, trades, and price tickers.",
193 | keywords: ["websocket", "market data", "streaming", "public api"],
194 | category: "API Reference",
195 | section: "Public",
196 | ogImage: {
197 | title: "Public WebSocket API",
198 | description: "Stream market data in real-time"
199 | }
200 | },
201 | // Webhook documentation
202 | "platform/webhook": {
203 | title: "Webhooks Integration | WhiteBIT API Documentation",
204 | description: "Complete guide to implementing webhooks for exchange events. Learn to receive notifications for account activities and orders.",
205 | keywords: ["webhooks", "notifications", "integration", "events"],
206 | category: "Platform",
207 | section: "Features",
208 | ogImage: {
209 | title: "Webhooks Integration",
210 | description: "Exchange event notifications"
211 | }
212 | },
213 | // OAuth documentation
214 | "platform/oauth": {
215 | title: "OAuth 2.0 Authentication | WhiteBIT API Documentation",
216 | description: "Complete guide to WhiteBIT's OAuth 2.0 for third-party authentication. Learn about authorization flows and token management.",
217 | keywords: ["oauth", "authentication", "authorization", "integration"],
218 | category: "Platform",
219 | section: "Features",
220 | ogImage: {
221 | title: "OAuth 2.0 Authentication",
222 | description: "Third-party integration flows"
223 | }
224 | },
225 | "platform/oauth/usage": {
226 | title: "OAuth 2.0 API Reference | WhiteBIT API Documentation",
227 | description: "Complete implementation guide for OAuth 2.0 with authorization flows, token management, scopes, and account endpoints.",
228 | keywords: ["oauth", "api reference", "authorization", "scopes"],
229 | category: "Platform",
230 | section: "Features",
231 | ogImage: {
232 | title: "OAuth 2.0 API Reference",
233 | description: "Authorization flows & tokens"
234 | }
235 | },
236 | // General pages
237 | faq: {
238 | title: "FAQ | WhiteBIT API Documentation",
239 | description: "Complete answers to common questions about API integration, rate limits, authentication, best practices, and troubleshooting.",
240 | keywords: ["faq", "questions", "troubleshooting", "help"],
241 | category: "Support",
242 | section: "Help",
243 | ogImage: {
244 | title: "API FAQ",
245 | description: "Common questions answered"
246 | }
247 | },
248 | glossary: {
249 | title: "API Glossary | WhiteBIT API Documentation",
250 | description: "Complete glossary of trading terms, API concepts, and cryptocurrency terminology used in our documentation.",
251 | keywords: ["glossary", "terminology", "definitions", "terms"],
252 | category: "Support",
253 | section: "Help",
254 | ogImage: {
255 | title: "API Glossary",
256 | description: "Trading & API terminology"
257 | }
258 | },
259 | sdks: {
260 | title: "API Libraries & SDKs | WhiteBIT API Documentation",
261 | description: "Complete overview of official SDKs and libraries for WhiteBIT API integration in Python, JavaScript, PHP, and other languages.",
262 | keywords: ["sdk", "libraries", "integration", "development"],
263 | category: "Support",
264 | section: "Development",
265 | ogImage: {
266 | title: "API Libraries & SDKs",
267 | description: "Official integration tools"
268 | }
269 | },
270 | };
271 |
272 | export default seoConfig;
273 |
--------------------------------------------------------------------------------
/public/data/changelog.json:
--------------------------------------------------------------------------------
1 | {
2 | "changes": [
3 | {
4 | "title": "Documentation Updates",
5 | "timeframe": "2025-09-22",
6 | "changes": [
7 | {
8 | "title": "Clarified Book Ticker stream update interval",
9 | "description": "Refined the description of the Book Ticker WebSocket stream to explicitly state instant BBO snapshot with incremental updates.",
10 | "type": "websocket",
11 | "links": [
12 | {
13 | "title": "ws - Book Ticker",
14 | "url": "/public/websocket/#book-ticker"
15 | }
16 | ]
17 | }
18 | ]
19 | },
20 | {
21 | "title": "Flex Earn Endpoints",
22 | "timeframe": "2025-09-22",
23 | "changes": [
24 | {
25 | "title": "Introduced Smart Flex private endpoints",
26 | "description": "Added documentation for Smart Flex (Flex Earn) endpoints: list plans, manage investments, view histories, and configure auto-reinvestment.",
27 | "type": "feature",
28 | "links": [
29 | { "title": "v4 - Get Flex Plans", "url": "/private/http-main-v4/#get-flex-plans" },
30 | { "title": "v4 - Get User Flex Investments", "url": "/private/http-main-v4/#get-user-flex-investments" },
31 | { "title": "v4 - Get Flex Investment History", "url": "/private/http-main-v4/#get-flex-investment-history" },
32 | { "title": "v4 - Get Flex Payment History", "url": "/private/http-main-v4/#get-flex-payment-history" },
33 | { "title": "v4 - Create Flex Investment", "url": "/private/http-main-v4/#create-flex-investment" },
34 | { "title": "v4 - Withdraw from Flex Investment", "url": "/private/http-main-v4/#withdraw-from-flex-investment" },
35 | { "title": "v4 - Close Flex Investment", "url": "/private/http-main-v4/#close-flex-investment" },
36 | { "title": "v4 - Update Flex Auto-Reinvestment", "url": "/private/http-main-v4/#update-flex-auto-reinvestment" }
37 | ]
38 | }
39 | ]
40 | },
41 | {
42 | "title": "Trade Engine Update",
43 | "timeframe": "2025-06-10",
44 | "changes": [
45 | {
46 | "title": "Event time and update id introduced in WebSocket events",
47 | "description": "WebSocket events now include `event_time` and `update_id` fields. The `event_time` field provides the timestamp of the event, while the `update_id` field is a unique identifier for each update, allowing for better tracking and synchronization of events.",
48 | "type": "websocket",
49 | "links": [
50 | {
51 | "title": "ws - Market Depth Subscribe",
52 | "url": "/public/websocket/#subscribe-5"
53 | }
54 | ]
55 | },
56 | {
57 | "title": "Hedge mode support",
58 | "description": "The Hedge mode is now available for all users. This mode allows you to hold both long and short positions in the same asset simultaneously, providing greater flexibility in managing your trades.",
59 | "type": "trading",
60 | "links": [
61 | {
62 | "title": "v4 - Collateral Limit Order",
63 | "url": "/private/http-trade-v4/#collateral-limit-order"
64 | },
65 | {
66 | "title": "v4 - Collateral Stop Limit Order",
67 | "url": "/private/http-trade-v4/#collateral-stop-limit-order"
68 | },
69 | {
70 | "title": "v4 - Collateral Market Order",
71 | "url": "/private/http-trade-v4/#collateral-market-order"
72 | },
73 | {
74 | "title": "v4 - Collateral OCO Order",
75 | "url": "/private/http-trade-v4/#create-collateral-oco-order"
76 | },
77 | {
78 | "title": "v4 - Collateral Trigger Market Order",
79 | "url": "/private/http-trade-v4/#collateral-trigger-market-order"
80 | },
81 | {
82 | "title": "v4 - Collateral Account Hedge Mode",
83 | "url": "/private/http-trade-v4/#collateral-account-hedge-mode"
84 | },
85 | {
86 | "title": "v4 - Update Collateral Account Hedge Mode",
87 | "url": "/private/http-trade-v4/#update-collateral-account-hedge-mode"
88 | },
89 | {
90 | "title": "ws - Orders Pending WebSocket",
91 | "url": "/private/websocket/#orders-pending"
92 | },
93 | {
94 | "title": "ws - Orders Executed",
95 | "url": "/private/websocket/#orders-executed"
96 | },
97 | {
98 | "title": "ws - Orders Executed",
99 | "url": "/private/websocket/#orders-executed"
100 | },
101 | {
102 | "title": "ws - Orders Executed",
103 | "url": "/private/websocket/#orders-executed"
104 | },
105 | {
106 | "title": "ws - Positions",
107 | "url": "/private/websocket/#positions"
108 | }
109 | ]
110 | },
111 | {
112 | "title": "Queries Limited to the Last 6 Months",
113 | "description": "To enhance performance and focus on the most relevant data, our API will soon limit data queries to the past 6 months. This means you will be able to retrieve data up to six months old from the current date. ",
114 | "type": "performance",
115 | "links": [
116 | {
117 | "title": "v4 - Convert History",
118 | "url": "/private/http-trade-v4/#convert-history"
119 | },
120 | {
121 | "title": "v4 - Executed order deals",
122 | "url": "/private/http-trade-v4/#query-executed-order-deals"
123 | },
124 | {
125 | "title": "v4 - Executed orders",
126 | "url": "/private/http-trade-v4/#query-executed-orders"
127 | },
128 | {
129 | "title": "v4 - Unexecuted (active) conditional orders",
130 | "url": "/private/http-trade-v4/#query-unexecutedactive-conditional-orders"
131 | },
132 | {
133 | "title": "v4 - Collateral bulk limit order",
134 | "url": "/private/http-trade-v4/#collateral-bulk-limit-order"
135 | },
136 | {
137 | "title": "v4 - Executed order history",
138 | "url": "/private/http-trade-v4/#query-executed-order-history"
139 | },
140 | {
141 | "title": "v4 - Positions history",
142 | "url": "/private/http-trade-v4/#positions-history"
143 | },
144 | {
145 | "title": "v1 - Order history",
146 | "url": "/private/http-trade-v1/#query-order-history"
147 | },
148 | {
149 | "title": "v1 - Executed History by single market",
150 | "url": "/private/http-trade-v1/#query-executed-order-history-by-single-market"
151 | },
152 | {
153 | "title": "v1 - Executed History by all markets",
154 | "url": "/private/http-trade-v1/#query-executed-order-history-by-all-markets"
155 | }
156 | ]
157 | }
158 | ]
159 | },
160 | {
161 | "title": "Withdraw update",
162 | "timeframe": "2025-05-29",
163 | "changes": [
164 | {
165 | "title": "HTTP Main V4 API - Withdraw",
166 | "description": "The `paymentDescription` field is now mandatory for withdrawal requests from whitebit-tr.com.",
167 | "type": "withdrawal",
168 | "links": [
169 | {
170 | "title": "Withdraw",
171 | "url": "/private/http-main-v4/#create-withdraw-request"
172 | }
173 | ]
174 | }
175 | ]
176 | },
177 | {
178 | "title": "Convert History Update",
179 | "timeframe": "2025-05-15",
180 | "changes": [
181 | {
182 | "title": "HTTP Trade V4 API - Convert",
183 | "description": "Added 30 days date range limit for convert history and default values for `from` and `to` parameters.",
184 | "type": "data",
185 | "links": [
186 | {
187 | "title": "Convert History",
188 | "url": "/private/http-trade-v4/#convert-history"
189 | }
190 | ]
191 | }
192 | ]
193 | },
194 | {
195 | "title": "Travel Rule Update",
196 | "timeframe": "2025-05-19",
197 | "changes": [
198 | {
199 | "title": "HTTP Main V4 API - Travel Rule",
200 | "description": "Added new statuses DEPOSIT_TRAVEL_RULE_FROZEN (27) and DEPOSIT_TRAVEL_RULE_FROZEN_PROCESSING (28) to the deposit endpoint.",
201 | "type": "compliance",
202 | "links": [
203 | {
204 | "title": "Get deposit/withdraw history",
205 | "url": "/private/http-main-v4/#get-depositwithdraw-history"
206 | }
207 | ]
208 | }
209 | ]
210 | },
211 | {
212 | "title": "Deposit and Withdrawal Updates",
213 | "timeframe": "2025-03-31",
214 | "changes": [
215 | {
216 | "title": "HTTP Main V4 API - Birth Date Addition",
217 | "description": "Added beneficiary.birthDate field (Format: YYYY-MM-DD) to withdrawal requests and customer.birthDate (Format: YYYY-MM-DD) to deposit requests.",
218 | "type": "fiat",
219 | "links": [
220 | {
221 | "title": "Withdraw",
222 | "url": "/private/http-main-v4/#create-withdraw-request"
223 | },
224 | {
225 | "title": "Fiat Deposit",
226 | "url": "/private/http-main-v4/#get-fiat-deposit-address"
227 | }
228 | ]
229 | }
230 | ]
231 | },
232 | {
233 | "title": "Public WebSocket Update",
234 | "timeframe": "2025-07-09T06:00:00",
235 | "changes": [
236 | {
237 | "title": "Added BookTicker WebSocket stream",
238 | "description": "New bookTicker stream added to WebSocket API, providing real-time updates of best bid/ask prices and quantities for markets. Includes subscription (bookTicker_subscribe) and unsubscription (bookTicker_unsubscribe) methods for managing the data feed.",
239 | "type": "websocket",
240 | "links": [
241 | {
242 | "title": "ws - Book Ticker",
243 | "url": "/public/websocket/#book-ticker"
244 | }
245 | ]
246 | }
247 | ]
248 | },
249 | {
250 | "title": "Private API Update",
251 | "timeframe": "2025-07-10T06:00:00",
252 | "changes": [
253 | {
254 | "title": "Issue JWT Token for Private API",
255 | "description": "New endpoint to issue JWT token for private API access.",
256 | "type": "feature"
257 | },
258 | {
259 | "title": "Issue card token using JWT",
260 | "description": "New endpoint to issue a card token using JWT for secure transactions.",
261 | "type": "feature"
262 | },
263 | {
264 | "title": "Withdraw endpoint update",
265 | "description": "Address now accepts card tokens for withdrawals.",
266 | "type": "withdrawal"
267 | }
268 | ]
269 | },
270 | {
271 | "title": "Public API Update",
272 | "timeframe": "2025-07-15",
273 | "changes": [
274 | {
275 | "title": "New Funding History endpoint",
276 | "description": "Added new endpoint `/api/v4/public/funding-history/{market}` to retrieve funding rate history for futures markets.",
277 | "type": "trading"
278 | }
279 | ]
280 | },
281 | {
282 | "title": "API Documentation Updates",
283 | "timeframe": "2025-07-17",
284 | "changes": [
285 | {
286 | "title": "Enhanced Query executed orders documentation",
287 | "description": "Updated documentation for `/api/v4/trade-account/order/history` endpoint to clarify date range filtering capabilities with `startDate` and `endDate` parameters.",
288 | "type": "performance",
289 | "links": [
290 | {
291 | "title": "Trade API - Query executed orders",
292 | "url": "/private/http-trade-v4/#query-executed-orders"
293 | }
294 | ]
295 | }
296 | ]
297 | }
298 | ]
299 | }
300 |
--------------------------------------------------------------------------------