Skip to main content

Base URL

Two authentication models

Public endpoints

No key required. Used directly by browsers — a <form>, submit.js, or the SDK’s client-side adapters. Protected by CAPTCHA, honeypot, rate limiting, and origin allowlisting instead of a credential, since a browser can’t keep a secret.

Server Data API

Requires a secret API key (Authorization: Bearer rk_live_...). Server-side only — never ship a key to a browser. Used by your backend to submit on someone’s behalf, or to read submissions/analytics/files.
An API key also authenticates a submission the same way — pass Authorization: Bearer rk_live_... on POST /api/v1/submit/p/:public_id and it skips the CAPTCHA gate entirely, since a server-to-server caller has no browser widget to solve one. Never expose a secret key in client-side code.

Public endpoints

Notice submit lives at its own top-level path, not nested under /forms/p/... — it’s the one endpoint that writes; everything else is a read or a side-effect-free helper around a form you’ve already fetched.

Rate limits

Every public endpoint is rate-limited per identity (IP, or workspace for the Server Data API), backed by a shared store — not an in-memory counter that resets per server replica. A 429 includes a human-readable message; there’s currently no Retry-After header, so back off with a fixed or exponential delay rather than parsing one.

Errors

Every error is a JSON body with an error message and, where relevant, a machine-readable code:

Response headers

Every response echoes X-Request-Id — include it when reporting an issue; it’s the fastest way to find the exact server-side log line for your request.