API overview
Create forms, change their settings and work with submissions from your own code with the Nisuform REST API and an API key.
The Nisuform API lets your code do what you do on a form's pages in the dashboard: create and configure forms, page through submissions, mark them read, starred or spam, delete them, download their files and export them as CSV.
You don't need the API to collect submissions. Forms post to the submit endpoint, which is public and needs no key. Use the API when a script, a server or an automation needs to manage forms or read what came in.
https://api.nisuform.com/v1Quick start
Create an API key
In the dashboard, open Account from the menu under your picture, go to API keys and choose Create key. Copy the key. It starts with nfa_ and is only shown once. See API keys.
Check that it works
curl https://api.nisuform.com/v1/me \
-H "Authorization: Bearer $NISUFORM_API_KEY"The response shows your account and the team the key works in.
List your forms and their submissions
curl https://api.nisuform.com/v1/forms \
-H "Authorization: Bearer $NISUFORM_API_KEY"
curl "https://api.nisuform.com/v1/forms/FORM_ID/submissions?filter=unread" \
-H "Authorization: Bearer $NISUFORM_API_KEY"Authentication
Send the key in the Authorization header of every request:
Authorization: Bearer nfa_...A missing, unknown or revoked key gets 401. API keys are secrets: call the API from a server, a script or an automation tool, never from code that runs in a visitor's browser. The API doesn't accept browser requests from other websites.
Teams
Every key belongs to one team, picked when you create it. Everything the key does happens in that team:
GET /v1/formslists that team's forms, and new forms are created there.- Forms in your other teams answer
404, as if they didn't exist. - Plan limits and Pro features follow that team's plan.
Switching teams in the dashboard doesn't change which team a key works in.
Endpoints
API keys can use these endpoints:
| Method | Path | Does |
|---|---|---|
GET | /v1/me | Get your account and the key's team |
GET | /v1/forms | List forms |
POST | /v1/forms | Create a form |
GET | /v1/forms/{id} | Get a form |
PATCH | /v1/forms/{id} | Update a form |
DELETE | /v1/forms/{id} | Delete a form and its submissions |
POST | /v1/forms/{id}/duplicate | Copy a form |
POST | /v1/forms/{id}/rotate-key | Give a form a new key |
POST | /v1/forms/{id}/test-submissions | Send a test submission |
GET | /v1/forms/{formId}/submissions | List submissions |
GET | /v1/forms/{formId}/submissions/{id} | Get a submission |
PATCH | /v1/forms/{formId}/submissions | Mark submissions read, starred or spam |
DELETE | /v1/forms/{formId}/submissions | Delete submissions |
GET | /v1/forms/{formId}/submissions/{id}/files/{index} | Get a download link for an uploaded file |
GET | /v1/forms/{formId}/export.csv | Export submissions as CSV |
Everything else stays in the dashboard: teams and members, billing, integrations, analytics, your profile and API keys themselves. API keys get 403 there.
Requests and responses
- Send request bodies as JSON with
Content-Type: application/json.GETrequests take query parameters. - Responses are JSON, except the CSV export.
- IDs are UUIDs. Times are ISO 8601 strings in UTC, like
2026-09-29T10:00:00.000Z. - Every response has an
x-request-idheader. Include it when you contact support about a request. - New fields can be added to responses at any time, so ignore fields your code doesn't know.
Errors
Errors answer with a 4xx or 5xx status and a JSON body:
{
"defined": true,
"code": "NOT_FOUND",
"status": 404,
"message": "Form not found"
}Branch on code. message is written for people and can change.
| Status | Code | When |
|---|---|---|
400 | BAD_REQUEST | The input is invalid. data.issues lists each problem with its path and message. |
401 | UNAUTHORIZED | The key is missing, unknown or revoked. |
402 | PAYMENT_REQUIRED | The change needs the Pro plan, or the team reached its form limit. |
403 | FORBIDDEN | API keys can't use this endpoint. |
404 | NOT_FOUND | The form or submission doesn't exist, or it is in another team. |
429 | TOO_MANY_REQUESTS | The key sent too many requests. Wait for the seconds in Retry-After. |
500 | INTERNAL_SERVER_ERROR | Something went wrong on our side. Retry later. |
A validation error looks like this:
{
"defined": true,
"code": "BAD_REQUEST",
"status": 400,
"message": "Input validation failed",
"data": {
"issues": [{ "path": ["name"], "message": "Too small: expected string to have >=1 characters" }]
}
}Rate limits
Each key can send 120 requests a minute. Past that, requests get 429 with a Retry-After header until the minute is over. To pull many submissions, ask for up to 100 per page or use the CSV export.
Rate limits on the submit endpoint are separate. See Limits.
Get new submissions as they arrive
The API is the right tool for reading and managing submissions on demand. To react to each new submission, use a webhook on Pro instead of asking the API every few seconds.