Skip to content

Configuration

Pass retry and rate_limits to a client constructor to tune how requests are retried and paced. RateLimitState is what the client reports back about bitFlyer's own counters, via the rate_limit property.

RetryPolicy dataclass

RetryPolicy(
    attempts: int = 3,
    backoff: float = 0.5,
    max_backoff: float = 30.0,
    retry_statuses: frozenset[int] = (
        lambda: frozenset({500, 502, 503, 504})
    )(),
    retry_unsafe_methods: bool = False,
)

When and how far apart to retry a failed request.

Retries are deliberately conservative about writes. A 5xx or a dropped connection on POST /v1/me/sendchildorder is ambiguous — the order may already be live on the exchange — so retrying it risks a double fill. By default only safe methods are retried on those failures.

HTTP 429 is always retried regardless of method: a rate-limited request was rejected before it reached the matching engine, so replaying it cannot duplicate an order.

Attributes:

Name Type Description
attempts int

Total attempts per request, including the first one. 1 disables retrying.

backoff float

Base delay in seconds. Doubles each attempt.

max_backoff float

Ceiling for a single delay, in seconds.

retry_statuses frozenset[int]

HTTP status codes worth retrying, on top of 429.

retry_unsafe_methods bool

Retry POST too. Only enable this if every call you make is idempotent, or you reconcile orders afterwards.

should_retry

should_retry(method: str, status_code: int | None) -> bool

Whether a failure is worth another attempt.

Parameters:

Name Type Description Default
method str

HTTP method of the failed request.

required
status_code int | None

Status code received, or None for a transport-level failure (timeout, reset connection) where the outcome is unknown.

required

delay

delay(
    attempt: int, retry_after: float | None = None
) -> float

Seconds to sleep before attempt, which is 1-based.

Honours a server-supplied retry_after when present, otherwise uses exponential backoff with full jitter on the lower half of the interval so that concurrent clients do not retry in lockstep.

RateLimits dataclass

RateLimits(
    general: int | None = 500,
    order: int | None = 300,
    period: float = 300.0,
)

Client-side request budget, mirroring bitFlyer's published limits.

The client tracks its own request timestamps and sleeps before sending when a window is full, so bursts get spread out instead of coming back as 429s. This is a courtesy backstop, not a guarantee: bitFlyer counts per IP and per account, so other processes sharing either will not be visible here.

One limit is not modelled here: bitFlyer separately caps orders of size 0.1 or less at 100 per minute, aggregated across every market, and drops you to 10 per minute for an hour if you exceed it. Sizing that budget needs to know each order's size, so pace small orders yourself.

Attributes:

Name Type Description
general int | None

Requests allowed per period across all endpoints, or None for no client-side limit. bitFlyer's own figure is 500 per 5 minutes, applied per IP and again per account.

order int | None

Requests allowed per period across sendchildorder, sendparentorder and cancelallchildorders, which share one tighter budget of 300 per 5 minutes. None disables just this limit. Single-order cancels are not metered here.

period float

Length of the window in seconds.

disabled classmethod

disabled() -> RateLimits

A budget that never delays a request.

RateLimitState dataclass

RateLimitState(
    remaining: int | None = None,
    period: int | None = None,
    reset: int | None = None,
)

bitFlyer's own view of your rate limit, read from the response headers.

Attributes:

Name Type Description
remaining int | None

Requests left in the current window.

period int | None

Seconds left in the current window.

reset int | None

Unix timestamp at which the window resets.

from_response classmethod

from_response(response: Response) -> RateLimitState | None

Parse the X-RateLimit-* headers, or None if absent.