Skip to content

Errors & Rate Limits

Errors use an HTTP status and a stable JSON object:

{
"error": {
"type": "invalid_request_error",
"code": "invalid_request",
"message": "Request validation failed."
}
}

Match application behavior on code, not the human-readable message.

  • 400 Bad Request: malformed JSON.
  • 401 Unauthorized: missing, invalid, or revoked API key.
  • 404 Not Found: unknown post ID.
  • 409 Conflict: an idempotency key was reused with a different body.
  • 422 Unprocessable Entity: request validation failed, including unknown fields such as limit.
  • 429 Too Many Requests: hourly, per-key concurrency, or total search quota exceeded.
  • 500 Internal Server Error: unexpected search failure.
  • 503 Service Unavailable: global search capacity is temporarily full or a latency-sensitive post-detail database read could not complete promptly.
  • 504 Gateway Timeout: search or post-detail work exceeded its execution deadline and was stopped.

Send a unique Idempotency-Key on the original POST /v1/search. Its scope is the authenticated API key and endpoint.

  • Reusing the same key with the identical body waits for or returns the original operation.
  • A completed replay keeps the original requestId and includes Idempotency-Replayed: true.
  • Reusing the key with a different body returns 409 with idempotency_key_conflict.
  • After a 504 or a dropped connection, retry with the same key and body. A still-running request is awaited, a completed request is replayed, and a timed-out request is rerun from the beginning.
  • Timed-out searches can be attempted up to three times with the same key. Once that cap is reached, the terminal 504 is replayed.
  • Without an idempotency key, a retry may start another search.
  • A terminal 500 is replayed by the same key. To intentionally make a new attempt after backoff, send a new idempotency key.
  • Idempotency records are retained for 24 hours. After that window, reusing the key starts a new logical search rather than replaying the earlier request.

Idempotency keys must contain 1–255 printable ASCII characters.

Search commonly takes 30–60 seconds and may take longer when thin-result diagnosis runs. Configure a read timeout of at least 270 seconds. Search work has a shorter execution deadline than the API and Cloud Run request deadlines, which gives the service time to stop work, record a terminal 504, and return a controlled response. Post-detail reads use a much shorter deadline.

Realtime search and bulk brand-search quotas are independent even though they use the same API key.

Some keys also have a fixed total allocation of realtime searches. This is an additional limit: hourly and concurrency limits still apply. Once the total allocation reaches zero, new searches return 429 with quota_exhausted. The total allocation does not reset on a timer. Post-detail requests and bulk brand-search jobs do not consume it.

One unit is consumed when a new logical POST /v1/search is accepted, even if the search later fails or times out. Validation failures, authentication failures, and requests rejected by another limit consume nothing. Replaying, waiting for, or rerunning an already admitted request with the same Idempotency-Key also consumes nothing, so an existing idempotent request can still be recovered after the allocation reaches zero while its 24-hour idempotency record is retained. A retry without an idempotency key is a new logical search and consumes another unit if accepted.

The current default realtime limits are:

  • POST /v1/search: 100 accepted searches per rolling hour per key, with up to 2 running concurrently per key.
  • GET /v1/posts/:id: 1,000 accepted requests per rolling hour per key, with up to 10 running concurrently per key.

Service-wide concurrency protection can temporarily return 503 even when a key has remaining hourly capacity. Limits may be adjusted operationally; the response headers are authoritative.

Accepted, replayed, and rate-limited responses include:

  • X-RateLimit-Limit: hourly request allowance.
  • X-RateLimit-Remaining: remaining accepted operations in the rolling window.
  • X-RateLimit-Reset: Unix timestamp when the oldest request in the current window expires.
  • X-Search-Quota-Limit: fixed total realtime-search allocation. Included only for keys with a total allocation.
  • X-Search-Quota-Remaining: searches remaining in that allocation. Included only for keys with a total allocation.
  • Retry-After: seconds to wait, included for temporary throttling. It is not included with quota_exhausted because the total allocation has no timed reset.

Example 429 response:

{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "This API key has exceeded its realtime Search API limit."
}
}

Example exhausted total quota:

{
"error": {
"type": "rate_limit_error",
"code": "quota_exhausted",
"message": "This API key has exhausted its total realtime Search API quota."
}
}