Skip to content

Responses

Responses are the submissions to a form. They’re nested under a form and require the responses:read scope.

GET /v1/forms/{formId}/responses
Query paramDescription
cursorPagination cursor from a previous nextCursor.
limitPage size, default 25, max 100.
sinceOnly responses started at/after this ISO-8601 UTC timestamp.
untilOnly responses started at/before this timestamp.
statusall (default), complete, or partial.
Terminal window
curl "https://publicapi.shout.com/v1/forms/90751669-9ebc-4215-9321-538b6c9e29ac/responses?status=complete&limit=1" \
-H "Authorization: Bearer $SHOUT_API_KEY"
{
"items": [
{
"id": "B3BfRaEZijiL_I15DzKuxpuLz33wnLSMZ9-lBi59snI",
"completed": true,
"startedAt": "2026-07-11T11:10:00Z",
"timeTakenSeconds": 42,
"countryCode": "US",
"contactId": null,
"answers": [
{
"questionId": 7441598,
"values": [
{ "optionId": "d820082d-d403-407e-b9ea-821e61f9ec13",
"label": "Dashboard", "text": null, "number": 1, "date": null }
]
},
{
"questionId": 7441603,
"values": [
{ "optionId": null, "label": null,
"text": "More integrations please", "number": null, "date": null }
]
}
]
}
],
"nextCursor": "djE6OTIxOTA1"
}

To poll for new responses, save the newest startedAt you’ve seen and pass it as since on your next run — don’t reuse the cursor for that (cursors walk a fixed snapshot, since fetches what’s new).

GET /v1/forms/{formId}/responses/{responseId}
Terminal window
curl "https://publicapi.shout.com/v1/forms/90751669-9ebc-4215-9321-538b6c9e29ac/responses/B3BfRaEZijiL_I15DzKuxpuLz33wnLSMZ9-lBi59snI" \
-H "Authorization: Bearer $SHOUT_API_KEY"

Returns a single response in the same shape as a list item. A response ID only resolves under the form it belongs to — fetching it under a different form returns 404 not_found.

FieldTypeNotes
idstringOpaque response ID — safe to store, but don’t parse it. Not a UUID.
completedbooleantrue if the respondent finished the form.
startedAtstringISO-8601 UTC time the response began.
timeTakenSecondsinteger | nullSeconds spent; null while a response is still partial.
countryCodestring | nullTwo-letter country code, if known.
contactIdUUID | nullThe linked CRM contact, if the respondent is identified.
answersarrayOne entry per answered question (see below).

Each entry in answers is one question, keyed by its integer questionId (matching the question id from GET /v1/forms/{id}):

{ "questionId": 7441598, "values": [ /* one or more values */ ] }

values holds one entry per selected option / sub-field. A value is a union — only the fields relevant to the answer are populated, the rest are null:

FieldPopulated forExample
optionIdChoice selections"d820082d-..." — the chosen option’s UUID
labelChoice / scale answers"Dashboard", or a scale point like "8"
textFree-text answers"More integrations please"
numberNumeric / scored answers4 (also carries the numeric value behind a scale point)
dateDate answers"2026-07-01"

Because it’s a union, read defensively: a single-choice answer has optionId + label (+ a number if the option is scored); a text answer has only text; an unanswered question is simply absent from answers (empty answers are omitted, not returned as blank values).