{"record":{"id":"1af2daa703ef8884","repo":"RocketChat/Rocket.Chat","slug":"error-too-many-requests","errorCode":"error-too-many-requests","errorMessage":"Error, too many requests. Please slow down. You must wait ${timeToResetAttempsInSeconds} seconds before trying this endpoint again.","messagePattern":"Error, too many requests\\. Please slow down\\. You must wait (.+?) seconds before trying this endpoint again\\.","errorType":"http","errorClass":"Meteor.Error","httpStatus":429,"severity":"warning","filePath":"apps/meteor/server/api/ApiClass.ts","lineNumber":443,"sourceCode":"\t\tresponse: Response,\n\t\tuserId?: string,\n\t): Promise<void> {\n\t\tif (!(await this.shouldVerifyRateLimit(objectForRateLimitMatch.route, userId))) {\n\t\t\treturn;\n\t\t}\n\n\t\trateLimiterDictionary[objectForRateLimitMatch.route].rateLimiter.increment(objectForRateLimitMatch);\n\t\tconst attemptResult = await rateLimiterDictionary[objectForRateLimitMatch.route].rateLimiter.check(objectForRateLimitMatch);\n\t\tconst timeToResetAttempsInSeconds = Math.ceil(attemptResult.timeToReset / 1000);\n\t\tresponse.headers.set(\n\t\t\t'X-RateLimit-Limit',\n\t\t\tString(rateLimiterDictionary[objectForRateLimitMatch.route].options.numRequestsAllowed ?? ''),\n\t\t);\n\t\tresponse.headers.set('X-RateLimit-Remaining', String(attemptResult.numInvocationsLeft));\n\t\tresponse.headers.set('X-RateLimit-Reset', String(new Date().getTime() + attemptResult.timeToReset));\n\n\t\tif (!attemptResult.allowed) {\n\t\t\tthrow new Meteor.Error(\n\t\t\t\t'error-too-many-requests',\n\t\t\t\t`Error, too many requests. Please slow down. You must wait ${timeToResetAttempsInSeconds} seconds before trying this endpoint again.`,\n\t\t\t\t{\n\t\t\t\t\ttimeToReset: attemptResult.timeToReset,\n\t\t\t\t\tseconds: timeToResetAttempsInSeconds,\n\t\t\t\t},\n\t\t\t);\n\t\t}\n\t}\n\n\tpublic registerRateLimiterForRoute({\n\t\troute,\n\t\trateLimiterOptions = defaultRateLimiterOptions,\n\t\tmethods,\n\t}: {\n\t\troute: string;\n\t\trateLimiterOptions?: RateLimiterOptions;\n\t\tmethods: string[];","sourceCodeStart":425,"sourceCodeEnd":461,"githubUrl":"https://github.com/RocketChat/Rocket.Chat/blob/e4b8178b205510181a96ceefee043d0abcd13e5a/apps/meteor/server/api/ApiClass.ts#L425-L461","documentation":"Per-route REST rate limiting in ApiClass: for routes registered with a rate limiter, every call increments the counter and re-checks the allowance; when the check reports not allowed within the window, the request fails with error-too-many-requests carrying timeToReset/seconds in details, and X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers are set on the response.","triggerScenarios":"Exceeding numRequestsAllowed requests to the same rate-limited REST route within the configured interval from the same matched caller (per the route's objectForRateLimitMatch, typically IP/userId combination).","commonSituations":"Polling loops, import/export scripts, monitoring hitting endpoints too fast, many clients behind one NAT IP, deployment dashboards hammering list endpoints.","solutions":["Back off and retry after the X-RateLimit-Reset moment (or the seconds value in the error details).","Reduce request frequency, batch with pagination (count/offset), or cache responses client-side.","Server-side, tune the limits when the route is registered via registerRateLimiterForRoute (numRequestsAllowed / intervalTime) if the workload legitimately needs more."],"exampleFix":"// before\nsetInterval(() => api.get('/v1/users.list'), 100); // bursts past the limit\n\n// after — honor the reset window\nconst res = await api.get('/v1/users.list');\nconst resetAt = Number(res.headers.get('x-ratelimit-reset'));\nconst waitMs = Math.max(0, resetAt - Date.now()) + 100;\nawait new Promise((r) => setTimeout(r, waitMs));","handlingStrategy":"retry","validationCode":"// adaptive throttle from response headers, before the next call\nconst remaining = Number(res.headers.get('x-ratelimit-remaining') ?? Infinity);\nif (remaining <= 1) {\n\tconst resetAt = Number(res.headers.get('x-ratelimit-reset'));\n\tawait new Promise((r) => setTimeout(r, Math.max(0, resetAt - Date.now()) + 50));\n}","typeGuard":null,"tryCatchPattern":"async function callWithBackoff<T>(fn: () => Promise<T>, maxRetries = 5): Promise<T> {\n\tfor (let attempt = 0; ; attempt++) {\n\t\ttry {\n\t\t\treturn await fn();\n\t\t} catch (e: any) {\n\t\t\tif (e?.error !== 'error-too-many-requests' || attempt >= maxRetries) throw e;\n\t\t\tconst wait = (e.details?.timeToReset as number) ?? 10_000;\n\t\t\tawait new Promise((r) => setTimeout(r, wait + Math.random() * 500)); // honor reset + jitter\n\t\t}\n\t}\n}","preventionTips":["Read and honor X-RateLimit-Remaining/Reset headers on every response","Batch list endpoints with count/offset instead of tight request loops","Add jitter to scheduled jobs so many clients do not sync on the same interval"],"tags":["rest-api","rate-limiting","throttling"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"e4b8178b205510181a96ceefee043d0abcd13e5a","analyzedAt":"2026-08-18T15:26:39.429Z","contentChangedAt":"2026-08-18T15:26:39.429Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}