Get your free splitforms access key
Sign up at splitforms.com, verify your email, and copy your access key from the dashboard. No credit card required.
Contact form · jQuery
If your site already loads jQuery, you're three method options away from a working contact form. Serialize the form with FormData, POST via $.ajax, and read the JSON response in .done()/.fail(). No new dependencies, no server route, no fetch polyfill for legacy browsers.

No server, API route, or SDK. Your jQuery form posts straight to one endpoint.
Every submission is emailed to you and saved to a searchable dashboard — spam filtered before it reaches you.
It's your own jQuery markup and styles. Splitforms is only the backend, so nothing constrains how the form looks.
Copy-paste ready
Replace YOUR_ACCESS_KEY with the key from your dashboard — that's the whole integration. No SDK to install, no build step, just the html you already write.
<!-- Include jQuery once per page (skip if your site already loads it) -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<form id="contact" autocomplete="off">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY" />
<input type="text" name="name" placeholder="Name" required />
<input type="email" name="email" placeholder="Email" required />
<textarea name="message" placeholder="Message" required></textarea>
<!-- Honeypot — invisible to humans -->
<input type="checkbox" name="botcheck" style="display:none" tabindex="-1" />
<button type="submit">Send</button>
<p id="status"></p>
</form>
<script>
$(function () {
$("#contact").on("submit", function (e) {
e.preventDefault();
var $form = $(this);
var $status = $("#status");
var $btn = $form.find('button[type="submit"]');
$btn.prop("disabled", true).text("Sending…");
$status.text("");
$.ajax({
url: "https://splitforms.com/api/submit",
method: "POST",
// Hand jQuery a real FormData so file fields (and multipart) work:
data: new FormData(this),
processData: false, // do NOT turn FormData into a url-encoded string
contentType: false, // let the browser set multipart/form-data; boundary=…
dataType: "json",
})
.done(function (data) {
if (data.success) {
$status.text("Thanks — we'll be in touch.");
$form[0].reset();
} else {
$status.text(data.message || "Something went wrong.");
}
})
.fail(function () {
$status.text("Network error — please try again.");
})
.always(function () {
$btn.prop("disabled", false).text("Send");
});
});
});
</script>How to add it
To add a contact form to a jQuery website you need three things: a free splitforms access key, the html snippet above, and your key pasted into it. No backend, server, or SDK — the form posts to one URL and every submission lands in your inbox and dashboard.
Sign up at splitforms.com, verify your email, and copy your access key from the dashboard. No credit card required.
Copy the jQuery code example into your project and replace YOUR_ACCESS_KEY with the key from step 1.
Submissions arrive in the splitforms dashboard within seconds. Free includes inbox delivery; Pro adds Slack, Discord, Sheets, or any signed webhook URL.
Where submissions go
Every submission is emailed to you and saved to a searchable dashboard — spam filtered before it ever reaches you. Search, export to CSV, or forward it to a webhook or Slack on Pro.

No backend needed
Your jQuery form posts standard FormData to one URL. Splitforms validates the access key, runs the spam classifier, and forwards it to your email — so there's no server, API route, or database for you to build or maintain.

Best practices
The difference between a form that works in the demo and one that survives launch traffic — the production-tested defaults, in priority order.

How SplitForms works
Connect your form, collect every submission, and send data where it needs to go — without building backend infrastructure.

Point your form to your unique SplitForms endpoint. That's it.

We instantly capture and organize every submission in your inbox.

Send data to email, spreadsheets, CRMs, webhooks, and 7,000+ apps.

No credit card required. Set up in under 60 seconds.
Connect & automate
SplitForms works with the destinations you route to and the platforms you build on — from Slack and Sheets to WordPress, Shopify, and Next.js.

Trusted by indie teams and agencies shipping forms worldwide
Testimonials
40 quotes on record — from indie hacks to agency migrations.
“I replaced a Lambda + DynamoDB + SES contact form with six lines of HTML. It took eleven minutes, and the dashboard is better than what I was going to build.”
“We migrated 14 client sites off Formspree in a single weekend. The price is a third of what we paid, the API is more honest, and the spam filter actually works.”
“The webhook payload is signed, idempotent, and well-shaped. It reads like code from a competent team, not a CRUD app held together with duct tape.”
“I stopped reaching for Typeform on small marketing sites. splitforms covers 90% of the use case at none of the bloat.”
“I onboarded our whole agency in an afternoon. The MCP integration meant Cursor literally dropped the form straight into our client repos for us.”
“The free plan gave me 500 submissions before I paid a cent, and Pro is five dollars a month. I've spent more on coffee deciding which backend to use.”
“Spam went from forty junk entries a day to zero, with no reCAPTCHA puzzle ruining the form. The honeypot and time-trap just quietly do their job.”
“Point the form action at one endpoint and you're done. No SDK, no client library, no build step. This is how a form backend should feel.”
“Leads land in Slack the second someone submits, and a copy goes to Google Sheets for the sales team. I wired both up in under ten minutes.”
“I run a static Hugo site on a five-dollar VPS. splitforms gave it a real contact form without me standing up a single server.”
Questions
Build a FormData from the form (new FormData(this)), pass it to $.ajax with processData: false and contentType: false, and POST to https://splitforms.com/api/submit. The access_key hidden input carries your key; in .done(), read data.success to know whether the submission was accepted.
Yes, but only in the full-call form. $.post(url, formData) still defaults processData and contentType to true, which mangles FormData. Use $.ajax({ url, method: 'POST', data: formData, processData: false, contentType: false, dataType: 'json' }) — the shorthand can't override those options.
For greenfield projects, yes — native fetch + FormData does everything splitforms needs (see the AJAX page). jQuery earns its keep when the site already loads it: legacy codebases, Bootstrap 4 themes, older WordPress frontends. Don't add jQuery just for this form.
Two layers. .fail() catches transport and parse errors (network down, non-JSON response, 5xx). .done() runs on 2xx — always inspect data.success; if false, render data.message for the user. Re-enable the button inside .always() so it resets on both paths.
Load jquery.validate.js after jQuery, call $('#contact').validate({ rules: { … } }), and at the top of your submit handler check if (!$(this).valid()) return; before building FormData. Keep the honeypot botcheck field out of the rules object so the validator ignores it.
Yes. FormData, processData: false, and contentType: false are supported back to jQuery 1.5, when $.ajax was rewritten around jqXHR. The .done/.fail/.always promise chain also dates to 1.5. If you're stuck on jQuery 1.4 or earlier, the jqXHR promise API doesn't exist — upgrade first.
jQuery's $.ajax defaults serialize the data option into a URL-encoded string and set Content-Type: application/x-www-form-urlencoded itself. If you pass a FormData object without overriding those defaults, jQuery calls .toString() on it (yielding [object FormData]), drops every field, and splitforms sees an empty body — returning a 400 with missing access_key. Always pass processData: false (don't serialize) and contentType: false (don't auto-set the header; the browser generates the correct multipart/form-data; boundary=…). It is the single most common reason a jQuery splitforms form silently fails.
jQuery resolves or rejects based on HTTP status AND whether the body parsed as the requested dataType. With dataType: 'json', a 200 with malformed JSON rejects (.fail()), and a 4xx/5xx rejects too. But a 200 with { success: false } resolves into .done() — that's exactly how splitforms signals an invalid key, spam flag, or rate-limit. Treat .done as 'transport ok' and branch on data.success for the real outcome. .fail is for network and parse failures only.
If your site swaps page content without a full reload (Turbolinks, Hotwire Drive, pjax, or any SPA-ish router) and your init code runs again on each navigation, $('#contact').on('submit', …) binds a NEW handler every time — the next submit fires duplicate POSTs to splitforms. Either bind once with event delegation on a stable parent ($(document).on('submit', '#contact', …)) or unbind first ($('#contact').off('submit').on('submit', …)). The jQuery Validation plugin's .validate() has the same stacking bug if called repeatedly.
Simple pricing
Choose a plan that fits your workflow — from a free form endpoint to full automations, exports, and higher submission limits.
Free forever
For side projects and indie devs.
For agencies and growing products.
Pay $59. 3 years sorted.
No credit card required on Free • Cancel anytime