Billing limits
Plan quota exhausted, wallet overage budget, plan-tier feature gates, and the difference between soft and hard caps.
When a request returns quota_exceeded or a feature is silently unavailable, the cause is one of three things: monthly search-unit quota, wallet overage budget, or plan-tier feature gate. Each has a different remedy.
Symptom
| What you see | Likely cause |
|---|---|
429 quota_exceeded | Monthly search-units used up, wallet overage either disabled or empty |
Feature returns 403 feature_not_available_on_plan | Plan tier does not include this feature |
| Connector cap reached: "Cannot create more than N indexes on Free" | Plan tier ceiling on indexes |
| Sudden drop to zero traffic mid-month | Hard cap with no overage budget |
| Continuous service but unexpected wallet drain | Soft cap exceeded with overage enabled |
See Plans and limits for the per-plan numbers.
Check 1: where you stand on the monthly quota
Open Settings → Billing → Usage. Three things are surfaced:
| Indicator | Meaning |
|---|---|
| Search units used / cap | Current month's count vs plan limit |
| Wallet balance | If overage is enabled, how much is left |
| Reset date | Always 1st of the next calendar month, UTC |
You can also pull this programmatically with an admin key:
curl -X GET https://app.aacsearch.com/api/v1/projects/<projectId>/usage \
-H "Authorization: Bearer aa_admin_..."The response includes searchUnitsUsed, searchUnitsCap, walletBalanceMinor (in kopecks), periodStartUtc, periodEndUtc.
Check 2: overage budget
If you are on a paid plan, you can enable wallet-funded overage. Once enabled, requests above the soft cap are billed against the wallet (priced per 1k search-units) instead of returning 429.
| State | Behavior |
|---|---|
| Overage disabled | Soft cap = hard cap. 429 quota_exceeded once reached. |
| Overage enabled, wallet has balance | Requests above soft cap deduct from wallet. Service continues. |
| Overage enabled, wallet empty | Hard cap returns 429 quota_exceeded. |
Enable overage in Settings → Billing → Overage budget. Set a maximum monthly spend ceiling so you do not get surprised.
Check 3: feature gate
Some features are not part of every plan tier:
| Feature | Minimum plan |
|---|---|
| Custom synonyms | Starter |
| Per-result curations | Pro |
| AI recommendations | Pro |
| SCIM 2.0 user sync | Business |
| Webhook deliveries log retention > 7 days | Business |
| SLA / dedicated support | Enterprise |
A request to a gated feature returns:
{
"error": "feature_not_available_on_plan",
"message": "Curations require the Pro plan or higher.",
"requiredPlan": "pro",
"upgradeUrl": "https://app.aacsearch.com/settings/billing"
}Upgrade in Settings → Billing → Change plan.
Check 4: plan-tier ceilings
Each plan caps the number of indexes:
| Plan | Max indexes |
|---|---|
| Free | 1 |
| Starter | 3 |
| Pro | 10 |
| Business | 50 |
| Enterprise | Unlimited |
Trying to create one more returns:
{
"error": "plan_limit_reached",
"message": "Free plan allows 1 index. Upgrade to Starter for up to 3.",
"requiredPlan": "starter"
}Soft vs hard cap (detail)
Soft cap fires:
├─ Email + dashboard alert to billing contact
├─ Requests still served if overage budget enabled
└─ Wallet starts deducting if overage on
Hard cap fires:
├─ All search and ingest endpoints return 429 quota_exceeded
├─ Connectors stop accepting sync calls
└─ Resets at 00:00 UTC on 1st of next monthThe two coincide when overage is disabled. With overage enabled, hard cap fires only when the wallet itself runs dry.
Per-key vs per-org
These are distinct:
| Limit | Scope | Counter |
|---|---|---|
rateLimitPerMinute | Per API key | Per-minute sliding window |
searchUnitsCap | Per organization | Per calendar month |
Hitting rateLimitPerMinute returns 429 rate_limit_exceeded (different error code) and is unrelated to billing. See Rate limits.
Fix matrix
| Diagnosis | Fix |
|---|---|
| Free plan quota hit | Either upgrade to Starter+, or wait for monthly reset |
| Paid plan quota hit, overage off | Enable overage in Settings → Billing |
| Paid plan quota hit, wallet empty | Top up the wallet, or upgrade plan tier |
| Feature gated by plan | Upgrade to the required tier |
| Index ceiling reached | Upgrade tier or delete an unused index |
| Unexpected wallet drain | Audit recent usage; identify the runaway caller and rate-limit it |
Predicting next month
To estimate whether you will fit in the current plan:
const usage = await admin.getUsage(7); // last 7 days
const dailyAvg = usage.rows.reduce((s, r) => s + r.searchUnits, 0) / 7;
const projected30d = dailyAvg * 30;
console.log("projected this month:", projected30d);
console.log("plan cap:", planCap);If projected30d is approaching the plan cap, upgrade preemptively rather than waiting for the alert.
Diagnostics packet
| Field | Notes |
|---|---|
| Organization ID | required |
| Plan tier | from Settings → Billing |
| Period start (UTC) | the calendar month in question |
searchUnitsUsed, searchUnitsCap | from usage API |
| Overage state and wallet balance | from Settings → Billing |
| When the cap was hit | UTC timestamp |
| What you were doing at the time | one sentence |
Quota is enforced per calendar month UTC, not per 30 days. A reset always lands at 00:00 UTC on the 1st.
Related
- Plans and limits — per-plan quota numbers
- Rate limits — per-key (not per-org) rate enforcement
- Errors and rate limits — error code matrix
- Auth errors — for 401/403 (not 429)