How we tested
A form backend is the service your form posts to when your site has no server of its own. It takes the POST, keeps the message and sends it to you. Most free plans look alike on a pricing page, so we looked at what you only find out once something goes wrong.
On 16 October 2026 we sent each service the same four kinds of request, with keys we made up, so nothing was stored or delivered. First the placeholder from the service's own docs, the one people forget to replace. Then a random key of the right shape. Then the same request from a server instead of a browser. Then a plain HTML form with no JavaScript, to see what a visitor's browser ends up showing. The rules for passing were written down before the first request. FormKeep answered every request with "Rate Limit Exceeded", in two passes an hour apart. We cannot tell from outside whether that is its answer to an unknown form or a block on our address, so its row is not scored.
The same day we read each service's pricing, docs and privacy pages and quoted them. Where a page said nothing, or two of its pages disagreed, the table says so. We left prices out on purpose: they change more often than anything else here, and each service shows its own.
What each service answered to a wrong key
| Service | Placeholder from its own docs | Random key | Same request from a server | Plain HTML form, no JavaScript |
|---|---|---|---|---|
| Formgong | 404, unknown_access_key | 404, unknown_access_key | 404, about the key | Error page: key does not exist |
| Formspree | 404, Form not found | 404, Form not found | 404, about the form | Error page: form not found |
| Web3Forms | 200, success: true ("It works") | 400, invalid key | 403, server calls need a paid plan | Error page: invalid key |
| Forminit | 200, "form has been deleted" | 200, "form has been deleted" | 200, about the form | Error page: form deleted |
| Basin | 400, endpoint does not exist | 400, endpoint does not exist | 400, about the form | Error page: endpoint locked |
| FormSubmit | No placeholder (its docs use a real email domain) | 200, success: "false" as a string | 200, "open this page through a web server" | Error page: address not valid |
| Formcarry | 500 (body says 401), form invalid | 500 (body says 401), form invalid | 500, about the form | Error page: form invalid |
| Formspark | 200, HTML: replace the placeholder | 404, form does not exist | 404, about the form | Error page: form does not exist |
| FormBackend | 404, HTML: form not found | 404, HTML: form not found | 404, about the form | Error page: form not found |
| Static Forms | 401, Invalid API key | 401, Invalid API key | 401, about the key | Error text: Invalid API key |
| Splitforms | 404, invalid_access_key | 404, invalid_access_key | 404, about the key | Error page: invalid key |
| Jotform | No placeholder (no docs page names the endpoint) | Unreadable from JavaScript (no CORS headers) | 404, form couldn't be found | Error page: form couldn't be found |
Every service showed a visitor some kind of error page when a plain HTML form used a wrong key. The differences are in what JavaScript gets back, and that matters because most forms built today send with fetch. In our study of AI-built sites, 27 of 105 forms told the visitor "thank you" while nothing was delivered, and a 200 answer to a broken setup is one way that happens.
- Web3Forms answers its own docs placeholder with
success: trueand "It works". A form shipped with the placeholder still in it looks fine in testing. - Forminit answers a form ID that never existed with HTTP 200 and "This form has been deleted". The body says
success: false, so code that reads the body is safe and code that checksresponse.okis not. - FormSubmit returns
"success":"false"as a string with HTTP 200. In JavaScript the string "false" is truthy, soif (data.success)passes. - Formspark answers its placeholder with a 200 HTML page that explains the mistake. It answers a random ID with a clean 404.
- Jotform's submit address sends no CORS headers, so a script on your own site cannot read its answer at all. It is built for Jotform's generated forms, not for your own code.
We ran the same tests on seven of these services a week earlier, on 9 October 2026. All seven answered the same way both times.
Free form backends: what the free plan includes
| Service | Free plan | A submission over the free limit |
|---|---|---|
| Formgong | 1,000 a month, unlimited forms, email for every submission, Telegram and Google Sheets | 20% extra is delivered; after that submissions are stored and wait for the next month or an upgrade, up to one more month's worth; then the form answers 429 |
| Formspree | 50 a month, unlimited forms, 30-day archive, no file uploads | Saved to an Over Limit folder with no notifications; you reprocess them once you have room |
| Web3Forms | 250 a month, unlimited forms, no integrations or file uploads | Not stated in the docs we could read (its pricing pages refused our reader) |
| Forminit | 100 a month, 1 form, file uploads with 100 MB | The form pauses; submissions are rejected and cannot be recovered |
| Basin | 50 a month, 1 form, 30-day retention, file uploads | Basin's own pages disagree: "may be restricted" in the FAQ, "we won't cut you off" on pricing |
| FormSubmit | No account; "unlimited" submissions, 30-day retention | No limit published |
| Formcarry | 50 a month, 1 form; spam counts toward the 50 | Held until next month (up to 1,900) while the visitor sees the normal thank-you page |
| Formspark | 250 in total, not per month; 10 forms; webhooks, Slack and Zapier | A small buffer is held in Overflow, then new submissions are discarded |
| FormKeep | 50 a month per form, unlimited forms, email once a day | Ignored and lost; spam counts toward the limit |
| FormBackend | 50 a month, 1 form | "Not dropped without warning"; held or rejected is not stated |
| Static Forms | 250 a month; more than one form needs a paid plan | Rejected with an error; warning emails at 80% and 90% |
| Splitforms | 500 in total, not per month; unlimited forms | Rejected with HTTP 429 and not stored |
| Netlify Forms | Unlimited on Netlify's credit plans, only for sites Netlify builds | If the project goes over the plan, the whole project pauses |
| Jotform | 100 a month, 5 forms, 10,000 form views (a form builder, not a backend) | Forms are disabled and show "This Form Has Exceeded Its Allocated Quota" |
Two things stand out. Formspark and Splitforms give a fixed number of free submissions that does not come back each month, which suits a form that gets a few messages a year and does not suit a busy one. And the services disagree most about the message that arrives after the limit. Formspree and Formgong keep it for you. Forminit, Static Forms, Splitforms and FormKeep turn it away. Formcarry keeps it but shows the visitor the normal thank-you page, so nobody knows it is waiting.
Headless form backend: what the term means
"Headless" means the backend has no form of its own for you to embed. You write the form in your own HTML, React, Astro or whatever the site uses, and the backend only receives what it sends. Every service on this page works that way except Jotform and Netlify Forms. Jotform is a form builder: you design the form in Jotform and embed it. Netlify Forms reads your HTML when Netlify builds the site, so it only works on sites Netlify hosts.
If you are choosing one for a JavaScript front end or a server, these are the lines in the docs that decide it:
- JSON in and out. Formgong, Web3Forms, Formcarry, Formspark, Forminit and Splitforms document a JSON request and a JSON answer. Netlify Forms says it "does not support JSON form data at this time". FormBackend's docs show FormData, not a JSON body.
- Posting from a server. Web3Forms needs a paid plan and an allow-listed server IP, and our curl request got 403. Formcarry's docs say to post from the browser, not from a server. FormKeep's docs warn that requests with a generic user agent are often blocked. Forminit has a separate protected mode with an API key for servers, and Splitforms' docs say a server can post like any other client. In our test 10 of the 12 answered a server request about the key, as they did in the browser; FormSubmit answered with a note about web servers.
- Webhooks on the free plan. Formspark and Formgong both include a signed webhook on the free plan. Formspree, Web3Forms, Forminit, Basin and Splitforms keep webhooks for paid plans.
Form endpoint service and contact form API: check the answer, not the status
A "form endpoint" or "contact form API" is the same thing seen from code: one URL that takes a POST and tells you whether the message was accepted. The table above is the case for checking both the status and the body. A 200 on its own proved nothing for four of the twelve services that answered.
This is the check we use in Formgong's own snippets. Formgong answers success: true only when the message was stored, and an error status with a code and a readable message otherwise. If you use another service, swap in the success field its docs name.
const res = await fetch("https://formgong.com/submit", {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form),
});
let data = null;
try { data = await res.json(); } catch { /* an HTML or text answer is not a success */ }
// Both checks. A 200 alone is not proof: some backends answer 200 to a broken key.
if (res.ok && data?.success === true) {
showThanks();
} else {
showError(data?.message ?? "The message was not sent. Please email us instead.");
}Point it at a form with a wrong key once before you ship. If the visitor would see "thank you", the check is not doing its job. Our free form checker does the same test on a live page.
Where each service keeps your submissions
- In the EU by default: Formgong (Cloudflare D1 with EU jurisdiction), Forminit (AWS in Ireland), Formcarry (Frankfurt), Formspark (Ireland and Germany).
- In the EU if you choose it: Static Forms (Frankfurt for accounts that pick EU residency, US otherwise) and Jotform (Frankfurt after you move the account).
- In the US: Formspree, Web3Forms (US East), FormBackend, Splitforms (EU residency is on its roadmap).
- Split: Basin lists its database in Canada, files on AWS in the US and backups in Germany.
- Not stated on their pages: FormSubmit, FormKeep and Netlify Forms.
Storage location is the service's half of the GDPR. Your half is a notice at the form, a lawful basis and a deletion period, and the GDPR contact form template has wording for each.
Which one to pick
- Your site is on Netlify and email is enough: Netlify Forms. Nothing else to sign up for.
- You want no account at all: FormSubmit, as long as your code handles its string
"false". - A form that gets a few messages a year: Formspark's one-off free allowance, with signed webhooks included.
- Files on the free plan: Forminit or Basin, both with 100 MB of storage. Formgong accepts files on the free plan too, up to 5 MiB each, once you verify your email and add a Turnstile key.
- Many integrations and a long track record: Formspree.
- Sending from your own server with an API key: Forminit's protected mode, or Splitforms.
- EU storage, every message by email at once, Telegram on the free plan, and the rest of the site in one place: Formgong. The same account gives you free website templates with the form already wired in, and an AI help chat that answers visitors from your own pages, with every message landing in one inbox.
What this test does not show
Made-up keys tell you how a service fails, not how it delivers. We did not measure how fast an email arrives, whether it lands in spam, or what really happens at the monthly limit; that needs a real form on each service, and we will add it to this page when we have run it. Docs change too: everything here was read on 16 October 2026, and each service's own page is the final word.
Questions
What is the best form backend?
It depends on the site. Netlify Forms for a site on Netlify, FormSubmit for no account, Formspree for the most integrations, Formgong for EU storage with Telegram and instant email on the free plan. In our 16 October 2026 test, Web3Forms, Forminit, FormSubmit and Formspark answered a wrong key with HTTP 200 at least once; the other eight that answered did not.
Is there a free form backend?
Most have a free plan. Formgong takes 1,000 a month. On 16 October 2026 the others gave: Web3Forms 250, Static Forms 250, Forminit 100, Formspree, Basin, Formcarry, FormKeep and FormBackend 50. Formspark gives 250 and Splitforms 500 in total, not per month. FormSubmit publishes no limit.
What is a headless form backend?
A backend with no form builder: you write the form in your own code and post it to the service's URL. Formgong, Formspree, Web3Forms, Basin, Formcarry, Formspark, Forminit and Splitforms work this way. Jotform is a form builder, and Netlify Forms only works on sites Netlify builds.
What is a form endpoint service?
A service that gives your form one URL to post to and then stores the message and sends it on, usually by email. Point the form's action or your fetch call at that URL and the site needs no server code.
Is there a contact form API I can call from JavaScript?
Yes. Formgong, Web3Forms, Formcarry, Formspark, Forminit and Splitforms accept JSON and answer with JSON. Check both the HTTP status and the success field: in our test four services answered HTTP 200 to a wrong key at least once.
Can I post to a form backend from my server?
With most. In our test 10 of 12 services, Formgong among them, answered a curl request about the key, as they did in the browser. Forminit has a protected mode with an API key. Web3Forms needs a paid plan and an allow-listed IP. Formcarry asks you to post from the browser, and FormKeep warns that generic user agents are often blocked.
Sources
Checked on 16.10.2026 against these public pages: Formspree plans, Formspree: over-limit submissions, Formspree privacy policy, Web3Forms llms.txt, Web3Forms API reference, Web3Forms FAQ, Forminit pricing, Forminit llms.txt, Forminit security, Basin pricing, Basin plan comparison, Basin GDPR, FormSubmit documentation, Formcarry pricing, Formcarry: what every form needs, Formcarry DPA, Formspark limits and plans, Formspark webhooks, Formspark GDPR, FormKeep pricing, FormKeep API docs, FormBackend pricing, FormBackend GDPR, Static Forms pricing, Static Forms DPA, Splitforms docs, Splitforms GDPR, Netlify Forms setup, Netlify Forms usage and billing, Jotform pricing, Jotform: account usage and limits, Formgong docs, Formgong data and location.