Rate Limits & Quotas
InvesTeam rate-limits programmatic (hfk_) keys on the routes that spend
work — the submits. Reads are not the throttled surface, but you should still
poll at a sane cadence. This page covers what is limited, how to handle a 429,
and how fast to poll.
What are InvesTeam's rate limits?¶
Two limits apply per API key, both on the spend-triggering submit routes —
POST /api/v1/briefs, POST /api/v1/briefs/{id}/answers, and
POST /api/v1/orchestrations:
- A submit rate limit (a token bucket) on every mutating route.
- A daily kickoff quota, drawn down by all three write routes — so an answer submit also consumes from the same daily allowance as a brief create or a convene.
Exceeding either returns 429 rate_limited (retryable: true) with a
Retry-After header in integer seconds. Firebase (browser) users are not subject
to these per-key limits.
The exact numeric defaults (rate, burst, daily cap)
are configuration, not a published contract, and the limiter is per-instance —
so treat the Retry-After header as the source of truth for back-off
timing, not a hard-coded number.
How do I handle a 429?¶
Wait the advertised Retry-After interval, then retry — do not retry
immediately, and do not tighten your loop. A 429 is retryable: true, so a
well-behaved client backs off and continues.
import time
import requests
def submit_with_backoff(url, headers, json):
while True:
resp = requests.post(url, headers=headers, json=json)
if resp.status_code != 429:
return resp
wait = int(resp.headers.get("Retry-After", "5"))
time.sleep(wait)
Read the error envelope to distinguish a rate_limited 429
from other failures — only retryable: true errors should back off and retry.
How fast can I poll?¶
Poll the read routes every two to three seconds — that is the cadence the product uses and it is well within limits. Reads are not the throttled surface, but relentless sub-second polling wastes your budget and the shared poll paths for no benefit, since results arrive on the order of seconds, not milliseconds.
Two poll disciplines keep it cheap:
- Advance the transcript cursor forward only. Pass the returned
latest_seqas the nextafter; never re-read consumed turns. See the Transcript reference. - Drain then stop. Once an execution is terminal, do one final transcript read and stop polling — a terminal stage produces nothing new.
Where next?¶
For the full error catalog and the meaning of retryable, see the
Error Reference. For the submit-then-poll pattern that shapes
your call volume, see the async model.