{"id":17874051,"url":"https://github.com/devkral/django-fast-ratelimit","last_synced_at":"2026-03-08T11:36:36.923Z","repository":{"id":34844585,"uuid":"184454457","full_name":"devkral/django-fast-ratelimit","owner":"devkral","description":"A fast ratelimit implementation based on the django cache framework","archived":false,"fork":false,"pushed_at":"2024-07-02T00:35:45.000Z","size":186,"stargazers_count":6,"open_issues_count":0,"forks_count":2,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-22T11:51:08.010Z","etag":null,"topics":["django","ratelimit"],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/devkral.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-05-01T17:30:38.000Z","updated_at":"2025-07-04T14:28:10.000Z","dependencies_parsed_at":"2023-12-27T07:32:39.374Z","dependency_job_id":"6b71792a-ff76-4457-b23c-7d0a88529c1f","html_url":"https://github.com/devkral/django-fast-ratelimit","commit_stats":{"total_commits":67,"total_committers":1,"mean_commits":67.0,"dds":0.0,"last_synced_commit":"651edcaa3eb3b96575d40441e6d858bb3c2f566f"},"previous_names":[],"tags_count":62,"template":false,"template_full_name":null,"purl":"pkg:github/devkral/django-fast-ratelimit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devkral%2Fdjango-fast-ratelimit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devkral%2Fdjango-fast-ratelimit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devkral%2Fdjango-fast-ratelimit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devkral%2Fdjango-fast-ratelimit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/devkral","download_url":"https://codeload.github.com/devkral/django-fast-ratelimit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/devkral%2Fdjango-fast-ratelimit/sbom","scorecard":{"id":337810,"data":{"date":"2025-08-11","repo":{"name":"github.com/devkral/django-fast-ratelimit","commit":"a442e0a3ab200953387e1500fc98fc9233c1fd87"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.8,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/devkral/django-fast-ratelimit/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/devkral/django-fast-ratelimit/test.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/test.yml:27: update your workflow using https://app.stepsecurity.io/secureworkflow/devkral/django-fast-ratelimit/test.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:20","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   0 out of   1 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":0,"reason":"18 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: PYSEC-2024-102 / GHSA-5hgc-2vfp-mqvc","Warn: Project is vulnerable to: PYSEC-2024-68 / GHSA-795c-9xpc-xw6g","Warn: Project is vulnerable to: PYSEC-2025-47 / GHSA-7xr5-9hcq-chf9","Warn: Project is vulnerable to: PYSEC-2024-156 / GHSA-8498-2h75-472j","Warn: Project is vulnerable to: PYSEC-2025-37 / GHSA-8j24-cjrq-gr2m","Warn: Project is vulnerable to: PYSEC-2024-58 / GHSA-9jmf-237g-qf46","Warn: Project is vulnerable to: PYSEC-2024-59 / GHSA-f6f8-9mx6-9mx2","Warn: Project is vulnerable to: PYSEC-2024-67 / GHSA-jh75-99hh-qvx9","Warn: Project is vulnerable to: PYSEC-2024-157 / GHSA-m9g8-fxxm-xg86","Warn: Project is vulnerable to: PYSEC-2025-13 / GHSA-p3fp-8748-vqfq","Warn: Project is vulnerable to: PYSEC-2024-70 / GHSA-pv4p-cwwg-4rph","Warn: Project is vulnerable to: PYSEC-2025-1 / GHSA-qcgg-j2x8-h9g8","Warn: Project is vulnerable to: PYSEC-2024-56 / GHSA-qg2p-9jwr-mmqf","Warn: Project is vulnerable to: PYSEC-2024-69 / GHSA-r836-hh6v-rg5g","Warn: Project is vulnerable to: GHSA-rrqc-c2jx-6jgv","Warn: Project is vulnerable to: PYSEC-2024-57 / GHSA-x7q2-wr7g-xqmf","Warn: Project is vulnerable to: PYSEC-2025-14 / GHSA-wqfg-m96j-85vm","Warn: Project is vulnerable to: PYSEC-2024-187 / GHSA-rqc4-2hc7-8c8v"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-18T05:09:19.737Z","repository_id":34844585,"created_at":"2025-08-18T05:09:19.737Z","updated_at":"2025-08-18T05:09:19.737Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278444714,"owners_count":25987830,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-05T02:00:06.059Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["django","ratelimit"],"created_at":"2024-10-28T11:07:28.221Z","updated_at":"2025-10-05T11:25:03.262Z","avatar_url":"https://github.com/devkral.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# django-fast-ratelimit\n\nDjango-fast-ratelimit provides a secure and fast ratelimit facility based on the django caching framework.\nIt uses a \"Fixed window counter\"-algorithm based on:\nhttps://medium.com/figma-design/an-alternative-approach-to-rate-limiting-f8a06cf7c94c\n\n## Installation\n\n```bash\npip install django-fast-ratelimit\n\n```\n\nNote: pip \u003e= 19 is required\n\nNote: version 5 renames package from ratelimit to django_fast_ratelimit\n\n## usage\n\nApply everywhere where wanted in the django app without restrictions:\n\ninstall companion library django-fast-iprestrict \u003e= 0.6.0\n\nDecorator:\n\n```python\nimport django_fast_ratelimit as ratelimit\n\n@ratelimit.decorate(key=\"ip\", rate=\"1/s\")\ndef expensive_func(request):\n    # how many ratelimits request limiting\n    if request.ratelimit[\"request_limit\"] \u003e 0:\n        # reschedule with end of rate epoch\n        return request_waiting(request.ratelimit[\"end\"])\n\n```\n\nor async\n\n```python\nimport django_fast_ratelimit as ratelimit\nimport asyncio\n\n@ratelimit.decorate(key=\"ip\", rate=\"1/s\")\nasync def expensive_func(request):\n    # how many ratelimits request limiting\n    if request.ratelimit[\"request_limit\"] \u003e 0:\n        # reschedule with end of rate epoch\n        await asyncio.sleep(request.ratelimit[\"end\"])\n\n```\n\nblocking Decorator (raises RatelimitError):\n\n```python\nimport django_fast_ratelimit as ratelimit\n\n@ratelimit.decorate(key=\"ip\", rate=\"1/s\", block=True, decorate_name=\"ratelimit\", methods=ratelimit.UNSAFE)\ndef expensive_func(request):\n    # how many ratelimits request limiting\n    if request.ratelimit[\"end\"] \u003e 0:\n\n```\n\ndecorate View (requires group):\n\n```python\nimport django_fast_ratelimit as ratelimit\nfrom django.views.generic import View\nfrom django.utils.decorators import method_decorator\n\n\n@method_decorator(ratelimit.decorate(\n  key=\"ip\", rate=\"1/s\", block=True, methods=ratelimit.SAFE, group=\"required\"\n), name=\"dispatch\")\nclass FooView(View):\n    ...\n\n```\n\nmanual\n\n```python\nimport django_fast_ratelimit as ratelimit\n\n\ndef func(request):\n    ratelimit.get_ratelimit(key=\"ip\", rate=\"1/s\", request=request, group=\"123\", action=ratelimit.Action.INCREASE)\n    # or only for GET\n    ratelimit.get_ratelimit(\n        key=\"ip\", rate=\"1/s\", request=request, group=\"123\", methods=\"GET\", action=ratelimit.Action.INCREASE\n    )\n    # also simple calls possible (note: key in bytes format)\n    ratelimit.get_ratelimit(\n        key=b\"abc\", rate=\"1/s\", group=\"123\", action=ratelimit.Action.INCREASE\n    )\n    # retrieve ratelimit\n    rlimit = ratelimit.get_ratelimit(\n        key=\"ip\", rate=\"1/s\", request=request, group=\"123\"\n    )\n    # reset (clears internal counter)\n    counter_before_reset = rlimit.reset()\n    # reset epoch (resets to the start of request/epoch)\n    counter_before_reset = rlimit.reset(request)\n    # decrease counter by arbitary amount\n    rlimit.reset(19)\n    # increase counter by arbitary amount\n    rlimit.reset(-19)\n\n    # check constraints of rate\n    r = ratelimit.parse_rate(\"1/s\")  # returns tuple (amount, period)\n    assert(r[1]==1)  #  assert period is 1 second\n    # for simple naming use o2g (object to group)\n    ratelimit.get_ratelimit(\n        key=b\"abc\", rate=r, group=ratelimit.o2g(func), action=ratelimit.Action.INCREASE\n    )\n\n```\n\nmanual async\n\n```python\nimport django_fast_ratelimit as ratelimit\n\n\nasync def func(request):\n    # retrieve ratelimit\n    rlimit = await ratelimit.aget_ratelimit(\n        key=\"ip\", rate=\"1/s\", request=request, group=\"123\"\n    )\n    # reset (clears internal counter)\n    await rlimit.areset()\n    # reset epoch (resets to the start of request/epoch)\n    await rlimit.areset(request)\n    # decrease counter by arbitary amount\n    await rlimit.areset(19)\n    # increase counter by arbitary amount\n    await rlimit.reset(-19)\n```\n\n## parameters\n\n### ratelimit.get_ratelimit:\n\n-   group: group name, can be callable (fun(request, action))\n-   rate: rate limit, multiple modes\n    Note: if count (first argument) is 0, then it raises the Disabled exception, the second argument must be greater then 0\n    -   str: default mode , specify rate in form of \"1/4s\" or \"2/s\" or \"2/m\"\n    -   2 element tuple/list: first argument is amount, second are seconds\n    -   callable: can return either string or 2 element tuple/list\n        None (default): raise ValueError if key function doesn't return False to disable further execution, for django-fast-iprestrict\n-   methods: set of checked methods, can be callable (fun(request, group, action)), modes:\n    -   callable(request, group, action): allow dynamic\n    -   ratelimit.ALL (default): all methods are checked\n    -   \\(\"HEAD\", \"GET\"\\): list of checked methods\n    -   ratelimit.invertedset([\"HEAD\", \"GET\"]): inverted set of checked methods. Here: every method is checked, except HEAD, GET\n-   request: ingoing request (optional if key supports it and methods=ratelimit.ALL (default))\n-   key: multiple modes possible:\n    -   bool: True: skip key (should only be used for baking), False: disable cache (like RATELIMIT_ENABLED=False)\n    -   int: sidestep cache, value will be used for request_limit. 0 is for never blocking, \u003e=1 blocks\n    -   str: \"path.to.method:argument\"\n    -   str: \"inbuildmethod:argument\" see methods for valid arguments\n    -   str: \"inbuildmethod\" method which is ready to use for (request, group, action)\n    -   tuple,list: [\"method\"/callable, args...]: method (can be also inbuild or a callable) with arbitary arguments\n    -   bytes: static key (supports mode without request)\n    -   callable: check return of function (fun(request, group, action)), return must be string (converted to bytes), bytes, bool or int (see \"key\" for effects)\n-   empty_to: convert empty keys (b\"\") to parameter. Must be bytes, bool or int (see \"key\" for effects) (default: keep b\"\")\n-   cache: specify cache to use, defaults to RATELIMIT_DEFAULT_CACHE setting (default: \"default\")\n-   hash_algo: name of hash algorithm for creating cache_key (defaults to RATELIMIT_KEY_HASH setting (default: \"sha256\"))\n    Note: group is seperately hashed\n-   hashctx: optimation parameter, read the code and only use if you know what you are doing. It basically circumvents the parameter hashing and only hashes the key. If the key parameter is True even the key is skipped\n-   action {ratelimit.Action}:\n    -   PEEK: only lookup\n    -   INCREASE: count up and return result\n    -   RESET: return former result and reset\n    -   RESET_EPOCH: return count after reset of epoch. If neither epoch nor request is given like peek (default: {PEEK})\n-   epoch:\n    -   None: (default): use request as epoch\n    -   int: RESET_EPOCH resets by int. Negative int increases\n    -   object: attach counter to an open object (Note: it cannot be object() directly and neither an object with slots)\n\nreturns the dataclass Ratelimit\n\nor raises `ratelimit.Disabled` in case of the count in the rate is zero\n\n### ratelimit.Ratelimit\n\nFields\n\n-   count: how often in the window the ip whatever was calling\n-   limit: limit when it should block\n-   request_limit: \u003e=1 should block or reject, 0: should accept\n-   end: when does the block end\n-   group: group name\n-   group_key, cache: Optional, when specified reset and areset can be used, internal fields\n\nFunctions:\n\n-   can_reset: is a reset possible or were bypasses used\n-   reset: function to reset count if cache was used. When given an epoch the same as RESET_EPOCH\n-   areset: async version of reset\n-   check(block=False): raise RatelimitExceeded when block = True and ratelimit is exceeded\n-   acheck(wait=False, block=False): raise RatelimitExceeded when block = True and ratelimit is exceeded, wait for end of ratelimit duration when wait=True\n-   decorate_object(obj, name=\"ratelimit\", block=False, replace=False): attach to object obj with name and use old limits too, pass block to check\n-   adecorate_object(obj, name=\"ratelimit\", wait=False, block=False, replace=False): attach to object obj with name and use old limits too, pass block and wait to acheck\n\nNote: decorate_object with name=None behaves like check (except return value), the same applies for adecorate_object\n\narguments:\n\n-   wait: wait until end timestamp when ratelimit was exceeded. Next call should work again, applied before block\n-   block: raise a RatelimitExceeded exception\n-   replace: ignore potential old ratelimit object atttached to object and just replace it\n\nwhy only async methods have wait? It doesn't really block (only the userlandthread). In contrast to its sync equivalent it doesn't block the webserver significantly\n\nExample: decorate_object\n\n```python\nimport ratelimit\n\nclass Foo():\n    pass\n\nr = get_ratelimit(\n    group=\"foo\",\n    rate=\"1/s\",\n    key=b\"foo\",\n    action=ratelimit.Action.INCREASE,\n)\n\n# manual way\nfoo = r.decorate_object(Foo(), name=\"ratelimit\")\nif not foo.ratelimit.check():\n    raise ratelimit.RatelimitExceeded(\"custom message\", ratelimit=r)\nelse:\n    pass\n    # do cool stuff\n\n# simplified\n\nfoo2 = r.decorate_object(Foo(), block=True)\n\n# artistic (no point in doing so)\n\nr.decorate_object(Foo(), name=\"ratelimit_is_cool\").ratelimit_is_cool.check(block=True)\n\n# like check with instance of Foo() as return value\n\nfoo3 r.decorate_object(Foo(), name=None, wait=True)\n\n# decorate function\n\n@r.decorate_object(block=True)\ndef fn():\n    pass\n\n# of course also this works\n\n@r.decorate_object\ndef fn():\n    pass\n\n\n\n```\n\n### ratelimit.aget_ratelimit:\n\nsame as `get_ratelimit` but supports async methods and has an optional parameter:\n`wait`, which suspends the execution (via `asyncio.sleep`) for the time specified in rate (second argument).\nThis is only possible in async mode, as it would block too much in sync mode.\n\n### ratelimit.decorate:\n\nAll of ratelimit.get_ratelimit except request. group is here optional (except for decorations with method_decorator (no access to wrapped function)).\nAlso supports:\n\n-   block: should hard block with an RatelimitExceeded exception (subclass of PermissionDenied) or only annotate request with ratelimit\n-   decorate_name: under what name the ratelimit is attached to the request. set to None/empty to not decorate request. Uses Ratelimit.decorate_object. Defaults to \"ratelimit\"\n-   wait (only when async possible): suspends execution, triggers force_async when not specified\n-   force_async: fail when not called in an async context, can be set to False, to not fail in sync context, or to True to fail always when in sync only context\n\nwhy only async methods have wait? It doesn't really block (only the userlandthread). In contrast to its sync equivalent it doesn't block the webserver significantly\n\n## helpers\n\n### ratelimit.invertedset:\n\ninverts a collection, useful for http methods\n\n### ratelimit.get_RATELIMIT_TRUSTED_PROXY:\n\nget the `RATELIMIT_TRUSTED_PROXIES` parsed as set\n\nnote: this function uses a cached subfunction. If you change this setting while testing you may have to call:\n\n`ratelimit._get_RATELIMIT_TRUSTED_PROXY.cache_clear()`\n\n### ratelimit.get_ip:\n\nget client ip from request, using `RATELIMIT_TRUSTED_PROXIES` and forwarded headers\n\n```python\nimport ratelimit\n\nratelimit.get_ip(request)\n\n```\n\n### ratelimit.o2g:\n\nauto generate group names for method/function as input, see tests/test_decorators for more info\n\nExample:\n\n```python\nimport ratelimit\n\n\nclass O2gView(View):\n    def get(self, request, *args, **kwargs):\n        request.ratelimit2 = ratelimit.get_ratelimit(\n            group=ratelimit.o2g(self.get),\n            rate=\"1/s\",\n            key=b\"o2gtest\",\n            action=ratelimit.Action.INCREASE,\n        )\n        if request.ratelimit2.request_limit \u003e 0:\n            return HttpResponse(status=400)\n        return HttpResponse()\n\n```\n\n### ratelimit.RatelimitExceeded\n\nRaised when the ratelimit was exceeded\n\nException, required keyword argument is ratelimit with the ratelimit.\nThe next arguments are passed to the underlying standard exception class for e.g. customizing the error message\n\n### ratelimit.Disabled\n\nStronger variant of RatelimitExceeded. Used for cases where limit is 0 and there is no way to pass the ratelimit.\nIt is a shortcut for disabling api.\n\nNote: it is weaker than the setting `RATELIMIT_ENABLED`\n\nNote: it isn't a subclass from RatelimitExceeded because APIs should be able to differ both cases\n\nNote: in contrast to RatelimitExceeded it is raised in (a)get_ratelimit and when using decorate, the view function isn't called.\n\n### ratelimit.protect_sync_only\n\nfor libraries. In case of async return protected asyncified function otherwise call library directly\n\n## recipes\n\njitter:\n\n```python\nimport ratelimit\nimport asyncio\nimport secrets\n\nasync def foo()\n\n    r = await ratelimit.aget_ratelimit(\n        group=\"foo\",\n        rate=\"1/s\",\n        key=b\"foo\",\n        action=ratelimit.Action.INCREASE,\n    )\n    # 100ms jitter\n    await asyncio.sleep(secrets.randbelow(100) / 100)\n    # raise when limit reached, wait until full second jitter is eliminated in raise case as end was created before the jitter\n    await r.acheck(wait=True, block=True)\n\n```\n\n## methods\n\nSee in methods which methods are available. Here some of them:\n\n-   `static`: use static key defined by argument, if no argument was specified default to b\"static\", the argument is automatically converted to bytes\n    Note: the conversion for non-bytes objects is str(obj).encode(\"utf8\")\n    Note: it is also possible to specify a bytes key to provide a static argument\n-   `ip`: use ip address as key, argument: [netmask ipv4/]netmask ipv6\n-   `user`: authenticated user primary key or b\"\"\n-   `user_or_ip`: use autenticated user primary key as key. If not autenticated fallback to ip, also with netmask argument.\n-   `user_and_ip`: same like user_or_ip except that the ip matching also applies for authenticated users\n-   `ip_exempt_user`: same like user_or_ip except that authenticated users are exempted or the inversion (true). Takes 0-2 arguments (netmask and invert (true,false))\n    \n    has the special argument true, which cause the inversion of the user check (false is also possible but without effect). The behavior for multiple true,false is unspecified.\n    Setting to true exempts requests without a user\n\n    When specified with reset actions: reset the ip key with the reset action when a user was found. If inverted the inversion is the case.\n\n    Note: true,false must be strings, True (bool) is used by netmask\n-   `user_or_ip_exempt`:\n    user with fallback ip. Exempts for user specs.\n\n    Note:\n    when permissions, user_ok and staff_ok are not specified exempts for superuser only.\n    Same behavior for reset like ip_exempt_user\n\n    With following parameters (either list or comma seperated):\n\n    -   netmask: either [\"netmask\", ...] or \"netmask:.../...\"\n    -   not_use_user_pk: when not exempting, the default uses preferred the authenticated user pk, this param skips this and directly fallback to ip, note: the boolean name is inverted\n    -   not_use_ip: block if no user was found, together with not_use_user_pk block non privileged access\n    -   permissions: either [\"permission\", perm1, perm2, ..] or \"permission:perm1\"\n    -   user_ok: string, exempts when authenticated user\n    -   staff_ok: string, exempts when staff\n    -   invert: flag, inverts the exemption condition, so only users with the parameters are checked, the rest is exempted\n\n    When resetting the key is only resetted if exempted (higher privilege). The invert stuff applies here too (see `ip_exempt_user`).\n\n-   `get`: generate key from multiple sources, input can be multiple input args or a dict with options\n\n## settings\n\n-   `RATELIMIT_TESTCLIENT_FALLBACK`: in case instead of a client ip a testclient is detected map to the fallback. Set to \"invalid\" to fail. Default ::1\n-   `RATELIMIT_GROUP_HASH`: hash function which is used for the group hash (default: md5)\n-   `RATELIMIT_KEY_HASH`: hash function which is used as default for the key hash, can be overridden with hash_algo (default: md5)\n-   `RATELIMIT_ENABLED` disable ratelimit (e.g. for tests) (default: enabled)\n-   `RATELIMIT_ENABLE` deprecated old name of RATELIMIT_ENABLED\n-   `RATELIMIT_KEY_PREFIX`: internal prefix for the hash keys (so you don't have to create a new cache). Defaults to \"frl:\".\n-   `RATELIMIT_DEFAULT_CACHE`: default cache to use, defaults to \"default\" and can be overridden by cache parameter\n-   `RATELIMIT_TRUSTED_PROXIES`: \"all\" for allowing all ip addresses to provide forward informations, or an iterable with proxy ips (will be transformed to a set). Note there is a special ip: \"unix\" for unix sockets. Default: [\"unix\"]\n    Used headers are: `Forwarded`, `X-Forwarded-For`\n\n## Update Notes:\n\nin version 9.0.0: ip_exempt_superuser and ip_exempt_privileged are replaced by user_or_ip_exempt\n\nin version 8.0.0: rate is the 4th argument of a key function, I need it for django-fast-iprestrict\n\nin version 7.3.0: rate is now optional (when having an appropiate key (function))\n\nin version 7.2.0: `RATELIMIT_ENABLE` is renamed to `RATELIMIT_ENABLED`, the old setting is still available, note: in tests where this settings are changed dynamically you may have to import \\_get_RATELIMIT_ENABLED and clear the cache, in most cases this isn't neccessary\n\nin version 7.0.0 method, group and key functions take an additional parameter: action\n\nin version 6.0.0 some small new restrictions are introduced for key functions as string\n\nin version 5.0.0 the package was renamed to django_fast_ratelimit for having an unique namespace. Reason, we have now a companion library: django-fast-iprestrict\nSorry for the big breaking change.\n\nin version 4.0.0 most parameters were made keyword only (helps finding bugs).\n\nin version 3.0.0 the name parameter of (a)decorate_object was changed to ratelimit\n\nin version 2.0.0 the parameter `raise_on_limit` was removed and replaced by check(block=True)\n\nin version 1.0.0 the parameter `include_reset` was removed\n\nin version 1.2.0 reset_epoch calls return the counter before reset instead of the count after\n\n## TODO:\n\n-   document and test \"get\" inbuild method\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevkral%2Fdjango-fast-ratelimit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdevkral%2Fdjango-fast-ratelimit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdevkral%2Fdjango-fast-ratelimit/lists"}