Skip to content

Learn

Exchange APIs

Bybit testnet setup

CoTrading editorial team Updated: 2026-09-23

Figures on this page are as of 2026-09. Fees, limits and margin tiers change — check the venue's own docs.

Bybit’s testnet is a separate hostname with separate credentials:

PurposeURL
Testnethttps://api-testnet.bybit.com
Mainnethttps://api.bybit.com (alternate https://api.bytick.com)

Testnet keys do not exist on mainnet, so pointing at the wrong host produces an authentication error rather than a live order. That is the arrangement you want, and it is worth appreciating — OKX, by contrast, shares a REST hostname between demo and production and switches with the x-simulated-trading: 1 header, where a dropped header means production.

Mainnet also has regional hosts — api.bybit.tr, api.bybit.kz, api.bybitgeorgia.ge, api.bybit.ae, api.bybit.eu, api.bybit.id, plus api.manepa.jp for Japan and api.spark-fintech.com for Hong Kong. Which one applies to you affects the host, not the mechanics.

What carries over

The request mechanics are identical, which is the point of testing there:

  • Headers: X-BAPI-API-KEY, X-BAPI-TIMESTAMP (milliseconds), X-BAPI-SIGN, and optional X-BAPI-RECV-WINDOW (default 5000 ms)
  • Signature: HMAC_SHA256 or RSA_SHA256
  • Pre-hash string: timestamp + api_key + recv_window + queryString for GET, or + jsonBodyString for POST

So the signing code you get working on testnet is the signing code that runs on mainnet. Since Bybit’s pre-hash construction differs from both Binance’s and OKX’s — no method, no request path, no separators — testnet is the right place to get that right.

The difference that does not carry over

The key. A key belongs to exactly one environment, and Bybit has four: mainnet, testnet, mainnet-demo and testnet-demo. Over REST, sending a key to the wrong environment’s host returns 10003, documented as “API key is invalid. Check whether the key and domain are matched, there are 4 env: mainnet, testnet, mainnet-demo, testnet-demo”. Bybit’s own FAQ calls a key/domain mismatch the most likely cause of 10003.

Codes that look similar but mean something else:

CodeMeaning
10003 (WebSocket order entry)“Too many sessions under the same UID”
-2015“(Spot) Your api key has expired”
33004“(Derivatives) Your api key has expired”
10006“Too many visits. Exceeded the API Rate Limit.”
10018“Exceeded the IP Rate Limit.”

Account mode is a separate question from environment. Two near-mirror codes flag an endpoint that requires, or excludes, a unified account:

  • 10028 — “The API can only be accessed by unified account users.”
  • 100028 — “The API cannot be accessed by unified account users.”

Bybit reports the mode as unifiedMarginStatus from Get Account Info. If you see either code on one environment and not the other, compare that field on both before you conclude your error handling works.

Migration checklist

  • Base URL changed to mainnet, and logged at startup so the environment is visible rather than inferred
  • Mainnet credentials in place; testnet credentials removed from the environment entirely
  • Account mode verified to match what you tested against — compare unifiedMarginStatus from Get Account Info on both
  • IP binding configured if you intend to use it (10010 is its signature when it is wrong)
  • Key permissions confirmed — 10005 is “Permission denied, please check your API key permissions”
  • Rate limit handling exercised, and order retries not automatic
  • Region checked — HTTP 403 is documented for restricted regions (the integration guide names the US and Mainland China), as well as for IP rate limit breaches and GET requests with an empty JSON body
  • First live orders sized small

Deriving both from one switch

The failure mode worth engineering against is not testnet credentials on mainnet — that fails immediately and loudly. It is mainnet credentials with a testnet URL still set in a code path you did not exercise.

Derive both from a single setting so they cannot disagree:

BASE, KEY, SECRET = (
    ("https://api-testnet.bybit.com", TESTNET_KEY, TESTNET_SECRET)
    if ENV == "testnet"
    else ("https://api.bybit.com", LIVE_KEY, LIVE_SECRET)
)
log.info("bybit environment=%s base=%s", ENV, BASE)

One switch, two values, one log line.

Testnet accounts are their own thing

Testnet credentials are issued from the testnet site, not from your mainnet account, and the two are unrelated. Two things follow that trip people up:

Testnet environments get reset. Balances and keys can be wiped as part of maintenance. Do not build anything that assumes a testnet key is permanent, and do not spend time debugging a testnet key that stopped working — regenerate it first and see whether that was the whole problem.

Demo Trading is not testnet. Bybit’s Demo Trading runs on https://api-demo.bybit.com, and its key is created on production while in Demo Trading mode. It is a different environment from testnet, with different keys — Bybit advises against using Demo Trading on Testnet, and mixing the two is exactly the key/domain mismatch that returns 10003.

What testnet cannot tell you

  • Fills and slippage. Testnet liquidity is not real; execution conclusions drawn from it are not evidence.
  • Rate limits in anger. Testnet traffic rarely reproduces production pressure, and 10006 versus 10018 — account-level versus IP-level — need different fixes.
  • Region restrictions. The HTTP 403 for restricted regions (US, Mainland China per the integration guide) is a production concern.
  • Your own behaviour. A workflow that felt fine on testnet feels different when the number is real. That is not a flaw in testnet; it is outside what it simulates.

FAQ

Do Bybit testnet keys work on mainnet?

No. Testnet credentials exist only on api-testnet.bybit.com and mainnet credentials only on api.bybit.com. This is a safety property — an environment mix-up gives you an authentication error rather than a real trade.

Why do I get 10003 after switching to mainnet?

Over REST, 10003 means the API key is invalid for the host you sent it to — Bybit’s FAQ names a key/domain mismatch as the most likely cause. Check that the base URL and the credentials were switched together, and that a Demo Trading key is not being sent to testnet or mainnet. (On the WebSocket order-entry channel the same code means too many sessions; an expired key is -2015 or 33004, not 10003.)

Is the signature the same on testnet and mainnet?

Yes. Same headers, same HMAC_SHA256 or RSA_SHA256, same pre-hash string of timestamp + api_key + recv_window + queryString or body. Testnet is the right place to get the construction right, since Bybit’s differs from Binance’s and OKX’s.

Can I use one key for testnet and mainnet?

No, and that is deliberate. Separate credentials per environment are what make pointing at the wrong host fail safely.