Low-level HTTP client for the Moneyhub data API (api_url).
Every resource module (MoneyHub.Accounts, MoneyHub.Transactions,
MoneyHub.Payments, ...) delegates its HTTP calls through here, so
retry behaviour, rate-limit handling, and error normalisation are
implemented exactly once.
You generally won't call this module directly - use the resource modules
instead. It's part of the public API for advanced use cases (calling
endpoints not yet wrapped by a dedicated module) via request/2.
Retries
Moneyhub's rate limit (per their docs) is approximately 1000 requests per
minute per client, returning 429 with a Retry-After header when
exceeded. request/2 retries 429 responses automatically (honouring
Retry-After, capped by :max_retry_after_ms) and retries 5xx/network
errors with capped exponential backoff, up to :max_retries (default 2).
Emits [:money_hub, :request, :start | :stop | :exception] telemetry
events around every call.
Summary
Functions
Issues an HTTP request against the data API.
Safely unwraps a %{"data" => [...]} envelope, as returned by most list
endpoints, falling back to the raw body unchanged when it isn't a map
with a "data" key (for example if the body is already a bare list).
Types
@type method() :: :get | :post | :patch | :put | :delete
@type token() :: String.t()
Functions
@spec request( MoneyHub.Config.t(), keyword() ) :: {:ok, Req.Response.t()} | {:error, MoneyHub.Error.t()}
Issues an HTTP request against the data API.
Options
:method- required, one of:get,:post,:patch,:put,:delete.:path- required, relative toconfig.api_url, e.g."/accounts".:token- required, a bearer access token.:query- a map/keyword list of query parameters.:json- a request body to be JSON-encoded.:max_retries- overrides the default retry count.:max_retry_after_ms- caps how long aRetry-Afterdriven sleep may be, to bound worst-case latency.
Safely unwraps a %{"data" => [...]} envelope, as returned by most list
endpoints, falling back to the raw body unchanged when it isn't a map
with a "data" key (for example if the body is already a bare list).
Resource modules use this instead of response.body["data"] || response.body
directly, since that pattern raises ArgumentError when response.body
is a list (lists only support atom-keyed Access calls).