languagetool-org/languagetool · error · TooManyRequestsException

User request limit of per day exceeded. Try again after mid

Error message

User request limit of  per day exceeded. Try again after midnight UTC.

What it means

HTTP 429 error thrown when an authenticated premium user has exceeded their configured maximum number of requests per day. The count comes from the database and the limit is enforced unless the user's enforcement mode is DISABLED.

Source

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

  private String getUserAgent(Map<String, List<String>> httpHeader) {
    List<String> values = httpHeader.get("User-Agent");
    if (values == null || values.isEmpty()) {
      return null;
    }
    return values.get(0);
  }

  static void checkUserLimit(String referer, String userAgent, UserLimits user) {
    Long maxRequests = user.getRequestsPerDay();
    if (user.getPremiumUid() != null
      && maxRequests != null
      && user.getLimitEnforcementMode() != LimitEnforcementMode.DISABLED) {
      if (user.getLimitEnforcementMode() == LimitEnforcementMode.PER_DAY) {
        Long requestCount = DatabaseAccess.getInstance().getUserRequestCount(user.getPremiumUid());
        //System.out.printf("requests for %d: %d / %d%n", user.getPremiumUid(), requestCount, maxRequests);
        if (requestCount > maxRequests) {
          String message = "limit: " + maxRequests + ", requests: " + requestCount + ", enforcement: " + user.getLimitEnforcementMode().name();
          throw new TooManyRequestsException("User request limit of " + maxRequests + " per day exceeded. Try again after midnight UTC.");
        }
      }
    }
  }

  void checkLimit(String ipAddress, Map<String, String> parameters, Map<String, List<String>> httpHeader) {
    int requestsByIp = 0;
    int requestSizeByIp = 0;
    int requestsByFingerprint = 0;
    int requestSizeByFingerprint = 0;
    // all requests before this date are considered old:
    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);

View on GitHub (pinned to 2e990059ce)

Solutions

  1. Wait until after midnight UTC when the daily counter resets
  2. Upgrade the user's daily request quota in the server configuration/database
  3. Distribute traffic across multiple licensed users or add client-side throttling/backoff
Defensive patterns

Strategy: retry

Validate before calling

// track local daily usage per user and compare to known quota
if (usageCounter.today >= userQuotaPerDay) throw new Error('Daily quota would be exceeded; defer requests');

Try / catch

try {
  return await check(text);
} catch (e) {
  if (e.status === 429 && /per day exceeded/.test(e.message)) {
    await waitUntilMidnightUTC();
    return check(text);
  }
  throw e;
}

Prevention

When it happens

Trigger: User with LimitEnforcementMode.PER_DAY and premiumUid whose getUserRequestCount exceeds maxRequests during checkUserLimit.

Common situations: CI pipelines or cron jobs burning through daily quotas, shared API credentials used by many services, quota reduced by an admin while usage stayed high, or a retry loop after failures.

Related errors


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