Rate Limits
NeuralRepo enforces a per-user daily request limit on programmatic access. The limit applies
only to requests authenticated with an API key or a bearer token. Requests carrying only the
nrepo_session cookie — that is, the web app — are exempt, because a single page load makes many
calls and the quota exists for scripted access.
Daily Limits
Section titled “Daily Limits”| Plan | Limit |
|---|---|
| Free | 100 requests per day |
| Pro | 10,000 requests per day |
The counter is keyed on your user id and the UTC date, so it resets at 00:00 UTC. It is a single quota per account: every API key, MCP token, and bearer session you hold draws from the same bucket.
What counts
Section titled “What counts”| Request | Counted? |
|---|---|
Any /api/v1/* call with X-API-Key | Yes |
Any /api/v1/* call with Authorization: Bearer | Yes — session tokens included |
Any /api/v1/* call with only the session cookie | No |
GET /api/v1/health | No — it runs before the middleware chain |
A request that then fails with 400/404 | Yes — the counter increments before your handler runs |
A request rejected with 429 | No — the counter is already at the limit and is not raised further |
Reads count exactly as much as writes. There is no separate write quota.
Retry Behavior
Section titled “Retry Behavior”When you exceed your daily limit, the API responds with 429 Too Many Requests:
{ "error": "Rate limit exceeded"}Because the window is a whole day, retrying within the same session will not succeed. Back off
so that a transient failure does not turn into a retry storm, but treat a sustained 429 as a
signal to stop for the day rather than to keep trying.
Example: Retry with Backoff
Section titled “Example: Retry with Backoff”async function fetchWithRetry(url, options, maxRetries = 3) { for (let attempt = 0; attempt < maxRetries; attempt++) { const res = await fetch(url, options);
if (res.status !== 429) return res;
// No Retry-After is sent — back off on a fixed schedule. const delay = Math.pow(2, attempt) * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); }
throw new Error("Rate limit exceeded after retries");}Limits that are not rate limits
Section titled “Limits that are not rate limits”Three other caps are easy to mistake for rate limiting, because two of them return 200 with
less data rather than an error:
| Cap | Plan | What you see |
|---|---|---|
| 50 unarchived ideas | Free | POST /ideas returns 403 idea_limit_reached |
| 10 semantic searches per month | Free | GET /ideas/search returns keyword results with semantic_limit_reached: true |
| Duplicate detections hidden | Free | GET /ideas/duplicates returns { "duplicates": [] } |
The semantic-search counter is monthly (not daily) and is consumed before the search runs, so a search that fails or matches nothing still spends one of the ten.
Best Practices
Section titled “Best Practices”- Batch operations.
PATCH /ideas/bulkupdates up to 50 ideas for one request, andPOST /map/relationsaccepts up to 50 links in one call. - Cache responses when possible to reduce request volume.
- Spread requests evenly across the day rather than sending bursts.
- Watch your own count. Nothing in the response tells you how much quota is left, so track request counts client-side if you are near the Free limit.
Status Codes
Section titled “Status Codes”| Status | Meaning |
|---|---|
429 Too Many Requests | Daily quota exhausted; resets at 00:00 UTC |