{"openapi":"3.1.0","info":{"title":"splitforms Form Submission API","version":"1.0.0","summary":"One HTTPS endpoint that receives HTML, fetch and server-side form submissions.","description":"POST any form to `/api/submit` with the form's `access_key`. splitforms validates the request, filters spam with rule-based checks, stores the submission and emails the form owner.\n\nPlans: Free is 500 submissions in total (one-time, no card). Pro is $5/month and Annual is $49/year. Paid plans add signed webhooks, integrations, exports and file uploads.\n\nRate limit: 6 submissions per minute and 20 per 15 minutes per IP, per form.\n\nFacts checked against the code on 2026-10-02.","termsOfService":"https://splitforms.com/terms","contact":{"name":"splitforms support","url":"https://splitforms.com/api-reference","email":"hello@splitforms.com"}},"externalDocs":{"description":"API reference with cURL, fetch, Python, PHP and Go examples","url":"https://splitforms.com/api-reference"},"servers":[{"url":"https://splitforms.com","description":"Production"}],"tags":[{"name":"Submissions","description":"Send form submissions to a splitforms form."}],"paths":{"/api/submit":{"post":{"operationId":"submitForm","summary":"Submit a form","description":"Send the form's `access_key` plus any fields. No Authorization header: the access key identifies the form and is public by design.\n\nThe response format follows the request. Requests with `Accept: application/json` or a JSON body, fetch requests, and clients that send no Accept header get JSON. A native HTML form post gets a hosted page, or a redirect when one is configured.\n\nFiles need multipart/form-data: up to 5 files of 5 MB each, stored on paid plans with Storage connected. On Free, text fields are saved, files are dropped and the JSON response carries a `note`.","tags":["Submissions"],"security":[],"requestBody":{"required":true,"description":"Accepted content types: application/x-www-form-urlencoded, multipart/form-data, application/json.","content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/SubmissionFields"}},"multipart/form-data":{"schema":{"$ref":"#/components/schemas/MultipartSubmission"}},"application/json":{"schema":{"$ref":"#/components/schemas/SubmissionFields"},"example":{"access_key":"YOUR_ACCESS_KEY","name":"Ada Lovelace","email":"ada@example.com","message":"Hello","botcheck":""}}}},"responses":{"200":{"description":"Submission accepted. JSON callers get `{ success: true, message }` plus `redirect`, `redirect_params`, `payment_url`, `note` or `files` when they apply. Native HTML posts with no redirect get a hosted thank-you page.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuccessResponse"},"example":{"success":true,"message":"Submission received"}},"text/html":{"schema":{"type":"string","description":"Hosted thank-you page."}}}},"302":{"description":"Native HTML post on a form with a redirect URL set in the dashboard. Works on every plan.","headers":{"Location":{"description":"Where the browser is sent.","schema":{"type":"string"}}}},"303":{"description":"Native HTML post with a valid hidden `redirect` field, or a payment form sending the visitor to Stripe Checkout.","headers":{"Location":{"description":"Where the browser is sent.","schema":{"type":"string"}}}},"400":{"description":"The request could not be read. `invalid_body`: The body could not be parsed (empty or malformed JSON, or an unreadable form body). `missing_access_key`: No access_key field was sent.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"message":"…","code":"invalid_body"}},"text/html":{"schema":{"type":"string","description":"Hosted error page, sent to native HTML form posts."}}}},"403":{"description":"The form refused the submission. `inactive_form`: The form owner turned the form off. `origin_not_allowed`: Strict origin protection is on and the request came from a domain that is not on the form's allowed list. `form_unavailable`: The form cannot accept submissions.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"message":"…","code":"inactive_form"}},"text/html":{"schema":{"type":"string","description":"Hosted error page, sent to native HTML form posts."}}}},"404":{"description":"Unknown form. `invalid_access_key`: No form has this access_key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"message":"…","code":"invalid_access_key"}},"text/html":{"schema":{"type":"string","description":"Hosted error page, sent to native HTML form posts."}}}},"413":{"description":"Request body too large. `payload_too_large`: The body is larger than 5 files of 5 MB plus form fields.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"message":"…","code":"payload_too_large"}},"text/html":{"schema":{"type":"string","description":"Hosted error page, sent to native HTML form posts."}}}},"415":{"description":"Unsupported Content-Type. `unsupported_content_type`: The Content-Type is missing or not URL-encoded, multipart or JSON.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"message":"…","code":"unsupported_content_type"}},"text/html":{"schema":{"type":"string","description":"Hosted error page, sent to native HTML form posts."}}}},"429":{"description":"Too many requests or no allowance left. There is no Retry-After header; wait about a minute before retrying a `rate_limited` response. `rate_limited`: More than 6 submissions a minute or 20 per 15 minutes from one IP to one form. `quota_reached`: The account's allowance is used: 500 submissions in total on Free, or the monthly cap on a paid plan.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"message":"…","code":"rate_limited"}},"text/html":{"schema":{"type":"string","description":"Hosted error page, sent to native HTML form posts."}}}},"500":{"description":"Server error. Some 500 responses carry no `code`. `profile_lookup_failed`: The form's account settings could not be read. Retry the request.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"},"example":{"success":false,"message":"…","code":"profile_lookup_failed"}},"text/html":{"schema":{"type":"string","description":"Hosted error page, sent to native HTML form posts."}}}}}},"options":{"operationId":"submitFormPreflight","summary":"CORS preflight","description":"Answers browser preflight requests so the endpoint can be called with fetch from any origin.","tags":["Submissions"],"security":[],"responses":{"204":{"description":"CORS headers, no body."}}}}},"components":{"schemas":{"SubmissionFields":{"type":"object","required":["access_key"],"properties":{"access_key":{"type":"string","description":"The form's public identifier, copied from the dashboard. Safe to embed in client-side HTML; it is not a secret."},"subject":{"type":"string","description":"Subject line of the owner notification email."},"replyto":{"type":"string","format":"email","description":"Reply-to address on the notification email. Without it, the `email` field is used."},"from_name":{"type":"string","description":"Sender name shown on the notification email."},"redirect":{"type":"string","format":"uri-reference","description":"Thank-you page. Native HTML posts get a 303 to it; JSON callers get it back as `redirect`. Accepted values: a relative path, a domain on the form's allowed list, or splitforms.com (the submitting site's own origin only when no allowed list is set). Anything else is ignored."},"botcheck":{"type":"string","maxLength":0,"description":"Honeypot. Leave it empty. A filled value gets a 200 that looks like success, but nothing is stored or sent."},"form_loaded_at":{"type":["integer","string"],"description":"Page-load time in milliseconds since the Unix epoch (time-trap). When present, posts sent less than 2 seconds or more than 24 hours after load are dropped."},"g-recaptcha-response":{"type":"string","description":"reCAPTCHA token, verified when the form has a reCAPTCHA secret. An invalid token, or a missing one when the owner requires it, gets a 200 that looks like success but nothing is stored."},"cf-turnstile-response":{"type":"string","description":"Cloudflare Turnstile token, verified when the form has a Turnstile secret. Same handling as g-recaptcha-response."}},"additionalProperties":{"description":"Any other field (name, email, message, …) is stored and emailed as submission data. Up to 100 fields; values longer than 10,000 bytes are truncated."}},"MultipartSubmission":{"allOf":[{"$ref":"#/components/schemas/SubmissionFields"},{"type":"object","description":"File parts may use any field name. Up to 5 files of 5 MB each per submission.","additionalProperties":{"type":"string","contentMediaType":"application/octet-stream"}}]},"SuccessResponse":{"type":"object","required":["success"],"properties":{"success":{"const":true},"message":{"type":"string","examples":["Submission received"]},"redirect":{"type":"string","format":"uri-reference","description":"The validated hidden `redirect` field, for JSON callers to navigate to."},"redirect_params":{"type":"object","additionalProperties":{"type":"string"},"description":"Submitted values the owner chose to forward to the thank-you page (paid plans)."},"payment_url":{"type":"string","format":"uri","description":"Stripe Checkout URL on payment forms. Send the visitor there to pay."},"note":{"type":"string","description":"Present when uploaded files were dropped (Free plan, Storage not connected, or storage full)."},"files":{"$ref":"#/components/schemas/FilesInfo"}}},"FilesInfo":{"type":"object","required":["received","stored"],"properties":{"received":{"type":"integer","minimum":0,"description":"File parts in the request."},"stored":{"type":"integer","minimum":0,"description":"Files saved."},"hint":{"type":"string","description":"Why files were not received, for example a filename sent as plain text."}}},"ErrorResponse":{"type":"object","required":["success","message"],"properties":{"success":{"const":false},"message":{"type":"string","description":"Human-readable reason, safe to show to the visitor."},"code":{"type":"string","enum":["invalid_body","missing_access_key","inactive_form","origin_not_allowed","form_unavailable","invalid_access_key","payload_too_large","unsupported_content_type","rate_limited","quota_reached","profile_lookup_failed"],"description":"Machine-readable error code."},"quota":{"$ref":"#/components/schemas/QuotaInfo"}}},"QuotaInfo":{"type":"object","description":"Sent with `quota_reached`.","required":["limit","used","plan"],"properties":{"limit":{"type":"integer","minimum":0},"used":{"type":"integer","minimum":0},"plan":{"type":"string"}}}}}}