send.webforms.toRead the docs →

Documentation

Submissions API

Read your forms and submissions from your own code. Available on the Pro and Agency plans. For creating and configuring forms instead of just reading them, see the write API.

Authentication

Create a key in Settings → API keys - it is shown once and stored only as a hash, so save it when you create it. Authenticate with a bearer token or the X-API-Key header. Keys are secret: keep them on a server, never in client-side code.

Terminal
curl https://api.webforms.to/v1/submissions \
  -H "Authorization: Bearer wf_live_your_key"

Or equivalently:

Terminal
curl https://api.webforms.to/v1/submissions \
  -H "X-API-Key: wf_live_your_key"

Every key is scoped to a workspace and a set of permissions (scopes) fixed when it is created. Read keys default to submissions:read and forms:read.

GET /v1/forms

List the forms in your workspace.

Response
{
  "ok": true,
  "data": [
    { "id": "frm_...", "name": "Contact form", "publicKey": "wft_...", "isTestMode": false, "createdAt": "2026-08-16T20:00:00.000Z" }
  ]
}

GET /v1/submissions

List submissions, newest first, across the workspace or one form.

Response
{
  "ok": true,
  "data": [
    {
      "id": "sub_…",
      "formId": "frm_…",
      "data": { "email": "jane@example.com", "message": "Hi" },
      "files": [],
      "meta": { "submittedAt": "2026-08-16T20:00:00.000Z", "ip": "…" },
      "spamScore": 0,
      "status": "new",
      "tags": [],
      "createdAt": "2026-08-16T20:00:00.000Z"
    }
  ],
  "pagination": { "limit": 25, "hasMore": true, "nextCursor": "…" }
}

Query parameters:

  • form_idRestrict to one form.
  • statusComma-separated: new, quarantined, spam. Quarantined and spam are excluded by default - ask for them explicitly.
  • limit1-100, default 25.
  • cursorOpaque pagination cursor from the previous response's nextCursor.
  • include_testInclude submissions marked with _test.

GET /v1/submissions/:id

Fetch a single submission by id, same shape as a list item.

Pagination

Cursor-based, not offset. Pass the previous response's nextCursor value back as cursor to fetch the next page, and stop when hasMore is false. Cursors are stable as new submissions arrive, so a walk never skips or repeats a row - unlike an offset, which drifts on a table that new rows are constantly appended to.

Rate limits

120 requests per minute per key. Every response carries X-RateLimit-Remaining and X-RateLimit-Reset. Revoking a key in the dashboard takes effect immediately - no propagation delay.

Get your endpoint

Free for 300 submissions a month. No card required.

Start free