splitforms.com

Contact Form API for any frontend

A contact form API is an HTTPS endpoint that receives a form POST, validates and spam-filters it, stores it and notifies the owner. splitforms runs one at https://splitforms.com/api/submit, free for 500 submissions in total.

Updated · facts checked against the code and /pricing

One endpoint, any frontend. REST, signed webhooks and an MCP server for AI agents.
contact.htmlHTML
<form action="https://splitforms.com/api/submit"
      method="POST">
  <input type="hidden" name="access_key"
         value="YOUR_KEY">
  <input name="email" type="email" required>
  <textarea name="message"></textarea>
  <button>Send</button>
</form>
200 OKaccepted
{
  "success": true,
  "message": "Submission received"
}

Landed in your inbox

nowAnna Lopez[email protected]Quick design ask
500 free submissions totalSpam filtered on every planHTML, React, Next.js, curl, Python

Answers

What a contact form API is — and how to use one.

Short answers about the splitforms endpoint, https://splitforms.com/api/submit: how to connect a form, what it costs and what it returns, for HTML, React, Next.js, curl or Python. Free covers 500 submissions total (one-time); Pro is $5/month for 5,000 a month.

What is a contact form API?

A contact form API is an HTTPS endpoint that receives a form POST, validates and spam-filters it, stores it and notifies the owner. It replaces the server route, database, and mail configuration you would otherwise build and maintain yourself.

How do I connect a form to the API?

Set your form's action to https://splitforms.com/api/submit with method="POST", and add a hidden access_key field. That is the whole integration for plain HTML. From React, Next.js, Vue, curl, or Python, POST the same fields as FormData or JSON to the same URL — the endpoint does not care what rendered the markup.

Is the contact form API free?

Yes — 500 submissions total (one-time), unlimited forms, no credit card. Free includes spam filtering, the searchable dashboard, owner notification emails, and the MCP server. Paid plans start with Pro at $5/month for 5,000 submissions a month and add HMAC-signed webhooks, CSV/XML/PDF export, integrations, and an auto-responder; Annual is $49/year for 15,000 a month.

Do I need a backend or server?

No. That is the entire point of the endpoint. There is no server to host, no API route to write, and no database to run. Static sites on GitHub Pages, Netlify, Vercel, Cloudflare Pages, or any shared host can all take submissions, because the form posts straight to splitforms rather than to your own origin.

What does the API return?

JSON for fetch and API calls: { success, message } on a 200. Validation, auth and rate-limit failures return { success: false, message, code } with a 4xx status, so you can branch on it directly in a fetch handler without parsing HTML. A note and files field appear when uploads are dropped. A plain HTML post gets a hosted thank-you page or your redirect instead.

Can an AI agent create forms through the API?

Yes — splitforms ships an MCP server with 12 tools. Claude, Cursor, Windsurf, and any other MCP client can call create_form, list_submissions, create_webhook, and get_usage directly, so an agent can provision a form and wire up its webhook without a human opening the dashboard.

API facts

What does the splitforms contact form API accept and return?

The splitforms contact form API takes a POST at one endpoint and answers in JSON. Each row below is checked against the endpoint's source code.

Last verified
FactBehavior
EndpointPOST https://splitforms.com/api/submit. Cross-origin requests are allowed and the OPTIONS preflight returns 204.
Content typesapplication/x-www-form-urlencoded, multipart/form-data, application/json. Any other Content-Type, or none, returns 415 unsupported_content_type.
access_keyRequired. The form's public identifier, sent as a hidden field or JSON property. Missing returns 400 missing_access_key; unknown returns 404 invalid_access_key.
botcheckOptional honeypot that must stay empty. When it is filled, the request gets a 200 that looks like success, but nothing is stored or sent.
Success response200 with JSON { success: true, message } for fetch and API calls, plus redirect, note or files when they apply. A native HTML post gets a hosted thank-you page or your redirect.
Error response4xx with JSON { success: false, message, code }, for example 400 invalid_body, 403 origin_not_allowed, 404 invalid_access_key or 413 payload_too_large.
Rate limit6 per minute and 20 per 15 minutes per IP, per form. Going over either returns 429 rate_limited.
Plan allowanceWhen Free's 500 total submissions are used, new ones get 429 quota_reached (with quota.limit and quota.used) until you upgrade. Paid plans return the same code at their monthly cap.
RedirectA hidden redirect field sends a native HTML post a 303 See Other to the validated URL. fetch and JSON callers get it back as a redirect field and navigate themselves.
File uploadsmultipart/form-data, up to 5 files of 5 MB each per submission on paid plans with Storage connected. Free saves the text fields, drops the files and adds a note. Larger bodies return 413 payload_too_large.
Webhook signingPaid plans. X-Splitforms-Signature: sha256=<hex>, an HMAC-SHA256 of the raw body with your webhook secret. 8-second timeout and one retry after about 2 seconds on network errors, timeouts and 5xx; 4xx is not retried.
MCP endpointhttps://splitforms.com/api/mcp over HTTP with an Authorization: Bearer token. 12 tools, on every plan including Free.
OpenAPIAn OpenAPI 3.1 description of POST /api/submit is published at https://splitforms.com/openapi.json.

Machine-readable copies: OpenAPI 3.1 spec and brand-facts.json. Every field, status and error code is in the API reference.

No backend to host

Point your form's action at one endpoint. No server, API route, or database to build or maintain.

Spam filtered on every plan

Honeypot, time-trap, content heuristics, and rate limits screen submissions before they reach you.

REST API + free MCP server

One API token powers the read API and a free MCP server, so AI agents can manage forms directly.

Copy-paste ready

Point any form at one API endpoint.

splitforms is exactly that: point any HTML, React, or Next.js form at https://splitforms.com/api/submit with a hidden access_key field. Each accepted submission is saved to your dashboard first, then emailed to you. The same endpoint takes a POST from fetch or Python (both shown below) or curl — every status and error code is in the API reference and the OpenAPI 3.1 spec.

Get your free access key
contact.html
<form action="https://splitforms.com/api/submit" method="POST">
  <input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
  <input type="hidden" name="form_loaded_at" />
  <input type="checkbox" name="botcheck" style="display:none" tabindex="-1" />
  <input name="name" required />
  <input name="email" type="email" required />
  <textarea name="message" required></textarea>
  <button type="submit">Send</button>
</form>

<script>
  // time-trap: stamp the load time so instant bot submits get flagged
  document.querySelector('[name="form_loaded_at"]').value = Date.now();
</script>

Request fields

Which fields does the contact form API accept?

Only access_key is required. Every other name you send, like name, email or message, is saved and emailed as a submission field. The fields below are control fields: they change how the submission is handled and are never stored as lead data.

FieldRequiredWhat it does
access_keyYesIdentifies your form. Public by design, so it is safe in HTML.
subjectNoSubject line of the notification email.
replytoNoReply-to address on the notification. Without it, the email field is used.
from_nameNoSender name shown on the notification email.
redirectNoThank-you page after a plain HTML post: a path like /thanks or a URL on your own domain. fetch callers get it back as redirect in the JSON.
botcheckNoHidden honeypot. Leave it empty; bots that fill it are filtered.
form_loaded_atNoPage-load timestamp in milliseconds for the time-trap. Posts sent under 2 seconds after load are flagged.
g-recaptcha-responseor cf-turnstile-responseWhen reCAPTCHA or Turnstile is enabled on the formThe widget token, verified server-side.

A default thank-you redirect can also be set in Dashboard > Form settings on paid plans. JSON responses are { success, message }, plus code on errors and note/files when uploads are dropped. Status codes and error codes are in the full API reference.

Submit with JavaScript (no page reload)

How do I submit the form without a page reload?

Keep the same HTML form, give it id="contact-form", add an empty <p id="form-status" role="status"> and intercept the submit. The Accept: application/json header makes the endpoint answer with JSON instead of its hosted thank-you page, so you can show your own message.

  • { success: true, message } means the submission was accepted.
  • HTTP 429 with code: "rate_limited" means the visitor hit the rate limit: 6 per minute and 20 per 15 minutes per IP, per form. A 429 with quota_reached means the account is out of submissions.
  • Other failures return { success: false, message, code } with a 4xx status, so data.message is safe to show.
contact.js
const form = document.querySelector('#contact-form');
const status = document.querySelector('#form-status');

form.addEventListener('submit', async (event) => {
  event.preventDefault();
  try {
    const res = await fetch('https://splitforms.com/api/submit', {
      method: 'POST',
      body: new FormData(form),
      headers: { Accept: 'application/json' },
    });
    const data = await res.json();
    if (res.status === 429 && data.code === 'rate_limited') {
      status.textContent = 'Too many submissions, try again in a minute.';
    } else if (data.success) {
      status.textContent = 'Thanks, we got your message.';
      form.reset();
    } else {
      status.textContent = data.message;
    }
  } catch {
    status.textContent = 'Network error, please try again.';
  }
});

Python

How do I POST to the contact form API from Python?

The endpoint takes JSON as well as form-encoded and multipart bodies, so a Flask or Django view, a cron job or a one-off script can send submissions the same way a browser does. Send Accept: application/json and a timeout, then read success from the response.

  • The same fields apply: access_key plus whatever you want saved.
  • If strict origin protection is on for the form, a server-side post has no matching Origin and gets a 403 origin_not_allowed, so use a form without it.
  • Files need multipart/form-data (the files= argument) and a paid plan.
submit.py
import requests

r = requests.post(
    'https://splitforms.com/api/submit',
    json={
        'access_key': 'YOUR_ACCESS_KEY',
        'name': 'Ada',
        'email': '[email protected]',
        'message': 'Hi',
    },
    headers={'Accept': 'application/json'},
    timeout=10,
)
print(r.status_code, r.json())

Free contact form APIs compared

How do the free contact form APIs compare?

splitforms Free gives you 500 submissions with no card and no time limit. For a steady monthly allowance, Pro is $5/month for 5,000 submissions a month and Annual is $49/year for 15,000 a month, both with webhooks, integrations and exports.

ServiceFree allowanceForms
splitforms500 total, no cardUnlimited
Web3Forms250/monthUnlimited
FormBold100/month5
Forminit (formerly Getform)100/month1
Formspree50/monthUnlimited
Formcarry50/month1

Checked September 27, 2026. Competitor free tiers are monthly allowances; splitforms Free is 500 submissions in total.

Setup

How to connect a form to the contact form API.

Works the same way whether your frontend is a React app, plain HTML, or something else — the endpoint doesn't care what rendered the markup. For fetch code that sends FormData, URL-encoded or JSON bodies, see how to send form data to an API.

Create a free access key

Sign up for splitforms (no credit card) and create a form in the dashboard. Copy its access_key — a public identifier that goes in your form, not a secret.

Point your form at the endpoint

Set your HTML form's action to https://splitforms.com/api/submit with method POST, or POST FormData/JSON to the same URL from React, Next.js, Vue, or any HTTP client. Include the access_key as a hidden field.

Add the spam-prevention fields

Add a hidden botcheck input that must stay empty (a honeypot) and a hidden form_loaded_at field stamped with the page-load time. Both are checked automatically before a submission is accepted.

Set optional control fields

Add subject to customize the email subject line, redirect to send visitors to a thank-you page, and replyto to control the reply-to address on notification emails.

Test it and check your inbox

Submit the form once. The owner notification email is free on every plan, and the submission shows up in the dashboard immediately.

Automate what happens next

Add signed webhooks, native Slack, Notion, or Google Sheets integrations, or an auto-responder on a paid plan (Pro at $5/month or Annual at $49/year) once email alone isn't enough.

Where submissions go

Where do your contact form API submissions go?

Every submission is emailed to you and saved to a searchable dashboard — spam filtered before it ever reaches you. Pull them back out with the read API (GET /api/submissions), or, on paid plans, export to CSV or forward it to a webhook or Slack.

  • 500 submissions total, free to start
  • Read API: GET /api/submissions with a Bearer token
  • Signed webhooks and Slack or Discord alerts on paid plans
Contact form API submissions in the splitforms dashboard

No backend needed

Do you need a backend for a contact form API? No.

Your form POSTs standard FormData or JSON to one URL. splitforms validates the access_key, runs the spam checks, and forwards it to your email — so there's no server, API route, or database for you to build or maintain.

  • POST /api/submit — multipart, url-encoded, or JSON
  • Returns JSON { success, message } to fetch and API calls
  • Works from HTML, React, Next.js, or any HTTP client
How the splitforms contact form API processes a submission

Spam filtering

Does the contact form API stop spam?

Every plan screens submissions with a honeypot, a time-trap, content heuristics, and per-form rate limits before anything reaches you — a reCAPTCHA-free way to keep bots out without a puzzle for real visitors.

  • Honeypot + time-trap on every plan, including Free
  • 6 per minute and 20 per 15 minutes per IP, per form
  • Optional allowed-domain locking per form
Spam filtering on the splitforms contact form API

REST + MCP

Drive the contact form API with REST and MCP.

One API token powers both the REST read API and a free MCP server, so agents like Claude and Cursor can create forms and read submissions directly — the same key, no bespoke integration. Full request and response examples live in the API reference.

  • Free MCP server on every plan — 12 tools
  • create_form, generate_form, list_submissions
  • Same Bearer token as the read API
The splitforms contact form API MCP server for AI agents

Questions

Contact form API questions.

View all FAQs
What's included in the splitforms contact form API?

One POST endpoint (/api/submit) that accepts any HTML form or fetch/JSON request, a read API for pulling submissions back out, a free MCP server for AI agents, file uploads on paid plans, thank-you redirects, and a replyto override — plus CC/BCC on paid plans. Spam filtering (honeypot, time-trap, content heuristics, allowed-domains) and owner notification emails are included on every plan, including Free.

Which contact form APIs are free, and how many submissions do they allow?

splitforms gives 500 submissions total (one-time) with unlimited forms; Web3Forms gives 250 a month; FormBold and Forminit give 100 a month (Forminit on one form); Formspree and Formcarry give 50 a month (Formcarry on one form). A monthly allowance lasts longer for a steady trickle; splitforms suits you if you want a bigger up-front allowance, then webhooks and exports from $5/month.

Do I need an API key to use it?

You need two different credentials. Every form has an access_key — a public identifier you put in a hidden field or POST body that tells splitforms which form a submission belongs to. Your account also has one API token, found at Dashboard → MCP, that authenticates the read API (GET /api/submissions) and the MCP server with an Authorization: Bearer header. Only the access_key belongs in client-side HTML.

Is it safe to put the access_key in my HTML?

Yes. The access key is a public form identifier, not a secret. Abuse is limited by the honeypot and time-trap, rate limits of 6 per minute and 20 per 15 minutes per IP, per form, and optional allowed-domain locking. Your account API token, from Dashboard > MCP, is the secret: never put it in client-side code.

Is the contact form API really free?

Yes. Free covers 500 submissions total (one-time), unlimited forms, spam filtering, the dashboard, owner notification emails, and the MCP server — no credit card required. Paid plans add CSV/XML/PDF export, webhooks, integrations, file uploads, CC/BCC and an auto-responder. Pro is $5/month for 5,000 submissions a month, and Annual is $49/year for 15,000 a month.

How do I POST to the contact form API from curl, fetch, or Python?

The endpoint is language-agnostic — POST to https://splitforms.com/api/submit with your access_key and fields. From curl use -F flags; from JavaScript send new FormData(form) with fetch; from Python post a JSON body with requests. Every client gets JSON back: { success, message } on a 200, or { success: false, message, code } on a 4xx validation, auth or rate-limit error.

How do I submit with fetch and show a success message without reloading?

POST new FormData(form) with an Accept: application/json header, read the JSON { success, message }, and show your own message. Handle HTTP 429 with code rate_limited by asking the visitor to try again in a minute; for other failures, show data.message.

What are the API's rate limits?

Submissions are limited to 6 per minute and 20 per 15 minutes per IP, per form. Go over either and you get HTTP 429 with code rate_limited. The read API (GET /api/submissions) is limited to 60 requests per minute per API token. Real visitors almost never hit the submission limit — it mostly catches bots.

How do I read submissions from another app or service?

Call GET https://splitforms.com/api/submissions with Authorization: Bearer YOUR_API_TOKEN. Filter with form (a form's id or access_key), and paginate with limit (default 25, max 100), before, and since. The response is { submissions, has_more }. Use it to sync leads into your own database, a CRM, or a script — the same token also powers the MCP server.

Can the contact form API send an auto-reply to the person who submitted?

Yes, on paid plans (Pro at $5/month and Annual) you can turn on an auto-responder per form. Owner notification emails are free on every plan.

Does the contact form API accept file attachments?

Yes, on paid plans with Storage connected: send multipart/form-data with up to 5 files of 5 MB each per submission. On the Free plan, text fields are saved and files are dropped; the JSON response includes a note when that happens.

Do webhooks retry if my endpoint is down?

Yes, once. Each webhook is an HMAC-SHA256-signed POST (the signature header is prefixed sha256=) with an 8-second timeout; a delivery that fails with a network error, timeout, or 5xx is retried once automatically after about 2 seconds. A 4xx response is not retried — the endpoint understood the request and refused it. Webhooks are on paid plans (Pro at $5/month and Annual); native Slack, Discord, Telegram, WhatsApp, and Mailchimp destinations are also available if you'd rather not run your own endpoint.

What's the MCP server for?

MCP (Model Context Protocol) is how AI agents like Claude, Cursor, and Windsurf call external tools directly. splitforms' MCP server is free on every plan and ships 12 tools, including create_form, generate_form, and list_submissions — so an agent can build a form and read its submissions with the same API token as the REST API, without a bespoke integration.

Can I connect a form without writing backend code?

Yes — that's the point of the API. Build the form in the drag-and-drop dashboard (50+ templates) and grab a ready-made embed for WordPress (shortcode), Shopify (Custom Liquid), iframe, plain HTML, or a React component; or wire up your own markup by pointing action at https://splitforms.com/api/submit with the access_key field. Either way there's no server to run — accepted submissions are saved to your dashboard before owner notification email is attempted; delivery time and inbox placement depend on provider and recipient settings.

Contact form API vs EmailJS vs SMTP: which should I use?

EmailJS sends an email from the browser through an email service you connect, such as Gmail; it is built for sending messages, not for storing and filtering submissions. Raw SMTP needs your own server plus SPF and DKIM setup. A contact form API receives, filters, stores and forwards submissions for you. See our EmailJS alternative guide.