Skip to content

alisaifee/limits

Folders and files

NameName
Last commit message
Last commit date

Latest commit

0b473c2 · Feb 17, 2025
Feb 8, 2025
Feb 17, 2025
Feb 15, 2025
Jan 5, 2025
Apr 22, 2022
Feb 14, 2025
Dec 22, 2022
Aug 18, 2017
Jan 10, 2022
Jun 6, 2022
Feb 5, 2025
Aug 1, 2022
Feb 9, 2025
Jan 16, 2025
Jan 1, 2023
Nov 1, 2022
Nov 27, 2024
Feb 5, 2025
Nov 28, 2024
Jan 9, 2015
Feb 8, 2024
Jan 7, 2025
Mar 8, 2023
Nov 27, 2024
Nov 30, 2024
Feb 8, 2024

Repository files navigation

limits

docs ci codecov pypi pypi-versions license

limits is a python library to perform rate limiting with commonly used storage backends (Redis, Memcached, MongoDB & Etcd).

Supported Strategies

Fixed Window

This strategy resets at a fixed interval (start of minute, hour, day etc). For example, given a rate limit of 10/minute the strategy will:

  • Allow 10 requests between 00:01:00 and 00:02:00
  • Allow 10 requests at 00:00:59 and 10 more requests at 00:01:00
Fixed Window (Elastic)
Identical to Fixed window, except every breach of rate limit results in an extension to the time out. For example a rate limit of 1/minute hit twice within a minute will result in a lock-out for two minutes.
Moving Window

Moving window strategy enforces a rate limit of N/(m time units) on the last m time units at the second granularity.

For example, with a rate limit of 10/minute:

  • Allow 9 requests that arrive at 00:00:59
  • Allow another request that arrives at 00:01:00
  • Reject the request that arrives at 00:01:01
Sliding Window Counter

The sliding window counter strategy enforces a rate limit of N/(m time units) by approximating the moving window strategy, with less memory use. It approximates the behavior of a moving window by maintaining counters for two adjacent fixed windows: the current and the previous windows.

To determine if a request should be allowed, we assume the requests in the previous window were distributed evenly over its duration and use a weighted sum of the previous and current window counts to calculate the effective current capacity.

Storage backends

Dive right in

Initialize the storage backend

from limits import storage
memory_storage = storage.MemoryStorage()
# or memcached
memcached_storage = storage.MemcachedStorage("memcached://localhost:11211")
# or redis
redis_storage = storage.RedisStorage("redis://localhost:6379")
# or use the factory
storage_uri = "memcached://localhost:11211"
some_storage = storage.storage_from_string(storage_uri)

Initialize a rate limiter with the Moving Window Strategy

from limits import strategies
moving_window = strategies.MovingWindowRateLimiter(memory_storage)

Initialize a rate limit

from limits import parse
one_per_minute = parse("1/minute")

Initialize a rate limit explicitly

from limits import RateLimitItemPerSecond
one_per_second = RateLimitItemPerSecond(1, 1)

Test the limits

assert True == moving_window.hit(one_per_minute, "test_namespace", "foo")
assert False == moving_window.hit(one_per_minute, "test_namespace", "foo")
assert True == moving_window.hit(one_per_minute, "test_namespace", "bar")

assert True == moving_window.hit(one_per_second, "test_namespace", "foo")
assert False == moving_window.hit(one_per_second, "test_namespace", "foo")
time.sleep(1)
assert True == moving_window.hit(one_per_second, "test_namespace", "foo")

Check specific limits without hitting them

assert True == moving_window.hit(one_per_second, "test_namespace", "foo")
while not moving_window.test(one_per_second, "test_namespace", "foo"):
    time.sleep(0.01)
assert True == moving_window.hit(one_per_second, "test_namespace", "foo")

Query available capacity and reset time for a limit

assert True == moving_window.hit(one_per_minute, "test_namespace", "foo")
window = moving_window.get_window_stats(one_per_minute, "test_namespace", "foo")
assert window.remaining == 0
assert False == moving_window.hit(one_per_minute, "test_namespace", "foo")
time.sleep(window.reset_time - time.time())
assert True == moving_window.hit(one_per_minute, "test_namespace", "foo")

Links