Submissions API
Read the responses your forms collect. Submissions are created by respondents through the hosted form or an embed — the API is read-only for them, so your data stays exactly as it was submitted.
All endpoints require a bearer token. You can only read submissions of forms in the token's workspace.
The submission object
{
"id": 9812,
"form_id": 42,
"answers": {
"Name": "Jane Smith",
"Email": "[email protected]",
"Topic": "Sales",
"Interests": ["Product news", "Webinars"],
"Message": "I'd like a demo.",
"Attachment": {
"path": "submissions/website-contact-form-k3j9xa/brief.pdf",
"name": "brief.pdf",
"size": 48211,
"mime": "application/pdf"
},
"source": "website"
},
"metadata": {
"ip": "203.0.113.7",
"user_agent": "Mozilla/5.0 …",
"referrer": "https://example.com/pricing",
"source_url": "https://example.com"
},
"read_at": null,
"created_at": "2026-09-04T09:41:12+00:00"
}
| Field | Type | Description |
|---|---|---|
id |
integer | Submission ID. |
form_id |
integer | The form it belongs to. |
answers |
object | One key per answered field, keyed by the field's label at the time of submission. Fields hidden by conditional logic are omitted. |
answers.<label> |
string | Text, email, phone, number, dropdown, radio, date (YYYY-MM-DD) and hidden fields. |
answers.<label> |
array of strings | checkbox fields — the selected options. |
answers.<label> |
object | file fields — path (storage path), name (original filename), size (bytes) and mime. Download files from the submissions page in your dashboard. |
metadata.ip |
string | Respondent's IP address. |
metadata.user_agent |
string | Respondent's browser user agent. |
metadata.referrer |
string or null | The page the respondent came from (Referer header). |
metadata.source_url |
string or null | Origin of the site the form was embedded on. |
read_at |
ISO 8601 datetime or null | When the submission was marked read in the dashboard. null means unread. |
created_at |
ISO 8601 datetime | When it was submitted, in UTC. |
List submissions
GET /forms/{form}/submissions
| Query parameter | Type | Description |
|---|---|---|
per_page |
integer 1–100 | Submissions per page. Default 25. |
cursor |
string | The meta.next_cursor value from the previous page. Omit for the first page. |
unread |
1 |
Only submissions that have not been marked read. |
since |
ISO 8601 date or datetime | Only submissions created at or after this moment, e.g. 2026-09-01 or 2026-09-01T00:00:00Z. Handy for incremental syncs — store the newest created_at you have seen and pass it next time. |
Submissions are ordered newest first and paginated with a cursor, so paging is stable even while new submissions arrive.
curl "https://rapidform.com/api/v1/forms/42/submissions?per_page=50&since=2026-09-01" \
-H "Authorization: Bearer $RAPIDFORM_API_KEY"
{
"data": [ { "id": 9812, "form_id": 42, "answers": { "…": "…" }, "…": "…" } ],
"links": { "first": null, "last": null, "prev": null, "next": "…?per_page=50&cursor=eyJpZCI6OTc2MywiX3BvaW50c1RvTmV4dEl0ZW1zIjp0cnVlfQ" },
"meta": {
"path": "https://rapidform.com/api/v1/forms/42/submissions",
"per_page": 50,
"next_cursor": "eyJpZCI6OTc2MywiX3BvaW50c1RvTmV4dEl0ZW1zIjp0cnVlfQ",
"prev_cursor": null
}
}
To fetch everything, keep requesting with cursor set to meta.next_cursor until it is null:
CURSOR=""
while :; do
PAGE=$(curl -s "https://rapidform.com/api/v1/forms/42/submissions?per_page=100&cursor=$CURSOR" \
-H "Authorization: Bearer $RAPIDFORM_API_KEY")
echo "$PAGE" | jq -c '.data[]'
CURSOR=$(echo "$PAGE" | jq -r '.meta.next_cursor // empty')
[ -z "$CURSOR" ] && break
done
Responds 404 if the form is not in this key's workspace.
Get a submission
GET /submissions/{submission}
Returns {"data": { …submission object… }}. Responds 404 if the submission belongs to a form outside this key's workspace.
curl https://rapidform.com/api/v1/submissions/9812 \
-H "Authorization: Bearer $RAPIDFORM_API_KEY"
Receiving submissions in real time
Polling with since works well for syncs, but if you want each submission pushed to you the moment it arrives, add a webhook to the form.