Rate Limits
Lathe Studio rate-limits the API to keep the platform stable and available for everyone.
Limits are not a packaging device. There is one plan — $25 per user per month, everything included — and the full API is part of it. Limits exist for two reasons:
- To absorb attacks and runaway clients so a burst of traffic cannot degrade the service for other customers.
- To keep the API a companion to your Lathe Studio usage, rather than a free backend for unrelated software.
Well-behaved integrations — CI pipelines posting results, dashboards polling on a sane interval, webhook consumers — are not the target and should not run into these limits in normal use.
How limiting works#
Each API key carries its own limit, so you can issue a tightly-limited key to a noisy pipeline without affecting your other integrations. Limits are applied per key and per organization, and we tune them as we learn what real usage looks like.
We deliberately do not publish specific thresholds. They are set to catch abuse
rather than to ration normal use, and they will change as the platform matures.
Write your client to react to a 429 (see below) rather than to a number
documented here.
Responses may carry X-RateLimit-* and X-Monthly-Quota-* headers indicating
remaining allowance. Treat them as advisory: use them to back off early when
present, but do not rely on them as an exact budget.
429 Too Many Requests#
When you exceed either limit, the API returns 429 in the standard error
shape:
{
"error": {
"code": "RATE_LIMITED",
"message": "Rate limit exceeded",
"details": {
"reset_at": 1775001600
}
}
}
details.reset_at is a Unix timestamp (seconds) for when you may retry.
If you are hitting limits during legitimate use, email support — we would rather adjust your limits than have you work around them.
Retry Guidance#
Use exponential backoff with jitter when retrying after a 429:
wait = min(base * 2^attempt + random_jitter, max_wait)
Recommended values:
| Parameter | Value |
|---|---|
| Base wait | 1 second |
| Max wait | 60 seconds |
| Jitter | 0–500ms random |
| Max attempts | 5 |
Example in Python:
import time
import random
import httpx
def create_build_with_retry(payload, api_key, max_attempts=5):
for attempt in range(max_attempts):
response = httpx.post(
"https://app.lathe.studio/api/v1/builds",
headers={"Authorization": f"Bearer {api_key}"},
json=payload,
)
if response.status_code == 429:
wait = min(2 ** attempt + random.uniform(0, 0.5), 60)
time.sleep(wait)
continue
response.raise_for_status()
return response.json()
raise RuntimeError("Max retry attempts exceeded")
Reducing API Usage#
Good practice regardless of where the limits sit:
- Cache read responses —
GET /buildsandGET /projectsresponses are stable. Cache them for 60 seconds. - Batch operations — The
POST /testruns/{id}/resultsendpoint accepts an array of results in a single call. - Use webhooks instead of polling — Subscribe to
testrun.completed.v1instead of pollingGET /testruns/{id}in a loop. - Issue per-integration keys — Separate keys make it obvious which client is doing the work.