Skip to main content
KeyPort enforces a daily request limit on the public validation API. Limits are tracked per product API key and reset at midnight UTC.
You can test normal request behavior directly from the Validate endpoint playground. For rate-limit scenarios, use the playground for request shape and handle 429 responses in your own integration logic.

Plan limits

PlanDaily limit per product
Free10,000 requests
Pro100,000 requests
EnterpriseOrganization override or unlimited

API call counting rules

A request is counted once the product API key is successfully authenticated. This matches the live backend flow: the guard checks the daily limit first, then increments usage for requests with a valid product API key before product- or license-level business failures are returned.

Counted

  • Valid key + license found (valid: true)
  • Valid key + license not found on validate (200, valid: false, status: license_not_found)
  • Valid key + license revoked (200, valid: false, status: revoked)
  • Valid key + ip_blocked (200, valid: false, status: ip_blocked)
  • Valid key + product archived (200, valid: false, status: product_archived)
  • Valid key + license lookup request on GET /api/v1/license/:key, including a lookup that later returns 404

Not counted

  • Invalid or missing API key (401 invalid_api_key)
  • Empty or malformed bearer token that fails auth (401)
  • Requests already rejected because the rate limit is exhausted (429)
The older wording “valid key + license not found (404)” is not correct for the validate endpoint. In the live public validate API, license_not_found returns HTTP 200 with valid: false. A 404 can still happen on the separate read-only license lookup endpoint.

When the limit is hit

Once the daily limit is reached, further requests return HTTP 429 with the following body:
{
  "valid": false,
  "status": "rate_limit_exceeded",
  "message": "This license provider or organization owner has reached the daily API request limit for their current plan. Please ask them to upgrade to a paid plan for higher limits, or try again after midnight UTC.",
  "retry_after": 3600
}
The retry_after value is the number of seconds remaining until midnight UTC, when the counter resets. The response also includes a Retry-After HTTP header with the same value.

Handling 429 gracefully

Do not retry immediately on a 429. The limit is daily, so retrying within the same window will continue to fail.
Read retry_after from the response body (or the Retry-After header) to determine when to resume. In most cases, the right behavior is to surface a clear message to your user and avoid hammering the API.
async function validateWithRetry(licenseKey: string): Promise<void> {
  const response = await fetch('https://api.keyport.sbs/api/v1/validate', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer kp_live_xxxxxxxxxxxxxxxxxxxxxxxxx',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ license_key: licenseKey }),
  });

  if (response.status === 429) {
    const data = await response.json();
    const retryAfter = data.retry_after ?? 3600;
    console.warn(`Rate limit reached. Try again in ${retryAfter} seconds (resets at midnight UTC).`);
    return;
  }

  // Handle other responses normally
}
If you regularly approach your plan limit, consider upgrading to Pro or Enterprise, or caching validation results on your side to reduce redundant calls.