Rate limits
The request limits on Matchai's public MCP server and listing search today, how to read them, and how limits and RateLimit headers will work in the Matchai API.
Limits keep Matchai fast for everyone. This page states what the public surfaces allow today, and how the planned Matchai API will report and apply its limits.
On the public surfaces
These limits apply to the surfaces you can use today without a key. Each is counted per calling address: the address your requests come from. Callers behind a shared address, such as an office network or a hosted assistant, share one allowance.
| Surface | Limit | When you go over |
|---|---|---|
MCP server tool calls (tools/call at https://matchai.co/mcp) | 600 per minute | The call returns a tool result with isError: true and a message asking you to try again in a minute. The HTTP status is still 200. |
Listing search (https://matchai.co/ask, GET and POST together) | 60 per minute | 429 with the error rate_limited and a Retry-After of 60 seconds. |
A few details:
- The MCP limit counts
tools/callonly. The handshake,tools/listand the other methods are not counted. It is an abuse guard, set high enough that a busy assistant host does not reach it. - Listing search can also answer
503. When search is temporarily unavailable, it answers503with the errorunavailableand aRetry-Afterof30seconds. Wait that long, then try again. - AI-readable pages have no per-minute figure, but ask you to be polite: about one request per second per host. To read a whole site, fetch its
llms-full.txtinstead of crawling page by page. On a429, wait forRetry-After. The discovery files change at most about once a minute, so polling faster returns the same content.
See listing search and the MCP server for the full picture.
Handle a limit in code
async function ask(query) {
const response = await fetch(`https://matchai.co/ask?query=${encodeURIComponent(query)}`);
if (response.status === 429 || response.status === 503) {
const wait = Number(response.headers.get('Retry-After') ?? 60);
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
return ask(query); // Try once more after the wait.
}
return response.json();
}Add a counter in real code, so a request that keeps failing eventually gives up.
In the Matchai API
Coming soonThe Matchai API limits requests per connection: one Matchai account connected to one app. The figures are not published here yet. Do not hard-code them: the API tells you where you stand on every response.
Rate limit headers
Every response carries three headers:
| Header | Meaning |
|---|---|
RateLimit-Limit | Requests allowed in the current window for this connection |
RateLimit-Remaining | Requests left in the current window |
RateLimit-Reset | Seconds until the window resets |
RateLimit-Limit: 120
RateLimit-Remaining: 117
RateLimit-Reset: 41
Matchai-Request-Id: req_7Hq2LmZ9xV4cThe numbers above are an illustration. They are not the real limit.
When you go over
A request over the limit is refused with 429 and does nothing. The response carries Retry-After, the number of seconds to wait:
{
"error": {
"type": "rate_limit_error",
"code": "rate_limited",
"message": "Too many requests. Try again in 20 seconds.",
"doc_url": "https://matchai.co/docs/developers/errors#rate_limited",
"request_id": "req_7Hq2LmZ9xV4c"
}
}A 429 is safe to retry. Wait for Retry-After, then send the request again with the same Idempotency-Key, so that nothing happens twice. Retry safely has a complete example.
Who shares a limit
- All the keys of one account share one limit, because they belong to one connection. A second key does not buy more room. See API keys.
- Two professionals connected to the same app have separate limits. One broker's busy morning does not slow another broker's connection.
- Public operations work without credentials, with lower limits. Looking up a professional's published profile or listings needs no key. Those requests are counted per IP address, and sending a key or token raises the limit. They are marked Free in the API reference.
Limits are not Energy limits
An action that uses Matchai Energy can be refused for two other reasons, and neither is a 429:
- The account has run out of Energy:
402with the codeinsufficient_energy. - The account holder set an Energy limit for your connection, and you reached it:
402with the codeenergy_limit_reached.
Waiting does not clear either one. See errors.
Stay well under the limit
- Read the headers. Slow down when
RateLimit-Remainingis low, and pause untilRateLimit-Resetwhen it reaches zero. - Listen instead of polling. Webhooks tell you when a lead arrives or a listing changes, with no requests at all.
- Sync what changed. Use
updated_sinceon list endpoints, and ask forlimit=100pages, the largest page. - Spread out bulk work. Importing a hundred contacts is a queue, not a burst.
- Repeat only what failed. Idempotency keys let you retry a single request safely.
export async function pacedFetch(url, init) {
const response = await fetch(url, init);
const remaining = Number(response.headers.get('RateLimit-Remaining'));
const reset = Number(response.headers.get('RateLimit-Reset'));
// Nearly out of requests: wait for the window to reset before the next call.
if (remaining <= 1 && reset > 0) {
await new Promise((resolve) => setTimeout(resolve, reset * 1000));
}
return response;
}