languagetool-org/languagetool · error · TooManyRequestsException

Whitelist request limit of requests per seconds exceeded

Error message

Whitelist request limit of  requests per  seconds exceeded

What it means

HTTP 429 error thrown when an IP on the whitelist exceeds its special per-period request rate. Whitelisted IPs get their own (usually higher) limit; when requests from that IP within requestLimitPeriodInSeconds exceed whitelistLimit, the request is rejected.

Source

Thrown at languagetool-server/src/main/java/org/languagetool/server/RequestLimiter.java:209

    Date thresholdDate = new Date(System.currentTimeMillis() - requestLimitPeriodInSeconds * 1000L);
    String fingerprint = computeFingerprint(httpHeader, parameters);
    String referer = getReferer(httpHeader);
    String userAgent = getUserAgent(httpHeader);
    Long clientId = getClientId(parameters);
    String user = parameters.get("username");
    boolean whitelistedUser = user != null && whitelistUsers.contains(user);
    for (RequestEvent event : requestEvents) {
      if (event.ip.equals(ipAddress) && event.date.after(thresholdDate)) {
        // text level rules cause much less load, so count them accordingly
        float modeFactor = event.mode == JLanguageTool.Mode.TEXTLEVEL_ONLY ? 0.1f : 1f;
        requestsByIp++;
        requestSizeByIp += event.getSizeInBytes() * modeFactor;
        if (whitelistedUser) {
          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) {

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Increase the whitelistLimit in the server's rate-limit configuration
  2. Spread load: have clients behind the whitelisted IP use separate API keys or back off
  3. Reduce polling frequency of automated clients and add client-side rate limiting
Defensive patterns

Strategy: retry

Validate before calling

const MIN_INTERVAL_MS = (periodSeconds * 1000) / whitelistLimit;
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 && /Whitelist request limit/.test(e.message)) {
    await sleep(periodSeconds * 1000);
    return check(text);
  }
  throw e;
}

Prevention

When it happens

Trigger: checkLimit called via checkAccess while whitelistedUser is true and requestsByIp >= whitelistLimit within the sliding period.

Common situations: Whitelisted office/VPN IP shared by many users exhausting its quota, monitoring/health checks hitting the API too frequently from an allowlisted host, or whitelistLimit misconfigured to a very low value.

Related errors


AI-assisted analysis of languagetool-org/languagetool@2e990059ce (2026-09-06). Data as JSON: /api/errors/935508ab09da4029. Report an issue: GitHub.