All docs
4 min read

Rate limits

We rate-limit aggressively. It's how we keep the platform fast for everyone and how we cut off spam floods before they cost you money.

When you exceed a limit, the response is HTTP 429 with a Retry-After header indicating how many seconds to wait.

Submission endpoint limits

Per-IP, per-form limits — applied at the submission endpoint POST /f/{public_id}:

Plan Per IP per minute Per IP per hour Per form per minute
Free 5 30 60
Pro 20 200 300
Team 60 600 1,000
Scale Negotiated Negotiated Negotiated

The "per form per minute" cap applies across all IPs — it's a flood guard. If a form is being flooded, we want the form to throttle even if the floodier rotates IPs.

When throttled at the IP level, the offending IP gets the 429. Other IPs keep working. When throttled at the form level, all IPs hit 429 until the rolling window drains.

REST API limits

Per-token limits — applied at all /api/v1/... routes:

Plan Requests per minute
Free 30
Pro 300
Team 1,000
Scale Negotiated

Tokens with submissions:export are subject to a separate, lower quota for exports specifically — 5 per minute on Pro, 30 per minute on Team — because exports can be expensive.

MCP limits

Per-token limits, same shape as the REST API. The MCP server is layered on top of the REST API for transport, so the same per-minute quotas apply. Bulk operations count as 1 request even if they touch 500 records.

Webhook delivery limits

Outbound — limits on how often we call your endpoint:

Plan Concurrent deliveries to one host Per-host per second
Free (no webhooks)
Pro 4 10
Team 10 50
Scale Negotiated Negotiated

If your endpoint is slow, we'll back off — repeated 5xx responses or timeouts trigger our retry schedule (1m, 5m, 15m, 1h, 6h, then mark failed). See webhook deliveries.

Reading Retry-After

A 429 response always includes:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{ "error": "rate_limited", "scope": "ip", "limit": 5, "window_seconds": 60 }

Retry-After is in seconds. Wait that long, then retry. Most HTTP clients and SDKs honour the header automatically — check yours.

The scope field tells you what limit fired:

  • ip — too many submissions from one IP
  • form — too many submissions across all IPs to one form
  • token — too many API calls per token
  • export — too many export calls per token
  • webhook — too many concurrent deliveries (only seen by your webhook handler when we throttle, never returned to clients)

Whitelisting trusted endpoints

Short answer: we don't whitelist trusted IPs. Even your own.

It looks paranoid; it's actually pragmatic. Once we accept "trust this IP," every customer wants their CI server, their backup script, and their friend-of-a-friend's lambda whitelisted. We end up running a permission system inside the rate limiter, which is exactly the surface you don't want to harden.

What works instead:

  • Use the REST API token's per-token quota, not the per-IP submission cap. API calls aren't IP-limited the way submission endpoints are.
  • Spread bursty work over time. A nightly job that needs to ingest 5,000 submissions should run at ~50/sec, not 5,000 in one second.
  • Use bulk endpoints. bulk_submissions (MCP) and POST /api/v1/forms/{public_id}/submissions/bulk (REST) accept up to 500 ids per call. One bulk call is one rate-limit unit.
  • Negotiate on Scale. Scale plans get custom limits as part of contract — that's the legitimate "whitelist" path.

Tuning per plan

The defaults work for nearly everyone. If they don't:

  • Pro / Team. Limits aren't tunable per-team. Upgrade to the next tier or split traffic across multiple forms.
  • Scale. Submit your expected peak QPS during onboarding and we'll provision against it.

If you're consistently hitting 429s on a Pro or Team plan and can't structure around it, that's a signal you've outgrown the tier. Talk to us — sometimes we'll give a temporary bump while you migrate.

What 429 doesn't mean

A 429 is not the same as "the submission is lost." On the submission endpoint, the request never reached the inbox — the rate limiter rejected it before any processing. Same on the API: the call did not execute. Retry after the window and the operation runs as if it were the first attempt.

Burst behaviour

Limits are implemented as token-bucket algorithms with a 60-second window. You can burst above the per-second average for short periods and the bucket refills smoothly. The headers you'll see during normal operation include:

X-RateLimit-Limit: 300
X-RateLimit-Remaining: 248
X-RateLimit-Reset: 1715080260

Watch Remaining. When it drops to 0, the next call returns 429 with Retry-After.

What's next