VIZANIXTrading Software Development
Bybit APIBybit API9 min read

Bybit V5 API: what actually changes when you move a bot to it

One account model, one set of endpoints, and a handful of details that silently break bots ported from earlier versions.

Vizanix engineering · about the author

ARTICLE
9 minreading time
SECTION
Bybit API
PUBLISHED
2026-08-28
CHAPTERS
7
READ NEXT
3
LANGUAGE
written in English
An engineering breakdown, not a rewrite of the docs.
V5UNIFIED ACCOUNTRESTSIGNINGRECV_WINDOW

Bybit V5 collapsed what used to be several parallel APIs into one surface. For a bot author this is mostly good news: one signing scheme, one error space, one account model. The trouble is that the things which break during a port are rarely the things you notice on the first request — they show up at three in the morning when a position exists that nobody planned.

The account model comes first

Before writing a single request, decide what account type the bot targets. Under a Unified Trading Account, spot, USDT perpetuals and options share one margin pool. That is convenient and it is also a coupling: a losing perpetual position reduces the margin available to everything else in the account.

This matters for risk code. A bot that computes available margin as wallet balance minus my own positions is correct on a dedicated account and wrong on a unified one, where a human might be holding something else in the same pool. Read the balance the exchange reports; do not reconstruct it.

Category routing is not cosmetic

Almost every V5 endpoint takes a category parameter — linear, spot, inverse, option. It decides which market your request lands in. A bot that hardcodes linear and is later pointed at a spot symbol will not fail loudly; it will get an instrument-not-found error that looks like a typo in the symbol.

Carry the category alongside the symbol in your instrument registry from the start. Deriving it later from the symbol name is guesswork that breaks the first time a venue lists a symbol that exists in two categories.

Signing, and the two clocks

V5 signs a string built from timestamp, API key, recv window and the request payload, in that order. Two failure modes account for most signing bugs:

  • Payload serialisation differs between signing and sending. If you build the signature from one JSON dump and let the HTTP library re-serialise the body, key order or whitespace can differ and the signature fails. Sign the exact bytes you send.
  • Clock drift. The signature carries a timestamp and the venue rejects requests outside recv_window. A server whose clock drifts by a second or two will work fine until it does not.

The second one is worth instrumenting rather than assuming. In our own Bybit executor we record the offset between local time and the venue's on every response and expose it in the health output; in normal operation it sits within roughly ±40 ms. A drift that starts climbing is visible long before it becomes a rejected order.

python
# Sign exactly what you are about to send.
raw = json.dumps(payload, separators=(",", ":"))   # one canonical form
sign_payload = f"{ts}{api_key}{recv_window}{raw}"
signature = hmac.new(secret, sign_payload.encode(), hashlib.sha256).hexdigest()

resp = session.post(url, data=raw, headers={          # data=raw, not json=payload
    "X-BAPI-API-KEY": api_key,
    "X-BAPI-TIMESTAMP": ts,
    "X-BAPI-RECV-WINDOW": recv_window,
    "X-BAPI-SIGN": signature,
    "Content-Type": "application/json",
})

Widening recv_window to hide drift is a trap. It does make the symptom go away, and it also widens the window in which a replayed request stays valid. Fix the clock instead — chrony or systemd-timesyncd costs nothing.

Instrument filters decide whether your order exists

Every symbol carries filters: qtyStep, minOrderQty, tickSize, minNotionalValue. A quantity that fails any of them is rejected. The failure is easy to handle and easy to get subtly wrong:

  • Round quantity down to qtyStep, not to the nearest step. Rounding up can push required margin past available balance.
  • Round price to tickSize in the direction that is conservative for your side — down for a buy limit, up for a sell limit.
  • Check minNotionalValue after rounding, not before. Rounding down can drop you under the minimum.
  • Refresh the filter table periodically. Venues change lot sizes, and a cached filter from deployment day eventually lies.

Margin is reserved with fees

This one surprises people porting a bot that used to size positions at “100% of free balance”. The venue holds initial margin plus the commission on the notional. So the real ceiling is not your free balance — it is roughly free / (1 + leverage × fee_rate). At 10× that is about 99% of balance; at 50× closer to 96%.

A bot that ignores this gets 110004 insufficient balance on exactly the orders it most wanted to place — the big ones. Our executor handles it with a retry ladder: read the fresh balance, shrink the size, re-quote, up to five attempts, stopping at the venue's minimum lot. It converts a hard failure into a smaller position, which is almost always what the operator wanted.

Error codes are a control flow, not a log line

V5 returns a numeric retCode. Treating anything non-zero as “error, retry” is how bots create duplicate positions. The codes split into groups that demand different behaviour:

GroupExampleCorrect response
Parameter / validation10001, 170137Do not retry. Fix the request; alert if it is unexpected.
Auth / permission10003, 10005, 10018Do not retry. Stop and alert — usually a key or IP-allowlist problem.
Rate limit10006Back off with jitter. Never retry in a tight loop.
Balance / risk limit110004, 110045Shrink and re-quote, or decline the signal.
Idempotent no-op110043 leverage not modifiedTreat as success. It means the state you wanted already holds.
Timeout / server10016Retry only with a client order ID, then reconcile.

That last row is the important one. A timeout is not a failure — it is an unknown. The request may have been executed. Retrying without a client-supplied order ID is how a bot ends up with two positions where the strategy asked for one. We keep a fuller map in the Bybit API error reference.

What to build first

  1. An instrument registry with filters and category, refreshed on a schedule.
  2. A signing function with a test that compares your signature against a known vector.
  3. A clock-offset metric, exported to health output.
  4. An error classifier that maps retCode to one of the behaviours above.
  5. Only then, order placement.

Bots that get written in this order are boring to operate. Bots that start with place-order and add the rest under pressure are the ones that need someone watching them.

This article describes engineering practice. It is not investment advice. Vizanix develops software and does not promise trading returns.

Blog

Read next

Want this running for you?

We write about what we build. If you need it built, get in touch — scoping is free.

Brief

Get a project estimate

Four questions and your contact. No deposit required to talk — if the job is not a fit, we say so straight away.

01What do you need
02Exchange
03Market
04Strategy
05Contacts

Prefer to write directly? Telegram @vx_ceo

Discuss a system