Rate limits and quotas
The hourly fair-use limit, the monthly credit quota, and every payload, pagination and timeout cap that applies to an API call.
Last updated 2026-08-06
Summary#
Two independent ceilings govern API usage. An hourly fair-use limit of 100 calls caps light, low-cost work and protects the platform from scraping. A monthly credit quota set by your plan caps expensive work. On top of those sit a set of payload, pagination and timeout limits per endpoint. This page lists all of them with exact numbers, so you can size a job before you run it.
Overview#
Which ceiling applies to a given call#
Every metered call carries a credit cost. That cost decides which ceiling applies:
| Cost of the call | Monthly quota | Hourly limit |
|---|---|---|
| 0 credits | Not counted | Counted |
| 1 or 2 credits ("light") | Not blocked by quota | Counted, 100 per hour |
| 3 credits or more ("premium") | Blocked when the allowance is spent | Not counted |
The threshold is exactly 3. A call costing fewer than 3 credits is treated as light and is never blocked by the monthly quota; a call costing 3 or more is checked against it. POST /api/v1/analyze costs 6, so it is governed by your monthly quota and is not subject to the hourly limit.
Important: Free-plan accounts are a separate case. Any call costing more than 0 credits is refused with upgrade_required, regardless of either ceiling. Free includes the 10 technical SEO tools and nothing else.
The hourly fair-use limit#
| Property | Value |
|---|---|
| Limit | 100 light-tool calls per hour |
| Bucket | One clock hour in UTC, keyed YYYY-MM-DD-HH |
| Scope | Per account, or per IP address for anonymous callers |
| Reset | On the hour, not on a rolling window |
| Response | HTTP 429, code: "hourly_rate_limit" |
| On limiter failure | Fails open. The call is allowed |
The 429 body:
{
"error": "Hourly fair-use limit reached (100 light-tool calls/hour). This protects our infrastructure from abuse while keeping your monthly usage unlimited. Please wait ~14 minutes for the next hourly reset.",
"code": "hourly_rate_limit",
"limit": 100,
"used": 100,
"reset_in_minutes": 14
}Because the bucket is a clock hour rather than a sliding window, the wait is never more than 60 minutes and reset_in_minutes tells you exactly how long.
IP-keyed buckets#
Two paths are limited by IP address rather than by account:
| Path | When |
|---|---|
/api/tools | The caller sent no user_email |
/api/translate | Always. This endpoint has no account concept |
Both share the same 100-per-hour ceiling, keyed on the connecting IP. Traffic from behind a shared egress address therefore shares one bucket.
The monthly credit quota#
Credits reset at the start of each UTC calendar month. Usage is the sum of AI runs and tool runs for that month.
| Plan | Credits per month |
|---|---|
| Free | 0 |
| Starter | 100 |
| Pro | 500 |
| Agency | 2,000 |
| Enterprise | 10,000 |
The 429 when the allowance is spent:
{
"error": "You've used all 10000 premium reports on the ENTERPRISE plan this month. Light tools (keyword research, SERP, content, technical audits) keep working. Quota resets on September 1, 2026 (in 26 days). Upgrade to continue using premium reports now.",
"code": "quota_exceeded",
"plan": "enterprise",
"used": 10000,
"quota": 10000,
"cost": 6,
"reset_at": "2026-09-01T00:00:00.000Z",
"reset_date": "September 1, 2026",
"days_until_reset": 26,
"upgrade_url": "https://metricvaultai.com/index.html#pricing"
}Read reset_at rather than parsing reset_date, and do not retry before it. See Quotas and rate limits and How credits work.
What the public API costs#
| Endpoint | Cost | Calls per month at each plan |
|---|---|---|
POST /api/v1/analyze | 6 credits | Enterprise 1,666. Lower plans cannot call it at all |
Usage from the API is attributed to the tool name domain_overview and appears in the same monthly totals as work done in the app. Query it with POST /api/usage, which returns used, quota, remaining, percent_used, near_limit, over_limit, days_until_reset and a per-tool breakdown.
The recommendations quota#
The Get Recommendations feature has its own separate monthly allowance, checked before the credit quota.
| Plan | AI recommendations per month |
|---|---|
| Free | Not available |
| Starter | Not available |
| Pro | 100 |
| Agency | 400 |
| Enterprise | 2,000 |
Below-tier calls return 403 with reco_upgrade_required; exhausted allowances return 429 with reco_quota_exceeded. The minimum plan and the per-plan numbers are operator-tunable, so treat the table as the current defaults rather than a contract.
Order of the gates#
The gates run in a fixed order, and the first failure wins:
- No identifiable account and a cost above zero →
401 auth_required. - Account suspended →
403 account_suspended. - Free plan and a cost above zero →
403 upgrade_required. - Cost below 3 → hourly limit, then allow.
- Cost of 3 or more → monthly quota check.
- Any lookup failure →
503, failing closed.
Payload limits#
| Endpoint | Field | Limit | Over-limit behavior |
|---|---|---|---|
/api/translate | Number of strings | 100 per request | 400 Too many strings (max 100 per request) |
/api/translate | Total characters | 20,000 per request | 400 Payload too large (max 20000 chars per request) |
/api/share/create | html_content | 2,000,000 characters | 400 Report too large — max 2MB |
/api/library/save-run | Serialised payload | 1,600,000 characters | 200 with reason: "data_too_large" |
/api/library/saved/html | html | 900,000 characters | 200 with reason: "too_large" |
/api/library/saved/delete | ids | 200 ids per call | Extra ids ignored |
/api/branding/save | logo_data_url | 520,000 characters | 400 Logo too large — keep it under 500KB |
/api/branding/save | company_name | Truncated to 120 characters | Silent truncation |
/api/branding/save | custom_footer | Truncated to 300 characters | Silent truncation |
/api/social/schedule | Post text | Truncated to 4,000 characters | Silent truncation |
/api/social/publish | X post text | 280 characters | This post is <n> characters; X allows 280. |
| Blog media upload | File size | 5 MB | 400 Max 5MB |
| Blog media import | Fetched size | 15,000,000 bytes | 413 Image is larger than 15MB |
| Blog newsletter send | Recipients | 2,000 per send | Capped, capped flag returned |
/api/tier2/compare | Brands | 5 | Extra brands ignored |
| Request body inspected for activity logging | Body size | 24,000 bytes | Larger bodies pass through unlogged |
Pagination and range limits#
| Endpoint | Parameter | Range | Default |
|---|---|---|---|
/api/library | limit | 1 to 200 | 60 |
/api/library/saved | limit | 1 to 100 | 40 |
/api/monitor/changes | limit | 1 to 500 | 200 |
/api/monitor/snapshot | limit | 1 to 100 | 20 |
/api/share/list | Rows returned | Fixed 50 | 50 |
/api/notifications | Items returned | Fixed 50, from a 14-day window | 50 |
/api/social/scheduled | Rows returned | Fixed 100 | 100 |
/api/tier2/trends/export | days | 1 to 365 | 90 |
/api/tier2/compare | days | 1 to 180 | 30 |
/api/tier2/alerts/add | drop_threshold_pct | 1 to 99 | 15 |
/api/rank-alerts/create | drop_threshold | 1 to 20 | 5 |
/api/monitor/add | check_interval_hours | 1 to 48 | 6 |
Blog public posts | per_page | 1 to 50 | 20 |
Blog stock/search | page | 1 to 20 | 1 |
Blog subscribers | Rows returned | Fixed 500 | 500 |
/api/admin/audit | limit | 1 to 500 | 50 |
/api/admin/runs | limit | 1 to 200 | 50 |
/api/admin/blog/posts | limit | 1 to 200 | 50 |
/api/admin/metrics | hours | Up to 168 | — |
/api/workflow/failures | limit | Up to 1,000 | 200 |
Timeouts#
| Operation | Timeout |
|---|---|
| Responsive analyzer, page fetch and analysis | 40 seconds |
| Responsive analyzer, follow-up chat | 25 seconds |
| Responsive preview proxy, page fetch | 15 seconds |
| Responsive preview asset fetch | 12 seconds |
| Stripe webhook signature freshness | 300 seconds of clock skew tolerated |
| Social OAuth state | 15 minutes, single use |
Long-form article generation runs inline on POST /api/premium-ai. The per-completion budget scales with the requested length: 90 seconds for the large content types (6000 output tokens or more), 45 seconds otherwise. Both sit under Cloudflare's 100-second idle ceiling. Set a client timeout above the larger one.
Set a client timeout of at least 60 seconds on POST /api/v1/analyze. It runs a model call inside the request.
Retention#
| Data | Retained |
|---|---|
| Saved tool results | 90 days |
| Article jobs | 1 hour |
| Notifications feed window | 14 days |
| Shared report links | Until deleted, or until their expiry date |
| Search Console performance cache | 1 hour |
| Extension quickview cache | 24 hours |
| Benchmarks cache | 30 days |
| Shared tool-data cache | 12 hours to 10 days, per tool |
Caching is explained in Result caching and freshness. Note that a cache hit still charges your credits: caching saves the provider bill and the wait, not the cost.
Practical sizing#
| Job | Arithmetic | Verdict |
|---|---|---|
40 domains through /api/v1/analyze nightly | 40 × 6 = 240 credits a night, 7,440 a month | Fits Enterprise's 10,000, with little headroom |
| 100 domains weekly | 100 × 6 = 600 credits a week, about 2,600 a month | Comfortable on Enterprise |
| 500 domains in one run | 3,000 credits in one sitting | Allowed, but spend 30 percent of the month in one job |
There is no concurrency limit on the public endpoint. Your monthly quota is the real ceiling, and a burst simply spends it faster.
Handling limits in a client#
- Read
codefirst.hourly_rate_limitis worth waiting out;quota_exceededis not. - On
hourly_rate_limit, sleep forreset_in_minutesand retry. - On
quota_exceeded, stop and alert a human. Retrying beforereset_atwill fail identically. - On
503withplan_check_failedorquota_check_failed, retry with exponential backoff. Nothing was charged. - Poll
POST /api/usagebefore a large job and abort ifremainingis less than the job's cost.
See also
Was this article helpful?
Thanks — feedback noted for the docs team.