PlaidEx.HTTP.RateLimiter (plaid_ex v1.0.0)

Copy Markdown View Source

ETS-backed token bucket rate limiter with per-tenant isolation.

Each tenant (or :global for single-tenant deployments) gets its own token bucket. Buckets are refilled on a configurable interval.

Plaid's rate limits vary by plan and endpoint. This limiter acts as a client-side guard to prevent hammering Plaid before their server responds with RATE_LIMIT_EXCEEDED. When Plaid does return a rate limit error, the HTTP client's retry logic handles it with backoff.

Architecture

Uses a GenServer to own the ETS table (so the table survives the calling process crashing) with an ETS-based fast path for the common check case. Refill happens via Process.send_after.

Bucket defaults

  • :global — 200 requests/second (conservative Plaid production limit)
  • Per-tenant — 50 requests/second (safe default for multi-tenant)

Override by calling configure_tenant/2.

Summary

Functions

Checks whether a request is allowed under the current rate limit.

Returns a specification to start this module under a supervisor.

Configures a custom rate limit for a specific tenant.

Functions

check(tenant_id, _)

@spec check(String.t() | :global, PlaidEx.Config.t()) :: :ok | {:error, :rate_limited}

Checks whether a request is allowed under the current rate limit.

Returns :ok if allowed, {:error, :rate_limited} if the bucket is empty. Does NOT block — callers should treat :rate_limited as a fast-fail signal and apply their own backoff.

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

configure_tenant(tenant_id, opts)

@spec configure_tenant(
  String.t() | :global,
  keyword()
) :: :ok

Configures a custom rate limit for a specific tenant.

PlaidEx.HTTP.RateLimiter.configure_tenant("acme", requests_per_second: 100)

start_link(opts \\ [])

@spec start_link(keyword()) :: GenServer.on_start()