HTTP client for the Longbridge OpenAPI REST API.
Used for:
- Refreshing the legacy API Key
access_tokenvia/v1/token/refresh - Arbitrary HTTP calls (e.g.
/v1/trade/execution/today)
HTTP requests are signed with HMAC-SHA256 using the configured
app_secret. OAuth 2.0 access tokens are passed directly via the
Authorization: Bearer header (no signing required).
This module uses Finch under the hood.
Examples
config = Longbridge.Config.new(
token: "current-access-token",
app_key: "your-app-key",
app_secret: "your-app-secret"
)
{:ok, %{token: new_token, expired_at: ts}} =
Longbridge.HTTPClient.refresh_access_token(config)
Summary
Types
Optional OAuth token refresher.
Functions
Builds a URL query string from a keyword list.
Obtains a one-time-password (OTP) token for socket authentication.
Refreshes the legacy API Key access_token.
Performs a signed HTTP request to the Longbridge REST API.
Performs a signed HTTP request and unwraps the Longbridge response envelope.
Computes the Longbridge HMAC-SHA256 signature header.
Types
@type token_refresher() :: (Longbridge.Config.t() -> {:ok, Longbridge.Config.t()} | {:error, term()})
Optional OAuth token refresher.
Called by request/5 to obtain a fresh token before retrying a
request that returned an auth error. The refresher receives the
current config and returns either a new config with a fresh
token (which request/5 will use to retry the request once) or
an error tuple (which request/5 returns unchanged).
Used together with the :refresh_on_401 option.
Functions
Builds a URL query string from a keyword list.
Nil values are filtered out. Values are URI-encoded.
Example
iex> Longbridge.HTTPClient.build_query(market: "US", symbol: nil)
"market=US"
@spec get_socket_token( Longbridge.Config.t(), keyword() ) :: {:ok, String.t()} | {:error, term()}
Obtains a one-time-password (OTP) token for socket authentication.
The Longbridge socket protocol requires an OTP token obtained via
GET /v1/socket/token. The legacy access token (config.token)
cannot be used directly for socket auth — it must be exchanged
for an OTP first.
Returns {:ok, otp_token} or {:error, reason}.
@spec refresh_access_token( Longbridge.Config.t(), keyword() ) :: {:ok, %{token: String.t(), expired_at: non_neg_integer()}} | {:error, term()}
Refreshes the legacy API Key access_token.
Calls GET /v1/token/refresh?expired_at=<unix> with the current
app_key / app_secret / access_token and returns the new
token and expiry.
Options
:expired_at— When the new token should expire (Unix timestamp, seconds). Defaults to 90 days from now.:http_url— Override the HTTP base URL (default from config).:finch— Override the Finch instance (defaultLongbridge.Finch).
Returns {:ok, %{token: String.t(), expired_at: non_neg_integer()}}
or {:error, reason}.
@spec request(atom(), String.t(), String.t(), Longbridge.Config.t(), keyword()) :: {:ok, term()} | {:error, term()}
Performs a signed HTTP request to the Longbridge REST API.
Signs the request with HMAC-SHA256 using config.app_secret and
the Longbridge signing scheme. Returns the parsed JSON body on
success, or {:error, reason}.
body is a binary (the raw HTTP body, e.g. JSON-encoded string).
Pass "" for GET requests.
Options
:http_url— Override the HTTP base URL.:params— URL query string to append to the path (e.g."a=1&b=2").:finch— Override the Finch instance (defaultLongbridge.Finch).:token_refresher— A(Config.t() -> {:ok, Config.t()} | {:error, term()})function called once if the request returns a token-expired error. On success, the request is retried once with the new token.:on_token_refresh— A(Config.t() -> any())function called after a successful token refresh, with the new config. Useful for updating a parent GenServer's state.
Token refresh retry
When a :token_refresher is provided and the server responds with
a token-expired error, the refresher is called and the request is
retried exactly once with the new token. If the refresher fails or
the retry also fails, the original error is returned.
@spec request_json(atom(), String.t(), String.t(), Longbridge.Config.t(), keyword()) :: {:ok, map() | list()} | {:error, term()}
Performs a signed HTTP request and unwraps the Longbridge response envelope.
Longbridge REST API responses all share the form:
{"code": 0, "data": {...}}
This helper calls request/5 and extracts the "data" field on success
(code 0), or returns an {:error, {:api_error, code, message}} tuple.
Computes the Longbridge HMAC-SHA256 signature header.
Public for testing. The signing scheme matches the official Python SDK:
canonical = METHOD|URI|PARAMS|authorization:TOKEN
\nx-api-key:KEY
\nx-timestamp:TS
\n|authorization;x-api-key;x-timestamp|
if body != "": canonical += sha1(body).hex
sign_str = "HMAC-SHA256|" + sha1(canonical).hex
signature = hmac_sha256(secret, sign_str).hex
header = "HMAC-SHA256 SignedHeaders=..., Signature={sig}"