# Rate limits (https://webmetic.de/docs/en/api/anfragelimits)

The eight visit endpoints of Visitor Intelligence answer one request per second per endpoint. For example, your n8n workflow calls `/company-sessions` for each of your websites in turn. If it waits one second between calls, it runs through without errors. There is no daily or monthly request quota.

## Rate limit per endpoint [#rate-limit-per-endpoint]

These endpoints accept one request per second: `/company-sessions`, `/company`, `/company_nace`, `/session`, `/session_user_data`, `/intensive-visits`, `/returning-visits`, and `/new-visits`.

The limit applies to each endpoint separately: a call to `/company` and one to `/session` in the same second don't affect each other. The limit is the same on every plan.

The [Partner API](https://webmetic.de/docs/en/partner/partner-api) has its own per-minute limits.

Enrichment (`/enrichment/*`), contacts (`/contacts/*`), and webhooks (`/webhooks`) currently have no rate limit. That can change, so build the retry on status `429` into every integration. When you unlock contact data, your credits limit the amount, not the number of requests. For details, see [Credits in the API](https://webmetic.de/docs/en/api/credits).

## Response when you exceed it [#response-when-you-exceed-it]

If you exceed the limit, the API answers with status `429` and the error code `RATE_LIMITED`. The `Retry-After` header gives the seconds until the next attempt:

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 1

{
  "detail": {
    "error": "RATE_LIMITED",
    "message": "Rate limit exceeded: 1 per 1 second. Wait and try again."
  }
}
```

The rejected request returns no data. Wait the number of seconds in `Retry-After` and then send the request again. On the visit endpoints that's one second, and the Partner API gives the value for its per-minute limit. All error codes are listed under [Errors](https://webmetic.de/docs/en/api/fehler).

## Retry with a wait [#retry-with-a-wait]

Send requests to the same endpoint one after another, not in parallel. If the API still answers with status `429`, we recommend this flow:

1. **Send the request**: Your integration calls the endpoint.
2. **Check the status**: For any status other than 429, it processes the response.
3. **Wait**: On 429, it waits the seconds in `Retry-After`. If the header is missing, it waits one second and then twice as long each time.
4. **Send again**: After five attempts, it stops and reports the error.

The example calls `/company-sessions` with this flow:

```bash tab="cURL" tab-group="lang"
delay=1
for attempt in 1 2 3 4 5; do
  status=$(curl -s -D headers.txt -o response.json -w "%{http_code}" \
    "https://hub.webmetic.de/company-sessions?domain=your-website.com" \
    -H "Authorization: wmtc_YOUR_API_KEY")
  if [ "$status" != "429" ] || [ "$attempt" -eq 5 ]; then break; fi
  retry_after=$(grep -i '^retry-after:' headers.txt | tr -dc '0-9')
  sleep "${retry_after:-$delay}" # Retry-After first, else the backoff
  delay=$((delay * 2)) # wait longer after each 429
done
```

```js tab="JavaScript" tab-group="lang"
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

async function fetchWithRetry(url, attempts = 5) {
  let delay = 1000;
  for (let i = 1; i <= attempts; i++) {
    const res = await fetch(url, {
      headers: { Authorization: 'wmtc_YOUR_API_KEY' },
    });
    if (res.status !== 429 || i === attempts) return res;
    const retryAfter = Number(res.headers.get('Retry-After'));
    await sleep(retryAfter > 0 ? retryAfter * 1000 : delay); // Retry-After first, else the backoff
    delay *= 2; // wait longer after each 429
  }
}

const res = await fetchWithRetry(
  'https://hub.webmetic.de/company-sessions?domain=your-website.com',
);
```

```python tab="Python" tab-group="lang"
import time
import requests

def get_with_retry(url, params, attempts=5):
    delay = 1
    for attempt in range(1, attempts + 1):
        res = requests.get(
            url,
            params=params,
            headers={"Authorization": "wmtc_YOUR_API_KEY"},
        )
        if res.status_code != 429 or attempt == attempts:
            return res
        retry_after = res.headers.get("Retry-After", "")
        time.sleep(int(retry_after) if retry_after.isdigit() else delay)  # Retry-After first, else the backoff
        delay *= 2  # wait longer after each 429

res = get_with_retry(
    "https://hub.webmetic.de/company-sessions",
    {"domain": "your-website.com"},
)
```

## Size limits [#size-limits]

Besides the rate limit, the API limits how much a single request covers:

| Area | Limit |
| --- | --- |
| Visitor Intelligence | `page_size` up to 10,000 entries per page, pages from 1 |
| Contact search | `size` 10 to 25 results per page, pages from 0 |
| Enrichment | Up to 5,000 domains per call, `page_size` up to 10,000 entries per page |
| Webhooks per account | 50 through the API, 10 through the dashboard. Both count together |
| Custom headers per webhook | Up to 3 |
| Response time of your server for webhooks | 10 seconds |
| Retries of failed deliveries | 3 days |

If a request exceeds a size limit, the API answers with status `422` and `VALIDATION_ERROR`. If you create a webhook beyond the account's limit, the API answers with status `409` and `WEBHOOK_LIMIT_REACHED`.

## Next steps [#next-steps]

- [Errors](https://webmetic.de/docs/en/api/fehler): Every status code and error code of the API with the matching fix.
- [Pagination](https://webmetic.de/docs/en/api/paginierung): Fetch large results page by page.
- [Webhooks](https://webmetic.de/docs/en/api/webhooks): Receive events instead of polling the API.
