diff --git a/.gitignore b/.gitignore index 5fefec58..6ef6df23 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,4 @@ examples/ts-testnet-trade.ts examples/ts-testnet.ts *.pem .issue* +docs/research diff --git a/README.md b/README.md index a312a894..1c7a3b5a 100644 --- a/README.md +++ b/README.md @@ -86,9 +86,10 @@ Professional Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs, WebSo - [Websocket API - Sending Orders via WebSockets](#websocket-api---sending-orders-via-websockets) - [Consumer Load Balancing](#balancing-load-across-multiple-connections) -## Bybit EU & Other Regions +## Bybit EU and Other Regions -- [REST API Usage with Bybit EU](#rest-api-usage-with-bybit-eu) +- [`apiRegion` and `x-site-id`](#bybit-eu-and-other-regions-apiregion-and-x-site-id) +- [REST and WebSocket API Usage with Bybit EU](#rest-and-websocket-api-usage-with-bybit-eu) ## Additional Features @@ -237,11 +238,17 @@ const restClientOptions = { /** * Optionally override API domain used: - * apiRegion: 'default' | 'bytick' | 'NL' | 'HK' | 'TK', + * apiRegion: 'default' | 'bytick' | 'NL' | 'TK' | 'KZ' | 'HK' | 'GE' | 'UAE' | 'EU' | 'ID' | 'JP', **/ // apiRegion: 'bytick', + /** + * Add the x-site-id header for an eligible international account that uses + * the global API domain, e.g. BRA_BTL for Brazil or ARG_BTL for Argentina. + */ + // siteId: 'BRA_BTL', + /** Default: false. Enable to parse/include per-API/endpoint rate limits in responses. */ // parseAPIRateLimits: true, @@ -354,6 +361,12 @@ const wsConfig = { /** Delay in milliseconds before respawning the connection */ // reconnectTimeout: 500, + /** Select WebSocket routing using the same region values as REST */ + // apiRegion: 'TK', + + /** Add x-site-id for an eligible Brazil or Argentina international account */ + // siteId: 'BRA_BTL', + // override which URL to use for websocket connections // wsUrl: 'wss://stream.bytick.com/realtime' @@ -563,33 +576,93 @@ Important: do not subscribe to the same topics on both clients or you will recei --- -## Bybit EU & Other Regions +## Bybit EU and Other Regions: apiRegion and x-site-id + +Bybit uses two regional API access models. Use the configuration that matches the site where your account is registered: + +| Account setup | REST API endpoint | WebSocket endpoint | SDK configuration | +| ---------------------------------------- | ---------------------------- | ------------------------------------------------------------------------- | ----------------------- | +| Account with a dedicated regional domain | Matching regional API domain | Regional mainnet stream when Bybit lists one, otherwise the global stream | Set `apiRegion` | +| Brazil international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'BRA_BTL'` | +| Argentina international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'ARG_BTL'` | + +`apiRegion` selects the regional REST domain and, where Bybit lists one, the regional mainnet WebSocket domain. `siteId` keeps the default global domains and adds the `x-site-id` header to REST requests and Node.js WebSocket handshakes. This is only relevant if your account belongs to a specific region. -By default, this Node.js, JavaScript & TypeScript SDK uses the Bybit Global API & WebSocket domains. For regions where Bybit has dedicated regional domains, including the alternative Bybit Global domain (bytick), these can be configured in the REST Client using the `apiRegion` property. +See [Bybit's Integration Guidance](https://bybit-exchange.github.io/docs/v5/guide#authentication) for current regional requirements. + +### Dedicated Regional API Domains with apiRegion + +By default, this Node.js, JavaScript & TypeScript SDK uses the Bybit Global API and WebSocket domains. For regions where Bybit has dedicated regional domains, including the alternative Bybit Global domain (bytick), pass `apiRegion` directly to the REST or WebSocket client. The following values are currently supported in this option: - `apiRegion: undefined`: if missing or undefined, this SDK will default to the Bybit Global domain `api.bybit.com`. - `apiRegion: "default"`: the Bybit Global domain (same behaviour as above). - `apiRegion: "bytick"`: the alternative Bybit Global domain `api.bytick.com`. -- `apiRegion: "NL"`: the dedicated Bybit Netherlands domain `api.bytick.nl`. -- `apiRegion: "TK"`: the dedicated Bybit Turkey domain `api.bybit-tr.com`. +- `apiRegion: "NL"`: the dedicated Bybit Netherlands domain `api.bybit.nl`. +- `apiRegion: "TK"`: the dedicated Bybit Turkey domain `api.bybit.tr`. - `apiRegion: "KZ"`: the dedicated Bybit Kazakhstan domain `api.bybit.kz`. -- `apiRegion: "HK"`: the dedicated Bybit HK domain `api.byhkbit.com`. +- `apiRegion: "HK"`: the dedicated Bybit Hong Kong domain `api.spark-fintech.com`. The SDK also adds `x-refer-site-id: HKG`. Testnet uses `api-testnet.spark-fintech.com`. - `apiRegion: "GE"`: the dedicated Bybit Georgia domain `api.bybitgeorgia.ge`. - `apiRegion: "UAE"`: the dedicated Bybit United Arab Emirates domain `api.bybit.ae`. - `apiRegion: "EU"`: the dedicated Bybit EU/EEA domain `api.bybit.eu`. +- `apiRegion: "ID"`: the dedicated Bybit Indonesia domain `api.bybit.id`. +- `apiRegion: "JP"`: the dedicated Bybit Japan domain `api.manepa.jp`. Testnet uses `api-testnet.manepa.jp`. + +For WebSocket clients, the same option selects these mainnet stream domains: + +| `apiRegion` | WebSocket domain | +| ----------- | -------------------------- | +| `TK` | `stream.bybit.tr` | +| `KZ` | `stream.bybit.kz` | +| `HK` | `stream.spark-fintech.com` | +| `GE` | `stream.bybitgeorgia.ge` | +| `ID` | `stream.bybit.id` | +| `JP` | `stream.manepa.jp` | + +For `default`, `bytick`, `NL`, `UAE`, and `EU`, WebSocket connections continue to use the global stream because Bybit does not list a dedicated V5 trading stream for those values. This means `apiRegion: 'EU'` selects `api.bybit.eu` for REST and keeps `stream.bybit.com` for WebSocket. Testnet WebSocket connections use `stream-testnet.bybit.com`. For Hong Kong mainnet WebSocket connections, the SDK also adds `x-refer-site-id: HKG` automatically. + +```typescript +const ws = new WebsocketClient({ apiRegion: 'TK' }); +ws.subscribeV5('tickers.BTCUSDT', 'linear'); +``` New regions will be supported when they become available in the Bybit API. If you notice any regions that have not been added yet, please open a new issue on GitHub. -### REST API Usage with Bybit EU +### Brazil and Argentina International Accounts with x-site-id -Below is an example for using this Node.js, TypeScript & JavaScript SDK for Bybit's APIs, using an account registered on the Bybit EU domain: +Brazil and Argentina international accounts use the global REST API domain with a site-specific request header. Pass the matching value through the REST client's `siteId` option: ```typescript -const { RestClientV5 } = require('bybit-api'); +import { RestClientV5, WebsocketClient } from 'bybit-api'; + +const client = new RestClientV5({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); + +const ws = new WebsocketClient({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); +``` + +Use `siteId: 'ARG_BTL'` for an Argentina international account. + +The REST client sends `x-site-id` on every request. In Node.js, `WebsocketClient` and `WebsocketAPIClient` send it during the WebSocket handshake using the same top-level `siteId` option. Both use the global Bybit endpoint. Browser WebSocket connections cannot set custom handshake headers. + +See Bybit's [WebSocket connection guidance](https://bybit-exchange.github.io/docs/v5/ws/connect) for the regional requirement. + +### REST and WebSocket API Usage with Bybit EU + +Below is an example for using REST and WebSocket APIs with an account registered on Bybit EU: + +```typescript +const { RestClientV5, WebsocketClient } = require('bybit-api'); // or -// import { RestClientV5 } from 'bybit-api'; +// import { RestClientV5, WebsocketClient } from 'bybit-api'; const client = new RestClientV5({ key: API_KEY, @@ -607,6 +680,9 @@ client .catch((err) => { console.error('getAccountInfo error: ', err); }); + +const ws = new WebsocketClient({ apiRegion: 'EU' }); +ws.subscribeV5('tickers.BTCUSDT', 'linear'); ``` --- diff --git a/docs/BYBIT_SDK_QUICKSTART_GUIDE.md b/docs/BYBIT_SDK_QUICKSTART_GUIDE.md index 5193fa4b..d8463149 100644 --- a/docs/BYBIT_SDK_QUICKSTART_GUIDE.md +++ b/docs/BYBIT_SDK_QUICKSTART_GUIDE.md @@ -1155,6 +1155,8 @@ See also: Live is the default environment: ```typescript +import { RestClientV5 } from 'bybit-api'; + const client = new RestClientV5({ key: process.env.BYBIT_API_KEY!, secret: process.env.BYBIT_API_SECRET!, @@ -1207,9 +1209,23 @@ ws.subscribeV5(['order', 'execution', 'position', 'wallet'], 'linear'); Do not combine `testnet: true` with `demoTrading: true`. Bybit's demo trading docs also note that WebSocket API commands are not supported in demo trading, so use REST API demo trading or private demo streams for demo workflows, and use testnet for WebSocket API command testing. -### Regional REST API domains +### Regional REST and WebSocket API access: apiRegion and x-site-id + +Bybit uses two regional API access models. Use the configuration that matches the site where your account is registered: + +| Account setup | REST API endpoint | WebSocket endpoint | SDK configuration | +| --- | --- | --- | --- | +| Account with a dedicated regional domain | Matching regional API domain | Regional mainnet stream when Bybit lists one, otherwise the global stream | Set `apiRegion` | +| Brazil international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'BRA_BTL'` | +| Argentina international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'ARG_BTL'` | -By default, REST API calls use the global Bybit domain. If your account belongs to a regional Bybit domain, set `apiRegion`: +`apiRegion` selects the regional REST domain and, where Bybit lists one, the regional mainnet WebSocket domain. `siteId` keeps the default global domains and adds the `x-site-id` header to REST requests and Node.js WebSocket handshakes. Only set `siteId` when Bybit documents it for your account. Do not derive or invent a value. + +See [Bybit's Integration Guidance](https://bybit-exchange.github.io/docs/v5/guide#authentication) for current regional requirements. + +#### Dedicated regional domains with apiRegion + +By default, REST and WebSocket clients use the global Bybit domains. If your account belongs to a regional Bybit domain, set `apiRegion` directly on each client: ```typescript const client = new RestClientV5({ @@ -1217,6 +1233,9 @@ const client = new RestClientV5({ secret: process.env.BYBIT_API_SECRET!, apiRegion: 'EU', }); + +const ws = new WebsocketClient({ apiRegion: 'EU' }); +ws.subscribeV5('tickers.BTCUSDT', 'linear'); ``` Supported API region values in this SDK: @@ -1230,9 +1249,50 @@ Supported API region values in this SDK: - `GE` - `UAE` - `EU` +- `ID` +- `JP` + +For a Hong Kong account, `apiRegion: 'HK'` selects `api.spark-fintech.com` on mainnet or `api-testnet.spark-fintech.com` on testnet. The SDK adds the required `x-refer-site-id: HKG` header automatically. This is separate from the `siteId` option used for Brazil and Argentina international accounts. + +On WebSocket clients, these mainnet routes are selected automatically: + +- `TK`: `stream.bybit.tr` +- `KZ`: `stream.bybit.kz` +- `HK`: `stream.spark-fintech.com`, with `x-refer-site-id: HKG` +- `GE`: `stream.bybitgeorgia.ge` +- `ID`: `stream.bybit.id` +- `JP`: `stream.manepa.jp` + +For `default`, `bytick`, `NL`, `UAE`, and `EU`, WebSocket connections continue to use the global stream because Bybit does not list a dedicated V5 trading stream for those values. This means `apiRegion: 'EU'` selects `api.bybit.eu` for REST and keeps `stream.bybit.com` for WebSocket. Testnet WebSocket connections use `stream-testnet.bybit.com`. New API regions will be supported as they become available. If you're looking for a region not yet supported, please get in touch. +#### Brazil and Argentina international accounts with x-site-id + +Brazil and Argentina international accounts use the global REST API domain with a site-specific request header. Pass the matching value through the REST client's `siteId` option: + +```typescript +import { RestClientV5, WebsocketClient } from 'bybit-api'; + +const client = new RestClientV5({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); + +const ws = new WebsocketClient({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); +``` + +Use `siteId: 'ARG_BTL'` for an Argentina international account. + +The REST client sends `x-site-id` on every request. In Node.js, `WebsocketClient` and `WebsocketAPIClient` send it during the WebSocket handshake using the same top-level `siteId` option. Both use the global Bybit endpoint. Browser WebSocket connections cannot set custom handshake headers. + +See Bybit's [WebSocket connection guidance](https://bybit-exchange.github.io/docs/v5/ws/connect) for the regional requirement. + You can also pass `baseUrl` for a custom REST API domain, or `wsUrl` for a custom WebSocket URL when needed. See also: [custom REST API URL example](../examples/Rest/rest-v5-custom-url.ts) diff --git a/examples/Rest/rest-v5-custom-url.ts b/examples/Rest/rest-v5-custom-url.ts index 8e10fe98..3fc974be 100644 --- a/examples/Rest/rest-v5-custom-url.ts +++ b/examples/Rest/rest-v5-custom-url.ts @@ -21,7 +21,8 @@ const client = new RestClientV5({ // /** * - * There are also predefined API regions, which you can easily use with the "apiRegion" property: + * Predefined API regions are available through the `apiRegion` property. + * Choose the region where your Bybit account is registered: * */ // @@ -34,16 +35,50 @@ const client = new RestClientV5({ // apiRegion: 'bytick', // // - // NL: routes to api.bybit.nl (for Netherland users) + // NL: routes to api.bybit.nl (for Netherlands users) // apiRegion: 'NL', // // - // HK: routes to api.byhkbit.com (for Hong Kong users) + // TK: routes to api.bybit.tr (for Turkey users) + // apiRegion: 'TK', + // + // + // KZ: routes to api.bybit.kz (for Kazakhstan users) + // apiRegion: 'KZ', + // + // + // HK: routes to api.spark-fintech.com and adds x-refer-site-id: HKG + // Testnet routes to api-testnet.spark-fintech.com // apiRegion: 'HK', // // - // TK: routes to api.bybit-tr.com (for Turkey users) - // apiRegion: 'TK', + // GE: routes to api.bybitgeorgia.ge (for Georgia users) + // apiRegion: 'GE', + // + // + // UAE: routes to api.bybit.ae (for United Arab Emirates users) + // apiRegion: 'UAE', + // + // + // EU: routes to api.bybit.eu (for EU and EEA users) + // apiRegion: 'EU', + // + // + // ID: routes to api.bybit.id (for Indonesian users) + // apiRegion: 'ID', + // + // + // JP: routes to api.manepa.jp (for Japan users) + // Testnet routes to api-testnet.manepa.jp + // apiRegion: 'JP', + // + // + /** + * Brazil and Argentina international accounts use the global API domain + * with an x-site-id header. Choose the site ID for your account: + */ + // siteId: 'BRA_BTL', + // siteId: 'ARG_BTL', }); (async () => { diff --git a/llms.txt b/llms.txt index 766890c7..bc407da2 100644 --- a/llms.txt +++ b/llms.txt @@ -3797,7 +3797,8 @@ import { RestClientV5 } from '../../src/index'; // /** * - * There are also predefined API regions, which you can easily use with the "apiRegion" property: + * Predefined API regions are available through the `apiRegion` property. + * Choose the region where your Bybit account is registered: * */ // @@ -3810,16 +3811,50 @@ import { RestClientV5 } from '../../src/index'; // apiRegion: 'bytick', // // -// NL: routes to api.bybit.nl (for Netherland users) +// NL: routes to api.bybit.nl (for Netherlands users) // apiRegion: 'NL', // // -// HK: routes to api.byhkbit.com (for Hong Kong users) +// TK: routes to api.bybit.tr (for Turkey users) +// apiRegion: 'TK', +// +// +// KZ: routes to api.bybit.kz (for Kazakhstan users) +// apiRegion: 'KZ', +// +// +// HK: routes to api.spark-fintech.com and adds x-refer-site-id: HKG +// Testnet routes to api-testnet.spark-fintech.com // apiRegion: 'HK', // // -// TK: routes to api.bybit-tr.com (for Turkey users) -// apiRegion: 'TK', +// GE: routes to api.bybitgeorgia.ge (for Georgia users) +// apiRegion: 'GE', +// +// +// UAE: routes to api.bybit.ae (for United Arab Emirates users) +// apiRegion: 'UAE', +// +// +// EU: routes to api.bybit.eu (for EU and EEA users) +// apiRegion: 'EU', +// +// +// ID: routes to api.bybit.id (for Indonesian users) +// apiRegion: 'ID', +// +// +// JP: routes to api.manepa.jp (for Japan users) +// Testnet routes to api-testnet.manepa.jp +// apiRegion: 'JP', +// +// +/** + * Brazil and Argentina international accounts use the global API domain + * with an x-site-id header. Choose the site ID for your account: + */ +// siteId: 'BRA_BTL', +// siteId: 'ARG_BTL', ================ File: examples/Rest/rest-v5-next-cursor.ts @@ -12900,6 +12935,8 @@ See also: Live is the default environment: ```typescript +import { RestClientV5 } from 'bybit-api'; + const client = new RestClientV5({ key: process.env.BYBIT_API_KEY!, secret: process.env.BYBIT_API_SECRET!, @@ -12911,6 +12948,8 @@ const client = new RestClientV5({ Testnet uses separate credentials and separate API domains: ```typescript +import { RestClientV5 } from 'bybit-api'; + const client = new RestClientV5({ key: process.env.BYBIT_API_KEY!, secret: process.env.BYBIT_API_SECRET!, @@ -12952,9 +12991,23 @@ ws.subscribeV5(['order', 'execution', 'position', 'wallet'], 'linear'); Do not combine `testnet: true` with `demoTrading: true`. Bybit's demo trading docs also note that WebSocket API commands are not supported in demo trading, so use REST API demo trading or private demo streams for demo workflows, and use testnet for WebSocket API command testing. -### Regional REST API domains +### Regional REST and WebSocket API access: apiRegion and x-site-id + +Bybit uses two regional API access models. Use the configuration that matches the site where your account is registered: + +| Account setup | REST API endpoint | WebSocket endpoint | SDK configuration | +| --- | --- | --- | --- | +| Account with a dedicated regional domain | Matching regional API domain | Regional mainnet stream when Bybit lists one, otherwise the global stream | Set `apiRegion` | +| Brazil international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'BRA_BTL'` | +| Argentina international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'ARG_BTL'` | + +`apiRegion` selects the regional REST domain and, where Bybit lists one, the regional mainnet WebSocket domain. `siteId` keeps the default global domains and adds the `x-site-id` header to REST requests and Node.js WebSocket handshakes. Only set `siteId` when Bybit documents it for your account. Do not derive or invent a value. -By default, REST API calls use the global Bybit domain. If your account belongs to a regional Bybit domain, set `apiRegion`: +See [Bybit's Integration Guidance](https://bybit-exchange.github.io/docs/v5/guide#authentication) for current regional requirements. + +#### Dedicated regional domains with apiRegion + +By default, REST and WebSocket clients use the global Bybit domains. If your account belongs to a regional Bybit domain, set `apiRegion` directly on each client: ```typescript const client = new RestClientV5({ @@ -12962,6 +13015,9 @@ const client = new RestClientV5({ secret: process.env.BYBIT_API_SECRET!, apiRegion: 'EU', }); + +const ws = new WebsocketClient({ apiRegion: 'EU' }); +ws.subscribeV5('tickers.BTCUSDT', 'linear'); ``` Supported API region values in this SDK: @@ -12975,9 +13031,50 @@ Supported API region values in this SDK: - `GE` - `UAE` - `EU` +- `ID` +- `JP` + +For a Hong Kong account, `apiRegion: 'HK'` selects `api.spark-fintech.com` on mainnet or `api-testnet.spark-fintech.com` on testnet. The SDK adds the required `x-refer-site-id: HKG` header automatically. This is separate from the `siteId` option used for Brazil and Argentina international accounts. + +On WebSocket clients, these mainnet routes are selected automatically: + +- `TK`: `stream.bybit.tr` +- `KZ`: `stream.bybit.kz` +- `HK`: `stream.spark-fintech.com`, with `x-refer-site-id: HKG` +- `GE`: `stream.bybitgeorgia.ge` +- `ID`: `stream.bybit.id` +- `JP`: `stream.manepa.jp` + +For `default`, `bytick`, `NL`, `UAE`, and `EU`, WebSocket connections continue to use the global stream because Bybit does not list a dedicated V5 trading stream for those values. This means `apiRegion: 'EU'` selects `api.bybit.eu` for REST and keeps `stream.bybit.com` for WebSocket. Testnet WebSocket connections use `stream-testnet.bybit.com`. New API regions will be supported as they become available. If you're looking for a region not yet supported, please get in touch. +#### Brazil and Argentina international accounts with x-site-id + +Brazil and Argentina international accounts use the global REST API domain with a site-specific request header. Pass the matching value through the REST client's `siteId` option: + +```typescript +import { RestClientV5, WebsocketClient } from 'bybit-api'; + +const client = new RestClientV5({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); + +const ws = new WebsocketClient({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); +``` + +Use `siteId: 'ARG_BTL'` for an Argentina international account. + +The REST client sends `x-site-id` on every request. In Node.js, `WebsocketClient` and `WebsocketAPIClient` send it during the WebSocket handshake using the same top-level `siteId` option. Both use the global Bybit endpoint. Browser WebSocket connections cannot set custom handshake headers. + +See Bybit's [WebSocket connection guidance](https://bybit-exchange.github.io/docs/v5/ws/connect) for the regional requirement. + You can also pass `baseUrl` for a custom REST API domain, or `wsUrl` for a custom WebSocket URL when needed. See also: [custom REST API URL example](../examples/Rest/rest-v5-custom-url.ts) @@ -15029,6 +15126,25 @@ export interface WSClientConfigurableOptions { /** Delay in milliseconds before respawning the connection */ reconnectTimeout?: number; + /** + * Select WebSocket routing with the same region values as REST. Uses a + * dedicated mainnet stream when Bybit documents one for that region. + * Top-level `apiRegion` takes precedence over `restOptions.apiRegion`. + */ + apiRegion?: APIRegion; + + /** + * Site ID sent as `x-site-id` during the Node.js WebSocket handshake. + * Required for eligible international accounts that use the global stream domain, + * e.g. `BRA_BTL` for Brazil or `ARG_BTL` for Argentina. + */ + siteId?: string; + + /** + * REST options reused for WebSocket routing. + * Regional and site ID values are retained as fallbacks; prefer the matching + * top-level `apiRegion` and `siteId` options. + */ restOptions?: RestClientOptions; requestOptions?: AxiosRequestConfig; @@ -21987,9 +22103,10 @@ Professional Node.js, JavaScript & TypeScript SDK for the Bybit REST APIs, WebSo - [Websocket API - Sending Orders via WebSockets](#websocket-api---sending-orders-via-websockets) - [Consumer Load Balancing](#balancing-load-across-multiple-connections) -## Bybit EU & Other Regions +## Bybit EU and Other Regions -- [REST API Usage with Bybit EU](#rest-api-usage-with-bybit-eu) +- [`apiRegion` and `x-site-id`](#bybit-eu-and-other-regions-apiregion-and-x-site-id) +- [REST and WebSocket API Usage with Bybit EU](#rest-and-websocket-api-usage-with-bybit-eu) ## Additional Features @@ -22138,11 +22255,17 @@ const restClientOptions = { /** * Optionally override API domain used: - * apiRegion: 'default' | 'bytick' | 'NL' | 'HK' | 'TK', + * apiRegion: 'default' | 'bytick' | 'NL' | 'TK' | 'KZ' | 'HK' | 'GE' | 'UAE' | 'EU' | 'ID' | 'JP', **/ // apiRegion: 'bytick', + /** + * Add the x-site-id header for an eligible international account that uses + * the global API domain, e.g. BRA_BTL for Brazil or ARG_BTL for Argentina. + */ + // siteId: 'BRA_BTL', + /** Default: false. Enable to parse/include per-API/endpoint rate limits in responses. */ // parseAPIRateLimits: true, @@ -22255,6 +22378,12 @@ const wsConfig = { /** Delay in milliseconds before respawning the connection */ // reconnectTimeout: 500, + /** Select WebSocket routing using the same region values as REST */ + // apiRegion: 'TK', + + /** Add x-site-id for an eligible Brazil or Argentina international account */ + // siteId: 'BRA_BTL', + // override which URL to use for websocket connections // wsUrl: 'wss://stream.bytick.com/realtime' @@ -22464,33 +22593,93 @@ Important: do not subscribe to the same topics on both clients or you will recei --- -## Bybit EU & Other Regions +## Bybit EU and Other Regions: apiRegion and x-site-id + +Bybit uses two regional API access models. Use the configuration that matches the site where your account is registered: + +| Account setup | REST API endpoint | WebSocket endpoint | SDK configuration | +| --- | --- | --- | --- | +| Account with a dedicated regional domain | Matching regional API domain | Regional mainnet stream when Bybit lists one, otherwise the global stream | Set `apiRegion` | +| Brazil international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'BRA_BTL'` | +| Argentina international account | `api.bybit.com` | `stream.bybit.com` | Set `siteId: 'ARG_BTL'` | + +`apiRegion` selects the regional REST domain and, where Bybit lists one, the regional mainnet WebSocket domain. `siteId` keeps the default global domains and adds the `x-site-id` header to REST requests and Node.js WebSocket handshakes. Only set `siteId` when Bybit documents it for your account. Do not derive or invent a value. -By default, this Node.js, JavaScript & TypeScript SDK uses the Bybit Global API & WebSocket domains. For regions where Bybit has dedicated regional domains, including the alternative Bybit Global domain (bytick), these can be configured in the REST Client using the `apiRegion` property. +See [Bybit's Integration Guidance](https://bybit-exchange.github.io/docs/v5/guide#authentication) for current regional requirements. + +### Dedicated Regional API Domains with apiRegion + +By default, this Node.js, JavaScript & TypeScript SDK uses the Bybit Global API and WebSocket domains. For regions where Bybit has dedicated regional domains, including the alternative Bybit Global domain (bytick), pass `apiRegion` directly to the REST or WebSocket client. The following values are currently supported in this option: - `apiRegion: undefined`: if missing or undefined, this SDK will default to the Bybit Global domain `api.bybit.com`. - `apiRegion: "default"`: the Bybit Global domain (same behaviour as above). - `apiRegion: "bytick"`: the alternative Bybit Global domain `api.bytick.com`. -- `apiRegion: "NL"`: the dedicated Bybit Netherlands domain `api.bytick.nl`. -- `apiRegion: "TK"`: the dedicated Bybit Turkey domain `api.bybit-tr.com`. +- `apiRegion: "NL"`: the dedicated Bybit Netherlands domain `api.bybit.nl`. +- `apiRegion: "TK"`: the dedicated Bybit Turkey domain `api.bybit.tr`. - `apiRegion: "KZ"`: the dedicated Bybit Kazakhstan domain `api.bybit.kz`. -- `apiRegion: "HK"`: the dedicated Bybit HK domain `api.byhkbit.com`. +- `apiRegion: "HK"`: the dedicated Bybit Hong Kong domain `api.spark-fintech.com`. The SDK also adds `x-refer-site-id: HKG`. Testnet uses `api-testnet.spark-fintech.com`. - `apiRegion: "GE"`: the dedicated Bybit Georgia domain `api.bybitgeorgia.ge`. - `apiRegion: "UAE"`: the dedicated Bybit United Arab Emirates domain `api.bybit.ae`. - `apiRegion: "EU"`: the dedicated Bybit EU/EEA domain `api.bybit.eu`. +- `apiRegion: "ID"`: the dedicated Bybit Indonesia domain `api.bybit.id`. +- `apiRegion: "JP"`: the dedicated Bybit Japan domain `api.manepa.jp`. Testnet uses `api-testnet.manepa.jp`. + +For WebSocket clients, the same option selects these mainnet stream domains: + +| `apiRegion` | WebSocket domain | +| --- | --- | +| `TK` | `stream.bybit.tr` | +| `KZ` | `stream.bybit.kz` | +| `HK` | `stream.spark-fintech.com` | +| `GE` | `stream.bybitgeorgia.ge` | +| `ID` | `stream.bybit.id` | +| `JP` | `stream.manepa.jp` | + +For `default`, `bytick`, `NL`, `UAE`, and `EU`, WebSocket connections continue to use the global stream because Bybit does not list a dedicated V5 trading stream for those values. This means `apiRegion: 'EU'` selects `api.bybit.eu` for REST and keeps `stream.bybit.com` for WebSocket. Testnet WebSocket connections use `stream-testnet.bybit.com`. For Hong Kong mainnet WebSocket connections, the SDK also adds `x-refer-site-id: HKG` automatically. + +```typescript +const ws = new WebsocketClient({ apiRegion: 'TK' }); +ws.subscribeV5('tickers.BTCUSDT', 'linear'); +``` New regions will be supported when they become available in the Bybit API. If you notice any regions that have not been added yet, please open a new issue on GitHub. -### REST API Usage with Bybit EU +### Brazil and Argentina International Accounts with x-site-id -Below is an example for using this Node.js, TypeScript & JavaScript SDK for Bybit's APIs, using an account registered on the Bybit EU domain: +Brazil and Argentina international accounts use the global REST API domain with a site-specific request header. Pass the matching value through the REST client's `siteId` option: ```typescript -const { RestClientV5 } = require('bybit-api'); +import { RestClientV5, WebsocketClient } from 'bybit-api'; + +const client = new RestClientV5({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); + +const ws = new WebsocketClient({ + key: process.env.BYBIT_API_KEY!, + secret: process.env.BYBIT_API_SECRET!, + siteId: 'BRA_BTL', +}); +``` + +Use `siteId: 'ARG_BTL'` for an Argentina international account. + +The REST client sends `x-site-id` on every request. In Node.js, `WebsocketClient` and `WebsocketAPIClient` send it during the WebSocket handshake using the same top-level `siteId` option. Both use the global Bybit endpoint. Browser WebSocket connections cannot set custom handshake headers. + +See Bybit's [WebSocket connection guidance](https://bybit-exchange.github.io/docs/v5/ws/connect) for the regional requirement. + +### REST and WebSocket API Usage with Bybit EU + +Below is an example for using REST and WebSocket APIs with an account registered on Bybit EU: + +```typescript +const { RestClientV5, WebsocketClient } = require('bybit-api'); // or -// import { RestClientV5 } from 'bybit-api'; +// import { RestClientV5, WebsocketClient } from 'bybit-api'; const client = new RestClientV5({ key: API_KEY, @@ -22508,6 +22697,9 @@ client .catch((err) => { console.error('getAccountInfo error: ', err); }); + +const ws = new WebsocketClient({ apiRegion: 'EU' }); +ws.subscribeV5('tickers.BTCUSDT', 'linear'); ``` --- diff --git a/package-lock.json b/package-lock.json index e10b9135..e92949c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "bybit-api", - "version": "4.7.5", + "version": "4.7.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "bybit-api", - "version": "4.7.5", + "version": "4.7.6", "license": "MIT", "dependencies": { "@types/ws": "8.18.1", diff --git a/package.json b/package.json index f49495b9..44bbdf71 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bybit-api", - "version": "4.7.5", + "version": "4.7.6", "description": "Complete & robust Node.js SDK for Bybit's REST APIs and WebSockets, with TypeScript & strong end to end tests.", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/src/types/websockets/ws-general.ts b/src/types/websockets/ws-general.ts index 14eb46c4..286ff8aa 100644 --- a/src/types/websockets/ws-general.ts +++ b/src/types/websockets/ws-general.ts @@ -2,7 +2,7 @@ import { AxiosRequestConfig } from 'axios'; import type { ClientRequestArgs } from 'http'; import WebSocket from 'isomorphic-ws'; -import { RestClientOptions, WS_KEY_MAP } from '../../util'; +import { APIRegion, RestClientOptions, WS_KEY_MAP } from '../../util'; /** For spot markets, spotV3 is recommended */ export type APIMarket = 'v5'; @@ -120,6 +120,25 @@ export interface WSClientConfigurableOptions { /** Delay in milliseconds before respawning the connection */ reconnectTimeout?: number; + /** + * Select WebSocket routing with the same region values as REST. Uses a + * dedicated mainnet stream when Bybit documents one for that region. + * Top-level `apiRegion` takes precedence over `restOptions.apiRegion`. + */ + apiRegion?: APIRegion; + + /** + * Site ID sent as `x-site-id` during the Node.js WebSocket handshake. + * Required for eligible international accounts that use the global stream domain, + * e.g. `BRA_BTL` for Brazil or `ARG_BTL` for Argentina. + */ + siteId?: string; + + /** + * REST options reused for WebSocket routing. + * Regional and site ID values are retained as fallbacks; prefer the matching + * top-level `apiRegion` and `siteId` options. + */ restOptions?: RestClientOptions; requestOptions?: AxiosRequestConfig; diff --git a/src/util/BaseRestClient.ts b/src/util/BaseRestClient.ts index c1d178d2..26a15c01 100644 --- a/src/util/BaseRestClient.ts +++ b/src/util/BaseRestClient.ts @@ -149,6 +149,9 @@ export default abstract class BaseRestClient { headers: { ...networkOptions.headers, 'x-referer': isEUAPIRegion(this.options) ? APIIDEU : APIID, + ...(this.options.apiRegion === 'HK' + ? { 'x-refer-site-id': 'HKG' } + : undefined), ...(this.options.siteId ? { 'x-site-id': this.options.siteId } : undefined), diff --git a/src/util/BaseWSClient.ts b/src/util/BaseWSClient.ts index 0e6db8a2..fad405a5 100644 --- a/src/util/BaseWSClient.ts +++ b/src/util/BaseWSClient.ts @@ -1,6 +1,7 @@ /* eslint-disable max-len */ /* eslint-disable @typescript-eslint/no-explicit-any */ import EventEmitter from 'events'; +import type { ClientRequestArgs } from 'http'; import WebSocket from 'isomorphic-ws'; import { @@ -726,12 +727,25 @@ export abstract class BaseWebsocketClient< const legacyAgent = (this.options.requestOptions as any)?.agent; const { protocols = [], ...wsOptions } = wsOptionsConfig; - - // Merge legacy agent if wsOptions doesn't have one - const finalWsOptions = - !wsOptions.agent && legacyAgent - ? { ...wsOptions, agent: legacyAgent } - : wsOptions; + const siteId = this.options.siteId ?? this.options.restOptions?.siteId; + const apiRegion = + this.options.apiRegion ?? this.options.restOptions?.apiRegion; + + const finalWsOptions = { + ...wsOptions, + ...(!wsOptions.agent && legacyAgent ? { agent: legacyAgent } : undefined), + ...(siteId || apiRegion === 'HK' + ? { + headers: { + ...wsOptions.headers, + ...(apiRegion === 'HK' + ? { 'x-refer-site-id': 'HKG' } + : undefined), + ...(siteId ? { 'x-site-id': siteId } : undefined), + }, + } + : undefined), + } as WebSocket.ClientOptions | ClientRequestArgs; const ws: WebSocket & { wsKey?: string } = new WebSocket( url, diff --git a/src/util/requestUtils.ts b/src/util/requestUtils.ts index 80e807f6..1b419c07 100644 --- a/src/util/requestUtils.ts +++ b/src/util/requestUtils.ts @@ -12,6 +12,7 @@ export type APIRegion = | 'GE' | 'UAE' | 'EU' + | 'ID' | 'JP'; export interface RestClientOptions { @@ -77,7 +78,13 @@ export interface RestClientOptions { apiRegion?: APIRegion; - /** Site ID header for regional access (e.g. 'ARG_BTL' for Argentina) */ + /** + * Site ID sent as the `x-site-id` header on every REST request. + * WebSocket clients expose the same top-level `siteId` option. + * Required for eligible international accounts that use the global API domain, + * e.g. `BRA_BTL` for Brazil or `ARG_BTL` for Argentina. + * @see https://bybit-exchange.github.io/docs/v5/guide#authentication + */ siteId?: string; /** Default: true. whether to try and post-process request exceptions. */ @@ -144,12 +151,13 @@ export function getRestBaseUrl( default: 'https://api.bybit.com', bytick: 'https://api.bytick.com', NL: 'https://api.bybit.nl', - TK: 'https://api.bybit-tr.com', + TK: 'https://api.bybit.tr', KZ: 'https://api.bybit.kz', - HK: 'https://api.byhkbit.com', + HK: 'https://api.spark-fintech.com', GE: 'https://api.bybitgeorgia.ge', UAE: 'https://api.bybit.ae', EU: 'https://api.bybit.eu', + ID: 'https://api.bybit.id', JP: 'https://api.manepa.jp', }; @@ -171,6 +179,9 @@ export function getRestBaseUrl( if (restClientOptions.apiRegion === 'JP') { return 'https://api-testnet.manepa.jp'; } + if (restClientOptions.apiRegion === 'HK') { + return 'https://api-testnet.spark-fintech.com'; + } return exchangeBaseUrls.testnet; } diff --git a/src/util/websockets/websocket-util.ts b/src/util/websockets/websocket-util.ts index bfa6975f..1f09d21e 100644 --- a/src/util/websockets/websocket-util.ts +++ b/src/util/websockets/websocket-util.ts @@ -9,6 +9,7 @@ import { } from '../../types'; import { WSAPIRequest } from '../../types/websockets/ws-api'; import { DefaultLogger } from '../logger'; +import { APIRegion } from '../requestUtils'; import { neverGuard } from '../typeGuards'; export const WS_KEY_MAP = { @@ -216,6 +217,15 @@ export const WS_BASE_URL_MAP: Record< }, }; +const WS_MAINNET_REGION_DOMAIN_MAP: Partial> = { + TK: 'stream.bybit.tr', + KZ: 'stream.bybit.kz', + HK: 'stream.spark-fintech.com', + GE: 'stream.bybitgeorgia.ge', + ID: 'stream.bybit.id', + JP: 'stream.manepa.jp', +}; + export function isPrivateWsTopic(topic: string): boolean { return PRIVATE_TOPICS.includes(topic); } @@ -279,56 +289,56 @@ export function getWsUrl( const isDemoTrading = wsClientOptions.demoTrading; const isTestnet = wsClientOptions.testnet; const networkKey = isTestnet ? 'testnet' : 'livenet'; - const isJP = wsClientOptions.restOptions?.apiRegion === 'JP'; + const apiRegion = + wsClientOptions.apiRegion ?? wsClientOptions.restOptions?.apiRegion; + const regionalDomain = + !isTestnet && apiRegion + ? WS_MAINNET_REGION_DOMAIN_MAP[apiRegion] + : undefined; + + const resolveUrl = (path: string, defaultUrl: string): string => { + return regionalDomain ? `wss://${regionalDomain}${path}` : defaultUrl; + }; switch (wsKey) { case WS_KEY_MAP.v5Private: { if (isDemoTrading) { return DEMO_TRADING_ENDPOINT; } - if (isJP) { - const base = isTestnet - ? 'stream-testnet.manepa.jp' - : 'stream.manepa.jp'; - return `wss://${base}/v5/private`; - } - return WS_BASE_URL_MAP.v5.private[networkKey]; + return resolveUrl('/v5/private', WS_BASE_URL_MAP.v5.private[networkKey]); } case WS_KEY_MAP.v5PrivateTrade: { if (isDemoTrading) { return DEMO_TRADING_ENDPOINT; } - if (isJP) { - const base = isTestnet - ? 'stream-testnet.manepa.jp' - : 'stream.manepa.jp'; - return `wss://${base}/v5/trade`; - } - return WS_BASE_URL_MAP[wsKey].private[networkKey]; + return resolveUrl( + '/v5/trade', + WS_BASE_URL_MAP[wsKey].private[networkKey], + ); } case WS_KEY_MAP.v5SpotPublic: { - if (isJP) { - return 'wss://stream.manepa.jp/v5/public/spot'; - } - return WS_BASE_URL_MAP.v5SpotPublic.public[networkKey]; + return resolveUrl( + '/v5/public/spot', + WS_BASE_URL_MAP.v5SpotPublic.public[networkKey], + ); } case WS_KEY_MAP.v5LinearPublic: { - if (isJP) { - return 'wss://stream.manepa.jp/v5/public/linear'; - } - return WS_BASE_URL_MAP.v5LinearPublic.public[networkKey]; + return resolveUrl( + '/v5/public/linear', + WS_BASE_URL_MAP.v5LinearPublic.public[networkKey], + ); } case WS_KEY_MAP.v5InversePublic: { - if (isJP) { - return 'wss://stream.manepa.jp/v5/public/inverse'; - } - return WS_BASE_URL_MAP.v5InversePublic.public[networkKey]; + return resolveUrl( + '/v5/public/inverse', + WS_BASE_URL_MAP.v5InversePublic.public[networkKey], + ); } case WS_KEY_MAP.v5OptionPublic: { - if (isJP) { - return 'wss://stream.manepa.jp/v5/public/option'; - } - return WS_BASE_URL_MAP.v5OptionPublic.public[networkKey]; + return resolveUrl( + '/v5/public/option', + WS_BASE_URL_MAP.v5OptionPublic.public[networkKey], + ); } default: { logger.error('getWsUrl(): Unhandled wsKey: ', {