Rate limits

WatchLLM returns Promptwatch-style rate-limit headers on authenticated REST requests, MCP metadata responses, and successful MCP tool calls.

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset

Local development uses persisted Postgres quota storage, not an in-process counter. Organization quota rows live in api_usage, and the per-IP guard stores SHA-256 IP hashes in api_ip_usage.

Quota model

Authenticated REST and MCP calls share the same organization hourly quota. Project-scoped keys resolve the organization through their project. Organization-scoped keys use X-Project-Id for REST project routes or projectId in MCP tool arguments when a tool needs project context, but the quota still belongs to the organization.

  • Explore: 100 requests per hour
  • Solo: 500 requests per hour
  • Startup: 2,000 requests per hour
  • Business: 10,000 requests per hour
  • Enterprise: 50,000 requests per hour

The reset value is the next UTC hour as a Unix epoch in seconds.

IP guard

The local API layer also applies a durable per-IP guard of 1,000 requests per minute. IP addresses are never stored raw; WatchLLM stores only the SHA-256 hash plus the minute window.

Error shape

Project-context errors return JSON with error and message fields.

{
  "error": "MISSING_PROJECT_ID",
  "message": "X-Project-Id header required for this endpoint."
}

MCP initialize, tools/list, and successful tools/call responses consume the shared organization quota and include the same three X-RateLimit-* headers. Invalid bearer requests and organization-scoped tool calls missing project context return before consuming organization quota.

When the organization hourly quota is exceeded, REST returns 429 with rate-limit headers and Retry-After.

{
  "error": "Too Many Requests",
  "message": "You've hit your hourly API request limit of 100 requests. Please retry after the quota resets.",
  "retryAfter": 3600,
  "limit": 100,
  "remaining": 0
}

When the per-IP guard is exceeded, REST returns 429 with the Promptwatch-style guard body.

{
  "code": "RATE_LIMITED",
  "message": "Too many requests, please slow down",
  "statusCode": 429
}

Local verification

The committed smoke:rest-core script verifies consecutive authenticated /api/v2/validate calls return X-RateLimit-Remaining values that decrement. smoke:mcp verifies authenticated MCP initialize, tools/list, and tools/call responses carry the same quota headers while unauthorized and missing-project calls do not consume organization quota. The rate-limit task queue also covers the persisted per-IP and per-organization 429 paths.