{"id":27643799,"url":"https://github.com/weyoss/throtty","last_synced_at":"2025-07-24T23:14:12.422Z","repository":{"id":143918785,"uuid":"121254616","full_name":"weyoss/throtty","owner":"weyoss","description":"Yet another rolling window rate limiter.","archived":false,"fork":false,"pushed_at":"2020-01-13T12:19:17.000Z","size":12,"stargazers_count":2,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-27T20:53:37.637Z","etag":null,"topics":["nodejs","rate-limiting","redis","rolling-windows","throtting","throttle"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","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/weyoss.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,"zenodo":null}},"created_at":"2018-02-12T14:11:51.000Z","updated_at":"2021-08-22T14:27:39.000Z","dependencies_parsed_at":null,"dependency_job_id":"5099eabf-a33f-4514-86d4-efe446380675","html_url":"https://github.com/weyoss/throtty","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/weyoss/throtty","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fthrotty","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fthrotty/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fthrotty/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fthrotty/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/weyoss","download_url":"https://codeload.github.com/weyoss/throtty/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/weyoss%2Fthrotty/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266922094,"owners_count":24006968,"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-07-24T02:00:09.469Z","response_time":99,"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":["nodejs","rate-limiting","redis","rolling-windows","throtting","throttle"],"created_at":"2025-04-24T00:18:19.388Z","updated_at":"2025-07-24T23:14:12.414Z","avatar_url":"https://github.com/weyoss.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Throtty - Yet another rolling window rate limiter\n\nThrotty is an efficient rate limiter for Node.js. Useful when you need to rate limit/throttle \nAPI clients or any other task that need to be rate limited. Can be used in standalone mode using in-memory storage or\nbacked by a Redis server.\n\n# Features\n\n* Based on rolling windows with minimum delay between successive requests.\n* Atomic. When backed by redis, atomicity is guaranteed with the help of transactions.\n* Concurrency proof. When backed by redis, `Throtty` can handle multiple requests which can be performed in parallel.\n* Distributed. When backed by redis, multiple `Throtty` instances can be run from different hosts. \n\n# Installation\n\n```text\nnpm install throtty --save\n```\n\n# Usage\n\n````javascript\nconst throtty = require('throtty');\nconst redisClient = require('redis').createClient();\n\nconst rateLimiter = throtty({\n    interval: 10000, // Required. Rolling windows in milliseconds.\n    threshold: 3, // Required. How many times per rolling window?\n    delay: 1000, // Required. Minimum delay between two successive requests in milliseconds.\n    redis: redisClient, // Optional. Redis client. If not provided In-memory storage is used.\n    promisify: true, // Optional. When true, `checkRateAsync` will be the promisified version of `checkRate`.\n});\n\n// using callback\nrateLimiter.checkRate('user-1234-some-action', function(err, res) {\n    if (err) {\n        // handle error\n\n    }  else {\n        if (res.allowed) {\n            // accepted request\n\n        } else {\n            // reject request\n\n        }\n    }\n})\n\n// using promise when promisify parameter is set to true\nrateLimiter.checkRateAsync('user-1234-some-action')\n    .then((res) =\u003e {\n        if (res.allowed) {\n            // accepted request\n\n        } else {\n            // reject request\n\n        }\n    }).catch((err) =\u003e {\n        // error handling\n\n    });\n\n// using throtty as a rate limiter middleware (in KOA)\napp.use(async rateLimit(ctx, next) =\u003e {\n    const res = await rateLimiter.checkRateAsync('user-1234-some-action');\n    if (res.allowed) return next();\n    throw new TooManyRequestsError();\n})\n\n````\n\n## Advanced usage\n\n```javascript\nrateLimiter.checkRate('user-1234-some-action', function(err, res) {\n    \n    // error handling\n    // ...\n        \n    const allowed = res.allowed; // Boolean. True when allowed otherwise false.\n    const { \n        wait, // Number. Time to wait in milliseconds before performing a new request. \n        thresholdViolation, // Boolean. Whether or not the current request was rejected because of threshold violation.\n        delayViolation, // Boolean. Whether or not the current request was rejected because of delay violation.\n        rolls, // Number. How many requests has been performed so far.\n        remaining, // Number. How many remaining requests do we have?\n    } = res.details;\n    \n})\n````\n\n# The algorithm\n\nLet's suppose we want to limit API requests on some busy service or maybe to rate limit user requests for some specific\nend-points. For example we want to limit API requests to our service like 1000 per user (token) each hour.\n\nThe rolling/sliding window in our case is one hour.\n\n```text\n\n-------------|--|------|------|-|----|----------|-----|--------|--------  Time\n             ^\n             request                  \n\n                           |\u003c---------------------------------\u003e|\n                                 Rolling window == 1 hour\n\n```\n\n```javascript\nconst interval = 60 * 60 * 1000000; // One hour in microseconds (rolling window)\nconst threshold = 1000;\n```\n\nTo track the number of user's requests performed in the last hour from NOW, we need to remember the timestamp of each \n request.\n\n```javascript\nconst rolls = {}; // storage of user's timestamps\n```\n\nEach time when a user performs a request, we save the request timestamp first. Request timestamps are saved in an \nordered list (array).\n\n```javascript\nconst now = microtime.now(); // time in microseconds\nconst key = 'user-2345-some-action';\nrolls[key] = rolls[key] || [];\nrolls[key].push(now);\n```\n\nTo get the count of user requests in the last hour:\n\n```javascript\nconst from = now - interval;\nrolls[key] = rolls[key].filter(timestamp =\u003e timestamp \u003e from);\nconst count = rolls[key].length; \n```\n\nFrom this point we can check if the count of the user's requests in the last hour exceeds maximum allowed \nrequests per hour and based on that we can accept/reject the request. \n\n```javascript\nconst thresholdExceeded = count \u003e threshold;\n```\n\nThis should give you a basic idea about the algorithm being implemented by this package. \n\n# Considerations\n\nBecause of the limitations of the Javascript engine, setTimeout and setInterval timers are allowed to lag\narbitrarily and are not guaranteed to run at exact time. They tend to drift and delays due to CPU load are expected to \nhappen. \n\nConsidering that, sometimes even when the rate limiter asks to wait for certain amount of time, it is not\nguaranteed that this timing will be fulfilled. Therefore `details.wait` provided by this package in callbacks can be \nconsidered only as an estimation and can not be exact.\n\n# Contributing\n\nSo you are interested in contributing to this project? Please see [CONTRIBUTING.md](https://github.com/weyoss/guidelines/blob/master/CONTRIBUTIONS.md).\n\n# License\n\n[MIT](https://github.com/weyoss/throtty/blob/master/LICENSE)                \n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweyoss%2Fthrotty","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fweyoss%2Fthrotty","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fweyoss%2Fthrotty/lists"}