Quotas and rate limits
Two separate things bound what you can do: how many searches your plan allows per day, and how fast requests may arrive. Response headers report both, so a client can pace itself without having to provoke an error to find out where the edges are.
Rate limit: ten requests per minute
The limit is per account and is shared by the API and the
MCP server: ten calls in a minute, whichever way
they arrive. Searches and all cluster endpoints count, as do the MCP tools
search and the cluster tools; /v1/account and the MCP
tools account and read_docs do not. A refused request
counts too, unless it was refused before the rate check: a wrong key, no plan, or
a search with no query, a wrong format or column, or no quota left. An eleventh
request inside the window comes back immediately with
429 too_many_requests and a Retry-After header giving
the seconds until a slot frees up. The same number is in the body as
error.retry_after.
HTTP/2 429
Retry-After: 18
{ "error": { "code": "too_many_requests",
"message": "At most 10 requests per minute.",
"retry_after": 18 } }
Sleep for Retry-After seconds and repeat the request. Nothing was
consumed and no quota was spent.
The API never holds a connection open to slow you down. The old export urls do - they sleep a second at a time for up to half a minute before refusing - which is one of the reasons the API exists.
Daily quota
Your plan allows a number of searches per day. Searches with snippets fall within that allowance and have a smaller daily limit of their own inside it: Pro, for example, allows 100 searches a day, up to 30 of them with snippets. Both reset at the next UTC midnight, not 24 hours after use.
When the daily searches are used up, every search is refused with 429 quota_exceeded, with snippets or without. When only the snippet limit is used up, searches with snippets are refused with 429 snippet_quota_exceeded, and ordinary searches still work. Each refusal carries the limit, what has been used, and how long until the reset.
What costs what
| Request | Costs |
|---|---|
/v1/search | One search; with snippets=1, also one from the snippet limit. |
| A cluster from a query | One search. |
| A cluster from a list, combine, rename, delete | Nothing. |
| Reading clusters, presets, signatures | Nothing. |
/v1/clusters/{id}/extract | Extraction points: each site found in the index costs as many points as values were found on it, 0.1 if none. |
/v1/account | Nothing, and it does not count against the rate. |
Trying an expression? Start with a small limit on extract, say 10:
it is the number of sites, and every value found on them is paid for.
Result depth
A plan also decides how far down the ranking results stay disclosed -
disclosed_positions from /v1/account. Rows past that
point are left out rather than blanked, and when any were,
truncated is true in the body and
X-Truncated: true in the headers.
This is the difference that matters most between the API and the website. A browser whose quota has run out quietly falls back to free-tier depth and shows less, which is fine for a person looking at a page. A script cannot see it happen, so the API refuses instead of shortening.
Reading the current state
The names are easy to mix up: X-Requests-* are the rate, while
X-RateLimit-* are the daily searches, which is their historic name.
| Header | Meaning |
|---|---|
X-Requests-Limit | Requests allowed per minute: 10. |
X-Requests-Remaining | Requests left in this minute. |
X-RateLimit-Limit | Searches allowed today. |
X-RateLimit-Remaining | Searches left today. |
X-RateLimit-Reset | Unix time when the day's quota resets. |
X-Snippets-Limit | Snippet requests allowed today. |
X-Snippets-Remaining | Snippet requests left today. |
X-Requests-*come on the requests that count against the rate, a429 too_many_requestsincluded./v1/accounthas none.- The other five come with search results,
/v1/accountand answers of the cluster endpoints. Refusals do not reliably carry them: a refused search never does, nor does a401,403 plan_requiredor429 too_many_requestsanywhere. Count on them only on a successful answer.
Search results carry four more:
| Header | Meaning |
|---|---|
X-Total-Results | How many sites match in the whole index. |
X-Returned-Results | How many rows this response carries. |
X-Truncated | true when the plan's depth limit removed rows. |
X-Index-Complete | false when part of the index did not answer in time. |
Usage statistics
/v1/account is the full picture in one call. It spends nothing and
does not count against the rate:
curl -H "Authorization: Bearer $KEY" https://api.publicwww.com/v1/account
{
"plan": "enterprise",
"plan_until": 1819461840,
"full_access": true,
"quota": {
"searches": { "limit": 300, "used": 12, "resets_at": 1787961600 },
"snippets": { "limit": 100, "used": 3, "resets_at": 1787961600 }
},
"limits": {
"disclosed_positions": 4294967295,
"disclosed_positions_snippets": 4294967295,
"max_per_page": 1000000,
"max_per_page_snippets": 10000
},
"clusters": {
"count": 7,
"max": 100,
"max_size": 1000000,
"extract_points_per_day": 1000000,
"extract_points_left": 998166,
"resets_at": "2026-08-29T00:00:00Z"
}
}
resets_at is the same moment spelled two ways: a unix time under
quota, an ISO 8601 string under clusters.
The older https://publicwww.com/profile/api_status.xml?key=...
reports the same counters as XML and still works. It belongs to
the old urls; new code should use
/v1/account, which also reports the limits, not only the counts.