flexmeasures.api.common.rate_limiting
Rate limiting for the FlexMeasures API.
Two limits apply:
a generous default limit on every endpoint under
/api/, anda stricter limit on the endpoints that trigger expensive computation (scheduling and forecasting).
Both are configurable (see FLEXMEASURES_API_DEFAULT_RATE_LIMIT and FLEXMEASURES_API_TRIGGER_RATE_LIMIT),
and both can be overridden per account, by assigning the account a Plan with
default_rate_limit and/or trigger_rate_limit set (see flexmeasures.data.models.user.Plan).
The special value “unlimited” exempts an account from a limit.
The two limits count differently. The default limit counts every request, including those we refuse (so that a client hammering us with bad credentials is bounded, too). The trigger limit only counts triggers we accepted, because it exists to protect the expensive computation which those set in motion: a client whose payload we rejected did not cost us a schedule, and should not pay for one.
The default limit is counted per account, so that all users and integrations of an account share one budget (set in the account’s plan).
By an integration, we mean anything which calls the API with an auth token, rather than through the UI.
A user of a consultancy account counts against the consultancy’s account, also when acting on a client account’s assets.
The UI is the exception: its pages call the API with the session of the user who is logged in, rather than with a token,
and those requests count against a budget per user (FLEXMEASURES_UI_RATE_LIMIT), which plans do not touch.
That way, an integration which uses up its account’s budget does not lock the account’s users out of the UI,
and browsing the UI does not spend the budget which the account’s integrations need.
Note that the limiter runs before authentication, so unauthenticated callers are counted by IP address.
Each refused request is logged as a warning, saying who hit which limit, on which endpoint (and, for triggers, about which asset), because a budget is shared by everyone it counts, so a 429 alone does not tell a host who used it up.
Neither limit applies on a play server (FLEXMEASURES_MODE is “play”), which is the mode for running
simulations ― precisely the tight trigger loop the trigger limit exists to stop. Note that this is about
the play mode, not about running in development: a development server rate-limits like any other, so that
what you see there is what you get in production.
Functions
- flexmeasures.api.common.rate_limiting.default_key_func() str
Count requests against the user’s account, or against the IP address if unauthenticated.
All users and integrations of an account share one budget, which is what the account’s plan sets. A consultant counts against their own account, also when acting on a client account’s assets, because it is the consultancy’s integration which makes the requests. Requests from the UI count against the user instead, in a budget of their own (see default_limit).
- flexmeasures.api.common.rate_limiting.default_limit() str
The UI’s budget per user for requests from the UI, and otherwise the account’s budget.
- flexmeasures.api.common.rate_limiting.limit_triggers()
Decorator for endpoints which trigger expensive computation, like scheduling.
- flexmeasures.api.common.rate_limiting.rate_limit_exceeded_handler(error)
Respond to a hit rate limit like we respond to other API errors.
The Retry-After and X-RateLimit-* headers are added by the limiter itself, after this request.
- flexmeasures.api.common.rate_limiting.register_at(app: Flask)
Set up rate limiting, storing counts in the Redis we already connected to.
- flexmeasures.api.common.rate_limiting.reset_rate_limits(account: Account, limit_names: Iterable[str] = ('default', 'trigger')) list[str]
Clear an account’s rate-limit counters, so that its requests are accepted again straight away.
We clear the counters of the limits in effect for the account (its plan’s, or the server-wide ones). The limit’s amount is part of a counter’s key, so counters left over from a limit that no longer applies are not counted against anymore, and simply expire. A limit the account is exempt from (“unlimited”) has nothing to reset, and is skipped.
Returns the names of the limits that were reset.
- flexmeasures.api.common.rate_limiting.trigger_key_func() str
Count triggers against the account, or against the IP address if unauthenticated.
The account is what a plan belongs to, so its trigger budget is shared by all of its users and assets.
- flexmeasures.api.common.rate_limiting.warn_about_deprecated_settings(app: Flask)
Tell hosts who still choose what triggers are counted against that this choice is gone.