API Documentation

FormKeep gives every form a JSON endpoint you can POST to, and a read API you can use to fetch submissions back out.

Submitting a form (write API)

Point any HTML form's action attribute at your FormKeep endpoint URL, found on the Setup tab of the form, and submit it with a normal POST. No JavaScript is required.

<form action="https://formkeep.com/f/<your-form-token>" method="POST">
  <input type="email" name="email">
  <button type="submit">Send</button>
</form>

You can also submit the same endpoint programmatically, as JSON or as multipart/form-data (for file uploads):

curl -X POST https://formkeep.com/f/<your-form-token> \
  -H "Accept: application/json" \
  -H "User-Agent: MyApp/1.0 (support@example.com)" \
  -d "email=support@formkeep.com" \
  -d "message=Hello from curl"

Send Accept: application/json to get a JSON response instead of a redirect to a thank-you page. This is the recommended approach for AJAX or non-browser clients.

Always send a descriptive User-Agent header (as shown above). Requests with no User-Agent, or a generic/default one like the bare curl user agent, are commonly blocked by our WAF.

Uploading file attachments (paid plans)

File attachments require a paid plan. On the Free plan, submissions with file uploads are still accepted, but the attachments are dropped. See pricing or your account billing page for current attachment storage limits per plan.

Submit the form as multipart/form-data and include one or more file fields alongside your regular data fields. The field name becomes the attachment's name on the submission.

<form action="https://formkeep.com/f/<your-form-token>" method="POST" enctype="multipart/form-data">
  <input type="text" name="name">
  <input type="email" name="email">
  <input type="file" name="Resume">
  <input type="file" name="Cover Letter">
  <button type="submit">Send</button>
</form>

Or with curl, using -F per field to send a proper multipart/form-data request:

curl -X POST https://formkeep.com/f/<your-form-token> \
  -H "Accept: application/json" \
  -H "User-Agent: MyApp/1.0 (support@example.com)" \
  -F "name=Bruce Wayne" \
  -F "email=bruce@wayneenterprises.com" \
  -F "Resume=@/path/to/resume.pdf" \
  -F "Cover Letter=@/path/to/cover-letter.pdf"
  • Each individual file is limited to 10MB.
  • A single submission can include up to 50 files; extras beyond that are dropped.
  • Total attachment storage is capped per account depending on plan (for example 2GB on Essential, up to 40GB on Enterprise).
  • Multiple files can be sent under the same field name to attach several files as one named attachment.

Spam protection

Every submission gets a spam flag. FormKeep offers several ways to configure and improve spam detection per form, from the form's Settings → Spam and Settings → Field Rules tabs.

  • Detect spam using submission data — FormKeep checks incoming submissions against known spam patterns and learns from submissions you manually mark as spam or not-spam.
  • Field Rules — server-side rules evaluated on submission, e.g. "mark as spam if email is blank" or checks against specific values or IP addresses.
  • Google reCAPTCHA — integrate reCAPTCHA (including the invisible v3 flow) by adding your reCAPTCHA keys under Spam Settings and posting the token back in a field named g-recaptcha-response.
  • Honey field — a hidden field name (available on the Spam Settings tab) that a human won't fill in but a bot might; considered less reliable against modern bots.
  • Field validation ("unique") — set a field's default value to a fixed placeholder, then use JavaScript to overwrite it with a unique value on page load, and configure the field's spam validation rule to unique.

Example reCAPTCHA v3 integration:

<head>
  <script src="https://www.google.com/recaptcha/api.js?render=<your-recaptcha-site-key>"></script>
  <script>
    grecaptcha.ready(function () {
      grecaptcha.execute('<your-recaptcha-site-key>', { action: 'contact_form' })
        .then(function (token) {
          document.getElementById('g-recaptcha-response').value = token;
        });
    });
  </script>
</head>
<form action="https://formkeep.com/f/<your-form-token>" method="POST">
  <input type="text" name="email">
  <input type="hidden" id="g-recaptcha-response" name="g-recaptcha-response">
  <button type="submit">Submit</button>
</form>

Spam submissions still count against a form's monthly submission limit (see pricing), so enabling spam protection is recommended even on the Free plan. Use the spam parameter on the read API below to filter spam in or out when fetching submissions.

Reading submissions (read API)

Every form has a submissions endpoint that returns the latest submissions as JSON. It must be explicitly enabled per form under Form Settings → Advanced → Enable API, and requires the form's secret api_token, found on the form's Reports tab.

curl "https://formkeep.com/api/v1/forms/<form-token>/submissions.json?api_token=<api-token>" \
  -H "User-Agent: MyApp/1.0 (support@example.com)"

Parameters

  • api_token — string, required. The secret API token from the form's Reports tab.
  • page — integer, default 1. Which page of results to return.
  • page_limit — integer, default 25. Number of submissions per page.
  • spamtrue/false. Filter to only spam or only non-spam submissions. Omit to return both.
  • startdate / enddate — dates formatted YYYY-MM-DD (UTC) to filter by submission date.
  • include_attachmentstrue/false, default true. Set to false to speed up responses when a form has no attachments.

Example response

{
  "submissions": [
    {
      "id": 9337144,
      "data": {
        "email": "support@formkeep.com",
        "created_at": "2021-12-04 18:41:48 UTC"
      },
      "created_at": "2021-12-04T18:41:48.577Z",
      "spam": false
    }
  ],
  "meta": {
    "pagination": {
      "current_page": 1,
      "next_page": null,
      "prev_page": null,
      "total_pages": 1,
      "total_count": 1
    }
  }
}

Example response with file attachments

When include_attachments is true (the default) and the submission has file attachments, each attachment appears as a field in data named after the attachment (the form field name it was uploaded under), whose value is an array of URLs — one URL per uploaded file, not raw file data.

{
  "submissions": [
    {
      "id": 9337144,
      "data": {
        "name": "Josiah Bartlett",
        "email": "josiah@example.com",
        "created_at": "2021-12-04 18:41:48 UTC",
        "resume": [
          "https://formkeep.com/rails/active_storage/blobs/redirect/<blob-token>/resume.pdf"
        ]
      },
      "created_at": "2021-12-04T18:41:48.577Z",
      "spam": false
    }
  ],
  "meta": {
    "pagination": {
      "current_page": 1,
      "next_page": null,
      "prev_page": null,
      "total_pages": 1,
      "total_count": 1
    }
  }
}

A field with multiple files uploaded under the same name returns multiple URLs in that field's array. Attachment URLs are redirects to the actual file and may require the requester to follow the redirect (curl -L) to download the file itself. Pass include_attachments=false to omit attachment fields entirely and speed up the response for forms that don't need attachment data back.

Errors

  • Missing or invalid api_token returns an empty submissions array rather than an authentication error.
  • An invalid startdate or enddate returns an empty submissions array with an "error" key in the pagination metadata.
  • If the read API has not been enabled for the form, or the form is disabled, the endpoint returns an empty submissions array.
  • File attachments submitted on the Free plan are dropped; the rest of the submission is still accepted and saved.
  • Individual files over 10MB, or submissions with more than 50 files, have the offending file(s) dropped rather than rejecting the whole submission.

Sign in to get your form's endpoint URL and API token from its Setup and Reports tabs.

For full documentation, guides, and answers to common questions beyond this API reference, visit FormKeep Support.