languagetool-org/languagetool · error · TooManyRequestsException
IP request limit of requests per seconds exceeded
Error message
IP request limit of requests per seconds exceeded
What it means
HTTP 429 error thrown when the total number of requests from one IP address exceeds ipRequestLimit within requestLimitPeriodInSeconds. This is the per-IP guard, regardless of fingerprint, protecting the server from high-volume sources.
Source
Thrown at languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java:223
if (whitelistLimit <= 0 || requestsByIp < whitelistLimit) {
continue;
} else {
String msg = "limit: " + ipRequestLimit + " / " + requestLimitPeriodInSeconds + ", requests: " + requestsByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
throw new TooManyRequestsException("Whitelist request limit of " + whitelistLimit + " requests per " +
requestLimitPeriodInSeconds + " seconds exceeded");
}
}
if (event.fingerprint.equals(fingerprint)) {
requestsByFingerprint++;
requestSizeByFingerprint += event.getSizeInBytes() * modeFactor;
}
if (ipFingerprintFactor > 0 && requestLimit > 0 && requestsByFingerprint > requestLimit) {
String msg = "limit: " + requestLimit + " / " + requestLimitPeriodInSeconds + ", requests: " + requestsByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
throw new TooManyRequestsException("Client request limit of " + requestLimit + " requests per " +
requestLimitPeriodInSeconds + " seconds exceeded"); }
if (requestLimit > 0 && requestsByIp > ipRequestLimit) {
String msg = "limit: " + ipRequestLimit + " / " + requestLimitPeriodInSeconds + ", requests: " + requestsByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
throw new TooManyRequestsException("IP request limit of " + ipRequestLimit + " requests per " +
requestLimitPeriodInSeconds + " seconds exceeded");
}
if (event.mode == JLanguageTool.Mode.TEXTLEVEL_ONLY) {
if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {
String msg = "limit in Mode.TEXTLEVEL_ONLY: " + requestLimitInBytes + " / " + requestLimitPeriodInSeconds + ", request size: " + requestSizeByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
throw new TooManyRequestsException("Client request size limit of " + requestLimitInBytes + " bytes per " +
requestLimitPeriodInSeconds + " seconds exceeded in text-level checks");
}
if (requestLimitInBytes > 0 && requestSizeByIp > ipRequestLimitInBytes) {
String msg = "limit in Mode.TEXTLEVEL_ONLY: " + ipRequestLimitInBytes + " / " + requestLimitPeriodInSeconds + ", request size: " + requestSizeByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
throw new TooManyRequestsException("IP request size limit of " + ipRequestLimitInBytes + " bytes per " +
requestLimitPeriodInSeconds + " seconds exceeded in text-level checks");
}
} else {
if (ipFingerprintFactor > 0 && requestLimitInBytes > 0 && requestSizeByFingerprint > requestLimitInBytes) {
String msg = "limit: " + requestLimitInBytes + " / " + requestLimitPeriodInSeconds + ", request size: " + requestSizeByIp + ", ip: " + ipAddress + ", fingerprint: " + fingerprint;
throw new TooManyRequestsException("Client request size limit of " + requestLimitInBytes + " bytes per " +
requestLimitPeriodInSeconds + " seconds exceeded");View on GitHub (pinned to 2e990059ce)
Solutions
- Reduce request rate or batch text checks into fewer, larger requests
- Raise ipRequestLimit in the server configuration if the source is trusted
- Route legitimate high-volume clients through distinct IPs/credentials or wait out the rate-limit window
Defensive patterns
Strategy: retry
Validate before calling
const MIN_INTERVAL_MS = (periodSeconds * 1000) / ipRequestLimit;
let last = 0;
async function throttle() {
const wait = last + MIN_INTERVAL_MS - Date.now();
if (wait > 0) await new Promise(r => setTimeout(r, wait));
last = Date.now();
} Try / catch
try {
return await check(text);
} catch (e) {
if (e.status === 429 && /IP request limit/.test(e.message)) {
await sleep(periodSeconds * 1000);
return check(text);
}
throw e;
} Prevention
- Coordinate all services behind a shared egress IP with a global rate limiter
- Batch multiple texts per request where possible
- Run load tests against staging, not production servers
When it happens
Trigger: checkLimit detects requestsByIp > ipRequestLimit while requestLimit > 0 within the period.
Common situations: Many users behind one NAT/proxy IP collectively exceeding the limit, load tests run against production, or microservices sharing an egress IP hitting the API concurrently.
Related errors
- User request limit of per day exceeded. Try again after mid
- Whitelist request limit of requests per seconds exceeded
- Client request limit of requests per seconds exceeded
- Client request size limit of bytes per seconds exceeded in
- IP request size limit of bytes per seconds exceeded in tex
AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06).
Data as JSON: /api/errors/eea94b7126eaf6da.
Report an issue: GitHub.