Skip to main content
The Matter API enforces per-user rate limits to ensure fair usage and protect the service.

Limits

OperationLimitApplies to
Read120 requests/minAll GET requests
Write30 requests/minPOST, PATCH, DELETE requests
Save10 requests/minPOST /v1/items (saving new URLs)
Search30 requests/minGET /v1/search
Markdown20 requests/minGET requests with ?include=markdown
Burst5 requests/secAll requests (short-term ceiling)
Save requests have a lower limit because each save triggers background content extraction (fetching, parsing, and processing the URL). Markdown requests count against both the read and markdown limits because these responses are significantly larger and more expensive to serve. Limits are applied per API token (i.e., per user account).

Rate limit headers

Every response includes headers showing your current quota:
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 117
X-RateLimit-Reset: 1711814400
HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window.
X-RateLimit-RemainingRequests remaining in the current window.
X-RateLimit-ResetUnix timestamp (seconds) when the window resets.

Handling 429 responses

When you exceed the limit, the API returns 429 Too Many Requests with a Retry-After header:
{
  "error": {
    "code": "rate_limited",
    "message": "Rate limit exceeded. Retry after 12 seconds."
  }
}
HTTP/1.1 429 Too Many Requests
Retry-After: 12
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711814400
import time
import requests

def api_request(url, headers, max_retries=3):
    for attempt in range(max_retries):
        response = requests.get(url, headers=headers)

        if response.status_code != 429:
            return response

        retry_after = int(response.headers.get("Retry-After", 10))
        time.sleep(retry_after)

    raise Exception("Rate limit exceeded after retries")
Do not retry immediately or in a tight loop. Always respect the Retry-After value. Clients that ignore rate limits may have their tokens temporarily suspended.

Best practices

The updated_since parameter on list endpoints dramatically reduces the number of requests needed to stay in sync. Fetch everything once, then sync only changes.
Set limit=100 (the maximum) to reduce the number of requests needed to paginate through results.
Store items locally and only refetch when needed. The updated_at field tells you if an item has changed.
If you’re tagging many items, space your requests rather than firing them all at once.