{"record":{"id":"6cc5098e2efeec57","repo":"languagetool-org/languagetool","slug":"ip-request-size-limit-of-bytes-per-seconds-excee","errorCode":null,"errorMessage":"IP request size limit of  bytes per  seconds exceeded in text-level checks","messagePattern":"IP request size limit of  bytes per  seconds exceeded in text-level checks","errorType":"http","errorClass":"TooManyRequestsException","httpStatus":429,"severity":"warning","filePath":"languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java","lineNumber":234,"sourceCode":"        }\n        if (ipFingerprintFactor > 0 && requestLimit > 0 && requestsByFingerprint > requestLimit) {\n          String msg = \"limit: \" + requestLimit + \" / \" + requestLimitPeriodInSeconds + \", requests: \"  + requestsByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n          throw new TooManyRequestsException(\"Client request limit of \" + requestLimit + \" requests per \" +\n            requestLimitPeriodInSeconds + \" seconds exceeded\"); }\n        if (requestLimit > 0 && requestsByIp > ipRequestLimit) {\n          String msg = \"limit: \" + ipRequestLimit + \" / \" + requestLimitPeriodInSeconds + \", requests: \"  + requestsByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n          throw new TooManyRequestsException(\"IP request limit of \" + ipRequestLimit + \" requests per \" +\n            requestLimitPeriodInSeconds + \" seconds exceeded\");\n        }\n        if (event.mode == JLanguageTool.Mode.TEXTLEVEL_ONLY) {\n          if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {\n            String msg = \"limit in Mode.TEXTLEVEL_ONLY: \" + requestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"Client request size limit of \" + requestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded in text-level checks\");\n          }\n          if (requestLimitInBytes > 0 && requestSizeByIp > ipRequestLimitInBytes) {\n            String msg = \"limit in Mode.TEXTLEVEL_ONLY: \" + ipRequestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"IP request size limit of \" + ipRequestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded in text-level checks\");\n          }\n        } else {\n          if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {\n            String msg = \"limit: \" + requestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"Client request size limit of \" + requestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded\");\n          }\n          if (requestLimitInBytes > 0 && requestSizeByIp > ipRequestLimitInBytes) {\n            String msg = \"limit: \" + ipRequestLimitInBytes + \" / \" + requestLimitPeriodInSeconds + \", request size: \"  + requestSizeByIp + \", ip: \" + ipAddress + \", fingerprint: \" + fingerprint;\n            throw new TooManyRequestsException(\"IP request size limit of \" + ipRequestLimitInBytes + \" bytes per \" +\n              requestLimitPeriodInSeconds + \" seconds exceeded\");\n          }\n        }\n      }\n    }\n  }\n  ","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/languagetool-org/languagetool/blob/2e990059ce67d5e2a0f7f7ca5d31160c6709df4b/languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java#L216-L252","documentation":"RequestLimiter enforces a per-IP request-size quota in Mode.TEXTLEVEL_ONLY. When the total bytes of requests from one IP address exceed ipRequestLimitInBytes within requestLimitPeriodInSeconds, checkLimit throws TooManyRequestsException (HTTP 429). This limits aggregate bandwidth per client IP regardless of fingerprint.","triggerScenarios":"POSTing to /v2/check with mode=textLevelOnly where cumulative request bytes from the client IP exceed ipRequestLimitInBytes within requestLimitPeriodInSeconds (and requestLimitInBytes > 0).","commonSituations":"Automated scripts or CI jobs behind one IP sending many large requests; multiple users behind a NAT/proxy sharing the quota; server configured with a tight IP size limit (ipRequestLimitInBytes); rapid retry loops amplifying usage.","solutions":["Wait for requestLimitPeriodInSeconds to pass, then resume with reduced volume","Add client-side throttling/backoff: track bytes sent and cap below ipRequestLimitInBytes per window","Use an authenticated Premium account with higher per-IP limits","If you administer the server, raise the IP size limit (ipRequestLimitInBytes) in HTTPServerConfig/launch options","Move the workload to a different IP or distribute requests across allowed clients"],"exampleFix":"// before: unbounded loop\nfor (doc : docs) client.check(doc, Mode.TEXTLEVEL_ONLY);\n// after: rate-aware loop\nlong sent = 0; long windowStart = now();\nfor (doc : docs) {\n  if (now()-windowStart > period) { sent = 0; windowStart = now(); }\n  if (sent + doc.length() > ipLimitBytes) sleep(windowStart + period - now());\n  client.check(doc, Mode.TEXTLEVEL_ONLY); sent += doc.length();\n}","handlingStrategy":"retry","validationCode":"function canSend(bytes) { return bytesSentFromThisIpInWindow + bytes <= IP_LIMIT_BYTES; }","typeGuard":null,"tryCatchPattern":"try {\n  const res = await api.check(text);\n} catch (e) {\n  if (e.response && e.response.status === 429) { await sleep(periodMs); return api.check(text); }\n  throw e;\n}","preventionTips":["Throttle request volume per IP; serialize jobs instead of parallel bursts","Distribute workload across allowed IPs or use authenticated access","Back off on 429 instead of retrying immediately","Monitor usage and alert before hitting the per-window byte cap"],"tags":["rate-limit","http","http-429","languagetool"],"backgroundTag":"rate-limit-exceeded","analyzedSha":"2e990059ce67d5e2a0f7f7ca5d31160c6709df4b","analyzedAt":"2026-09-06T09:20:17.015Z","contentChangedAt":"2026-09-06T09:20:17.015Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}