MarzbanSDK
Advanced

HTTP & Retry

Timeouts, automatic retries with exponential backoff, and network resilience.

MarzbanSDK is built on top of Axios and uses axios-retry to handle transient network failures automatically, without any extra code in your application.

Timeout

The default timeout is 30 000 ms (30 seconds) per request. Override it in the config:

const sdk = await createMarzbanSDK({
  // ...
  timeout: 10_000, // 10 seconds
})

Pass timeout: 0 to disable the timeout entirely (wait forever). This is not recommended for production.

Automatic retries

When a request fails, the SDK retries it automatically using exponential backoff:

AttemptDelay
1st retry1 s
2nd retry2 s
3rd retry4 s
4th retry8 s
…… (capped at 30 s)

The default is 3 retries (4 total attempts). Change it:

const sdk = await createMarzbanSDK({
  // ...
  retries: 5,  // 5 retries, 6 total attempts
})

Set retries: 0 to disable retries:

const sdk = await createMarzbanSDK({
  // ...
  retries: 0,
})

What gets retried

Only safe methods — GET, HEAD, OPTIONS — are retried, and only for:

  • Network errors (e.g. a dropped connection with no response)
  • 5xx and 429 responses

The method check always comes first: POST, PUT, PATCH, and DELETE are never retried, not even on a network error — re-sending a request whose outcome is unknown could duplicate a write (e.g. restartCore).

The one exception is the login request, which is retried on network errors since re-issuing a token has no side effects.

Other 4xx responses are never retried.

401 re-auth retry

The SDK has a separate, built-in mechanism for 401 responses:

  1. A 401 Unauthorized response triggers sdk.authorize().
  2. After a fresh token is obtained, the original request is retried once with the new token.
  3. If re-auth fails, the error is propagated to the caller.

This happens transparently — your code never sees the 401.

WebSocket reconnects

WebSocket log streams don't use retries at all — they have their own reconnect policy: exponential backoff with jitter, bounded by a time budget (10 minutes by default) instead of an attempt count.

const stream = await sdk.logs.connectByCore({
  onMessage: (data) => console.log(data),
  onError: (error) => console.error('Stream ended', error.code, error.message),
})

If the connection drops after opening, the SDK re-authenticates and reconnects automatically until it succeeds or the budget runs out. A failed first connect rejects connect*() immediately by default (reconnect.initial opts into retrying that too). See Reconnect & Stream Lifecycle for the full policy, including the config.reconnect SDK-wide default and per-call overrides.

Multiple SDK instances

Each MarzbanSDK instance has its own isolated Axios instance. Retry counters, tokens, and connections from one instance never affect another.

const sdkA = await createMarzbanSDK({ baseUrl: 'https://server-a.com', ... })
const sdkB = await createMarzbanSDK({ baseUrl: 'https://server-b.com', ... })

// Independent connections, tokens, and retry state

Cleanup

Call sdk.destroy() to release everything the SDK owns — active WebSocket connections, webhook listeners, the stored token — and enter a terminal, idempotent shutdown state:

await sdk.destroy()

This is particularly important in long-running processes or tests where you create multiple SDK instances. See Lifecycle & shutdown for the full destroy() contract, including which operations reject afterward.

On this page