Rate limits

How fast one visitor can post to a form, what an over-limit request gets back, and how to send higher volumes from a server.

Works on every planUpdated Sep 22, 2026

The limits

Every form endpoint is rate limited, with nothing to switch on and nothing to configure. A browser post is counted per IP address and form, and has to be inside both the burst and the sustained window to be let through. A call made with an API key has a single window of its own:

WindowAllowsCatches
Burst5 per 30 secondsRapid-fire scripts and a finger stuck on the submit button.
Sustained30 per 10 minutesSlow, steady automation that stays under the burst limit.
API key300 per 1 minuteServer-to-server calls, counted per key and form instead of per IP.

The limits are per form, so a busy visitor on one of your forms is never throttled on another. Every request counts, including ones that are later refused as spam or fail validation.

How the windows work

  • A window starts with the first request and resets when it expires. It does not slide forward with each new request, so a caller who waits out the window starts clean.
  • The rate limit is checked before your quota. A flood of requests is turned away without using a single unit of your monthly allowance.
  • Nothing is blocked permanently. Windows are short on purpose, so one burst from a shared office or school network recovers on its own.
If our limiter itself is unavailable, submissions are let through rather than refused. Losing a real enquiry is worse than a few minutes without throttling, and the other layers in spam filtering still run.

Handling a 429

An over-limit request gets 429 with the code rate_limited and a Retry-After header giving the seconds until the window that tripped resets. A browser doing a plain HTML post sees a short error page instead of JSON.

JavaScript
const form = document.querySelector('form');

form.addEventListener('submit', async (event) => {
  event.preventDefault();

  const response = await fetch('https://mtform.co/f/your-form-key', {
    method: 'POST',
    headers: { Accept: 'application/json' },
    body: new FormData(form),
  });

  if (response.status === 429) {
    const body = await response.json();
    const wait = Number(response.headers.get('Retry-After') ?? 30);
    if (body.error.code === 'rate_limited') {
      alert('Sent too quickly. Try again in ' + wait + ' seconds.');
    } else {
      alert(body.error.message);
    }
    return;
  }

  if (response.ok) form.reset();
});
Response · 429 Too Many Requests
{
  "ok": false,
  "error": {
    "code": "rate_limited",
    "message": "Too many submissions. Please wait a moment and try again."
  },
  "request_id": "0b7e4c1a-9f2d-4a8e-b3c5-6d1f2e8a7b90"
}

Check the code, not only the status. quota_exceeded is also a 429, but it means the workspace has used its monthly allowance, carries no Retry-After, and waiting a few seconds will not fix it.

Retrying automatically? Wait at least the Retry-After value, and send an Idempotency-Key so a retry can never create a second submission. See limits and idempotency.

Sending from your own server

If your backend forwards submissions, every one arrives from the same IP address and the burst window of 5 per 30 seconds will throttle it. Post to the authenticated endpoint with an API key instead: it is limited per key and form at 300 per 1 minute, and it skips the browser-only checks. Create a key under Settings in the dashboard, then follow server-side submissions.

Next steps