The Account Level API lets you build, manage, and analyse surveys from code: create and edit surveys, move them through their lifecycle (start / pause / complete), pull aggregate results, and read individual respondent sessions.
The Account Level API is available on the Standard Plan and above. Upgrade from your account settings.
| Feature | Account Level API | Embeds Integration |
|---|---|---|
| Primary Use Case | Programmatic survey management and analytics | Real-time survey deployment and interaction |
| Integration Method | RESTful API endpoints | HTML snippets or SDKs |
| Direction | Pull / push data from your code to Polling.com | Embed surveys into your application |
| Timing | Any point in the survey lifecycle | During survey deployment and completion |
| Capabilities | Manage surveys, read results, respondent data, sessions | Display surveys, send events, trigger surveys |
Use the Account Level API to drive surveys from your own systems or agents; use Embeds when you need the survey rendered inside your app.
https://api.polling.com
All requests authenticate with your account API key, sent either as:
token: <your-api-key>?token=<your-api-key>Find your key under Integrations > API Integration > API Key.

60 requests per minute per API key. Exceeding the limit returns 429 Too Many Requests.
POST /surveys, POST /surveys/{uuid}, and the three lifecycle endpoints (start, pause, complete) all return the same identity payload:
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "ACTIVE",
"share_url": "https://app.polling.com/forms/550e8400-.../share"
}
DELETE returns 204 No Content.
hash vs idEach question has a stable 10-character hash. That's the identifier returned on reads, and the one you send back on update to preserve a question and the answers already collected for it.
id is an optional write-only convenience used at create time, so you can reference a question in a later question's logic.target before hashes exist (for example id: "screener" then logic.target: "screener"). The server resolves id to the generated hash and discards the id — it is never persisted and never returned.
On update, logic.target values must be hashes (either an existing hash on the survey or the id of a new question in the same payload). Use the hashes returned by GET /surveys/{uuid} when round-tripping.
Every survey has a settings object. All fields are optional; omitted keys fall back to their defaults.
| Field | Type | Default | Options / Notes |
|---|---|---|---|
stop_criteria |
string | forever |
forever | datetime | responses. Pairs with stop_value. |
stop_value |
string | integer | null | null |
ISO 8601 datetime for datetime; response count for responses. Required when stop_criteria ≠ forever. |
start_trigger |
string | now |
now | scheduled. Pairs with start_at. |
start_at |
datetime | null | null |
ISO 8601. Required when start_trigger = scheduled. |
is_anonymous |
boolean | false |
When true, respondent identifiers are not stored. |
redirect_type |
string | none |
none | custom | results. Pairs with redirect_url. |
redirect_url |
string | null | null |
Redirect target after completion. Required when redirect_type = custom. Must start with http:// or https://. |
redirect_url_disqualified |
string | null | null |
Redirect target for disqualified respondents. Must start with http:// or https://. |
user_hash_method |
string | cookie |
Restrict Repeat Submissions setting: cookie (browser cookie) | ip (IP-based dedup) | none (allow unlimited). |
reward_amount |
number | null | null |
Reward amount offered for completion. |
reward_name |
string | null | null |
Reward description, e.g. "Amazon Gift Card". |
complete_html |
string | null | null |
Custom HTML rendered on the thank-you page. |
complete_json |
object | null | null |
Structured completion-screen config (dashboard-driven). |
screenout_enabled |
boolean | false |
Enable geographic / language screening. |
screenout_countries |
string[] | [] |
ISO 3166-1 alpha-2 country codes allowed to take the survey. |
screenout_languages |
string[] | [] |
ISO 639-1 language codes allowed to take the survey. |
List every survey in the account, newest first.
Endpoint:
GET /api/account/surveys
Query parameters:
status (optional): filter by status — DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, or DISABLED (case-insensitive).Example request:
GET /api/account/surveys?status=ACTIVE HTTP/1.1
token: YOUR_API_KEY
Example response (200):
[
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "ACTIVE",
"share_url": "https://app.polling.com/forms/550e8400-.../share",
"name": "Customer Satisfaction Survey",
"description": "Quarterly CSAT survey",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T12:00:00Z"
}
]
Example response (422) — unknown status value:
{
"error": "Unknown status 'GARBAGE'.",
"allowed": ["DRAFT", "SCHEDULED", "ACTIVE", "PAUSED", "COMPLETED", "DISABLED"]
}
Create a survey. New surveys start in DRAFT; use the lifecycle endpoints to move them to ACTIVE when you're ready to collect responses.
Endpoint:
POST /api/account/surveys
Example request:
POST /api/account/surveys HTTP/1.1
token: YOUR_API_KEY
Content-Type: application/json
{
"name": "Customer Satisfaction Survey",
"description": "Quarterly CSAT survey",
"questions": [
{
"id": "country",
"type": "radio",
"title": "Where are you located?",
"choices": ["US", "Canada", "Other"],
"required": true,
"logic": [
{ "if": "is", "value": "Other", "then": "disqualify" }
]
},
{
"type": "radio",
"title": "How satisfied are you?",
"choices": ["Very satisfied", "Satisfied", "Neutral", "Dissatisfied"],
"required": true
},
{
"type": "textarea",
"title": "What could we improve?"
}
],
"settings": {
"is_anonymous": true,
"user_hash_method": "none",
"stop_criteria": "responses",
"stop_value": 500
}
}
Example response (201):
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "DRAFT",
"share_url": "https://app.polling.com/forms/550e8400-.../share"
}
Example response (422) — validation failure:
{
"valid": false,
"errors": [
{ "path": "questions[0].choices", "message": "Choices are required and must be a non-empty array", "severity": "error" }
]
}
Every supported type and its type-specific fields are listed in Question Types Reference. For branching / skip / disqualify / URL rules see Logic Rules.
Validates a survey payload and returns the same errors the create endpoint would, without saving anything.
Endpoint:
POST /api/account/surveys/validate
Example request: same body shape as Create Survey.
Example response (200) — valid (may include warnings):
{
"valid": true,
"errors": [
{ "path": "questions[2].title", "message": "Question has no title", "severity": "warning" }
]
}
Example response (200) — invalid:
{
"valid": false,
"errors": [
{ "path": "name", "message": "Survey name is required", "severity": "error" },
{ "path": "questions[0].choices", "message": "Choices are required and must be a non-empty array", "severity": "error" }
]
}
Fetch a survey's full configuration. The response can be edited and sent straight back to the update endpoint.
Endpoint:
GET /api/account/surveys/{uuid}
Parameters:
uuid (path, required): survey UUID.Example request:
GET /api/account/surveys/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1
token: YOUR_API_KEY
Example response (200):
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"name": "Customer Satisfaction Survey",
"description": "Quarterly CSAT survey",
"status": "ACTIVE",
"share_url": "https://app.polling.com/forms/550e8400-.../share",
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-01-15T12:00:00Z",
"questions": [
{
"hash": "aB3xY9kL2q",
"type": "radio",
"title": "Where are you located?",
"required": true,
"choices": ["US", "Canada", "Other"],
"logic": [
{ "if": "is", "value": "Other", "then": "disqualify" }
]
},
{
"hash": "mN7pQ4rS8t",
"type": "textarea",
"title": "What could we improve?"
}
],
"settings": {
"stop_criteria": "responses",
"stop_value": 500,
"start_trigger": "now",
"start_at": null,
"is_anonymous": true,
"redirect_type": "none",
"redirect_url": null,
"redirect_url_disqualified": null,
"user_hash_method": "none",
"reward_amount": null,
"reward_name": null,
"complete_html": null,
"complete_json": null,
"screenout_enabled": false,
"screenout_countries": [],
"screenout_languages": []
}
}
Replace the survey's name, description, questions, and settings. Questions are matched by hash: include a question's hash to keep it (and its collected answers) intact, omit it to delete it, or add a new entry (without hash) to create one. See Logic Rules for logic.target semantics during updates.
Endpoint:
POST /api/account/surveys/{uuid}
Body flags:
confirm_delete_questions_with_answers (boolean, default false): required when removing a question that already has collected answers. Without it the request returns 409.Example request — reorder and add a new question, preserving hashes:
POST /api/account/surveys/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1
token: YOUR_API_KEY
Content-Type: application/json
{
"name": "Customer Satisfaction Survey",
"description": "Added an NPS question and reordered.",
"questions": [
{
"hash": "aB3xY9kL2q",
"type": "radio",
"title": "Where are you located?",
"choices": ["US", "Canada", "Other"],
"required": true,
"logic": [{ "if": "is", "value": "Other", "then": "disqualify" }]
},
{
"type": "nps",
"title": "How likely are you to recommend us?",
"rate_max": 10
},
{
"hash": "mN7pQ4rS8t",
"type": "textarea",
"title": "What could we improve?"
}
]
}
Example response (200):
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "ACTIVE",
"share_url": "https://app.polling.com/forms/550e8400-.../share"
}
Example response (409) — removing a question with collected answers:
{
"message": "Some questions you are removing already have collected responses.",
"questions_with_answers": [
{ "id": 42, "title": "How satisfied are you?", "answer_count": 57 }
]
}
Pass confirm_delete_questions_with_answers: true in the body to proceed anyway.
Example response (422) — hash from another survey:
{
"valid": false,
"errors": [
{ "path": "questions", "message": "hash 'aB3xY9kL2q' does not reference a question on this survey", "severity": "error" }
]
}
Every hash in an update payload must belong to the survey being updated. Mixing hashes from another survey (even one in the same account) is rejected — fetch the target survey's current hashes with GET /surveys/{uuid} and round-trip them.
The survey status is not modified here — use the lifecycle endpoints below.
Permanently delete a survey along with every session and answer. Irreversible; use /complete instead if you just want to stop collecting responses.
Endpoint:
DELETE /api/account/surveys/{uuid}
Example request:
DELETE /api/account/surveys/550e8400-e29b-41d4-a716-446655440000 HTTP/1.1
token: YOUR_API_KEY
Example response: 204 No Content.
Move a survey through its lifecycle. Invalid transitions (e.g. reactivating a COMPLETED survey) return 409.
POST /api/account/surveys/{uuid}/start → ACTIVE (from DRAFT, SCHEDULED, or PAUSED)
POST /api/account/surveys/{uuid}/pause → PAUSED (from ACTIVE)
POST /api/account/surveys/{uuid}/complete → COMPLETED (terminal; from any non-DISABLED status)
Example request:
POST /api/account/surveys/550e8400-e29b-41d4-a716-446655440000/start HTTP/1.1
token: YOUR_API_KEY
Example response (200):
{
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"status": "ACTIVE",
"share_url": "https://app.polling.com/forms/550e8400-.../share"
}
Example response (409) — transition not allowed:
{
"error": "Cannot start a survey in status 'COMPLETED'.",
"current_status": "COMPLETED",
"allowed_from": ["DRAFT", "SCHEDULED", "PAUSED", "ACTIVE"]
}
Status values: DRAFT, SCHEDULED, ACTIVE, PAUSED, COMPLETED, DISABLED. DISABLED is a staff-imposed lock — contact support if you land in it.
Aggregate stats, per-question summaries, and respondent demographics for a survey. Each question summary carries common fields (hash, question, type, total_answers, avg_time_seconds) plus only the extra fields that apply to its type — see Question Types Reference for every type's result shape.
Endpoint:
GET /api/account/surveys/{uuid}/results
Query parameters (all optional):
country — ISO country codelanguage — language codefrom, to — YYYY-MM-DD date range, applied to session startsession_status — only_completed | only_incompleted | only_disqualified | except_disqualifiedexclude_bots — true to exclude bot-flagged respondentsPer-question-type fields:
| Question types | Extra fields |
|---|---|
radio, checkbox, boolean, confirmation |
choices: [{value, count, percent}] |
rating |
rate_format, rate_max, avg_rating, choices |
nps |
rate_max, avg_score, nps_score, detractors, passives, promoters, choices |
ranking |
choices: [{value, avg_rank, position_counts}] |
matrix_radio, matrix_checkbox, matrix_linear |
matrix: {rows: [{value, columns: [{value, count, percent}]}]} |
signature |
signed_count, not_signed_count |
click_heatmap |
image_url, clicks: [{x, y, count}] |
text, textarea, address |
word_cloud: [{word, count}] — top 20 words, stopwords removed; empty array when there are no answers. |
Example request:
GET /api/account/surveys/550e8400-.../results?country=US&from=2026-01-01&session_status=only_completed HTTP/1.1
token: YOUR_API_KEY
Example response (200 — abridged):
{
"survey": { "uuid": "550e8400-...", "name": "CSAT Q1", "status": "ACTIVE" },
"stats": {
"sessions": { "completed": 423, "incompleted": 0, "disqualified": 0, "total": 423 },
"completion_rate_percent": 100.0,
"median_completion_seconds": 187,
"questions_count": 3
},
"demographics": {
"countries": [{ "code": "US", "count": 423 }],
"languages": [{ "code": "en", "count": 389 }, { "code": "es", "count": 34 }]
},
"questions": [
{
"hash": "aB3xY9kL2q",
"question": "How satisfied are you?",
"type": "radio",
"total_answers": 423,
"avg_time_seconds": 4.2,
"choices": [
{ "value": "Very satisfied", "count": 201, "percent": 47.5 },
{ "value": "Satisfied", "count": 158, "percent": 37.4 }
]
},
{
"hash": "xY2zA6bC9d",
"question": "How likely are you to recommend us?",
"type": "nps",
"total_answers": 423,
"avg_time_seconds": 2.1,
"rate_max": 10,
"avg_score": 7.8,
"nps_score": 31.7,
"detractors": { "count": 67, "percent": 15.8 },
"passives": { "count": 155, "percent": 36.6 },
"promoters": { "count": 201, "percent": 47.5 },
"choices": [
{ "value": "10", "count": 89, "percent": 21.0 }
]
},
{
"hash": "mN7pQ4rS8t",
"question": "What could we improve?",
"type": "textarea",
"total_answers": 318,
"avg_time_seconds": 22.8,
"word_cloud": [
{ "word": "documentation", "count": 42 },
{ "word": "pricing", "count": 38 }
]
}
],
"filters_applied": {
"country": "US", "language": null, "from": "2026-01-01", "to": null,
"session_status": "only_completed", "exclude_bots": false
}
}
Every session a given respondent has on a single survey.
Endpoint:
GET /api/account/surveys/{uuid}/sessions-by-respondent/{respondent_id}
Parameters:
uuid (path, required): survey UUID.respondent_id (path, required): the external_id you passed via the Embed integration's customer_id.Example request:
GET /api/account/surveys/550e8400-.../sessions-by-respondent/user_12345 HTTP/1.1
token: YOUR_API_KEY
Example response (200):
[
{
"session_id": 456,
"is_completed": true,
"is_disqualified": false,
"started_at": "2024-01-20T14:30:00Z",
"completed_at": "2024-01-20T14:45:00Z",
"answers": [
{
"question_hash": "aB3xY9kL2q",
"question": "How satisfied are you with our service?",
"answer": "Very satisfied"
}
]
}
]
A respondent's profile plus every session they have across every survey in the account. Each session includes a survey_uuid so you can identify which survey it belongs to.
Endpoint:
GET /api/account/respondents/{respondent_id}
Parameters:
respondent_id (path, required): the external_id you passed via the Embed integration's customer_id.Example request:
GET /api/account/respondents/user_12345 HTTP/1.1
token: YOUR_API_KEY
Example response (200):
{
"external_id": "user_12345",
"country": "US",
"region": "California",
"city": "San Francisco",
"language": "en",
"is_anonymous": false,
"is_mobile": true,
"lat": 37.7749,
"lon": -122.4194,
"last_access_at": "2024-01-20T14:45:00Z",
"created_at": "2024-01-10T08:00:00Z",
"sessions": [
{
"survey_uuid": "550e8400-e29b-41d4-a716-446655440000",
"session_id": 456,
"is_completed": true,
"is_disqualified": false,
"started_at": "2024-01-20T14:30:00Z",
"completed_at": "2024-01-20T14:45:00Z",
"answers": [
{
"question_hash": "aB3xY9kL2q",
"question": "How satisfied are you with our service?",
"answer": "Very satisfied"
}
]
}
]
}
The external ids of every respondent that completed a survey. Respondents who started but didn't finish aren't included. Capped at 1000 entries.
Endpoint:
GET /api/account/surveys/{uuid}/respondents/list
Parameters:
uuid (path, required): survey UUID.Example request:
GET /api/account/surveys/550e8400-.../respondents/list HTTP/1.1
token: YOUR_API_KEY
Example response (200):
[
"user_12345",
"user_67890",
"customer_abc",
"customer_def"
]
200 OK — successful read or update.201 Created — survey created.204 No Content — successful delete.401 Unauthorized — missing or invalid API key.403 Forbidden — the account's plan doesn't include API access (Standard or above required).404 Not Found — resource doesn't exist or doesn't belong to the account.409 Conflict — destructive update without confirm_delete_questions_with_answers=true, or a lifecycle transition that isn't allowed from the survey's current status.422 Unprocessable Entity — payload validation failed.429 Too Many Requests — rate limit exceeded (60 requests per minute).500 Internal Server Error — server error. The response includes a code you can reference in support requests.A question can carry a logic array. Each rule matches an if condition against the respondent's answer and, on the first match, applies a then action. Rules are evaluated top-down; the first match wins.
{
"logic": [
{ "if": "is", "value": "Other", "then": "disqualify" },
{ "if": "lower", "value": 7, "then": "go_question", "target": "aB3xY9kL2q" },
{ "if": "higher","value": 6, "then": "finish" }
]
}
if conditions| Condition | What it checks | value shape |
|---|---|---|
is |
Answer equals value | string | number |
is_filled |
Answer is not empty | (no value) |
is_empty |
Answer is empty | (no value) |
contains_any |
Selected choices include at least one value from the list | string[] |
contains_all |
Selected choices include every value in the list | string[] |
doesnt_contains_any |
None of the listed values are selected | string[] |
doesnt_contains_all |
At least one listed value is not selected | string[] |
contains |
Free-text answer contains the substring | string |
doesnt_contains |
Free-text answer doesn't contain the substring | string |
between |
Numeric / date answer is within range (inclusive) | { "from": ..., "to": ... } |
higher |
Numeric / date answer is strictly greater than value | number | string |
lower |
Numeric / date answer is strictly less than value | number | string |
| Condition | radio | checkbox | ranking | text | textarea | rating | nps | boolean | confirmation | matrix_* | address |
|---|---|---|---|---|---|---|---|---|---|---|---|
is / is_filled / is_empty |
✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
contains_any / doesnt_contains_any |
✓ | ✓ | ✓ | ✓ | ✓ | ||||||
contains_all / doesnt_contains_all |
✓ | ✓ | ✓ | ✓ | |||||||
contains / doesnt_contains |
✓¹ | ✓ | |||||||||
between / higher / lower |
✓² | ✓ | ✓ |
¹ Only for text questions with input_type of text, email, or url.
² Only for text questions with input_type of number, date, time, or datetime-local.
Logic is not supported on captcha, signature, or click_heatmap. Using a condition that isn't valid for a question's type returns 422 at create / update time.
then actions| Action | What it does | Extra field |
|---|---|---|
go_question |
Jump to another question, skipping anything in between | target — question hash |
make_required |
Mark another question as required | target — question hash |
make_not_required |
Mark another question as optional | target — question hash |
disable_question |
Hide another question (pair with default_disabled: true) |
target — question hash |
enable_question |
Show another question that was default_disabled |
target — question hash |
finish |
End the survey and render the thank-you page | — |
disqualify |
End the survey and render the disqualified page | — |
open_url |
Redirect the respondent to a URL | url — must start with http:// or https:// |
target — identifying a question inside logictarget accepts either:
hash — the preferred form. Use this on any rule that references an existing question (always available on read).id — a create-time convenience. If you set id: "screener" on a question, a later rule can say target: "screener" before hashes exist. Ids are not persisted, so on update you must use hashes.Every question shares the common fields from Create Survey: type, optional id (write-only) or hash (on update), title, helper_text, required, default_disabled, logic. The snippets below show the type-specific input fields and the matching object you'll see inside the Results response's questions[] array.
radio — Single-selectCreate / update payload:
{
"type": "radio",
"title": "How did you hear about us?",
"choices": ["Search engine", "Social media", "Friend", "Other"],
"randomize": false,
"other_choice": true,
"other_choice_text": "Somewhere else…",
"image_choices": false
}
Choices can be plain strings or {text, value} objects (or {text, image} when image_choices: true).
Results JSON:
{
"hash": "aB3xY9kL2q",
"question": "How did you hear about us?",
"type": "radio",
"total_answers": 423,
"avg_time_seconds": 4.2,
"choices": [
{ "value": "Search engine", "count": 180, "percent": 42.6 },
{ "value": "Social media", "count": 120, "percent": 28.4 },
{ "value": "Friend", "count": 95, "percent": 22.5 },
{ "value": "Other", "count": 28, "percent": 6.6 }
]
}
checkbox — Multi-selectCreate / update payload:
{
"type": "checkbox",
"title": "Which features do you use?",
"choices": ["Dashboard", "Reports", "Integrations", "API"],
"min_choices": 1,
"max_choices": 3,
"randomize": false,
"other_choice": false
}
Supports everything radio does, plus min_choices / max_choices.
Results JSON:
{
"hash": "cD4eF6gH8i",
"question": "Which features do you use?",
"type": "checkbox",
"total_answers": 398,
"avg_time_seconds": 6.8,
"choices": [
{ "value": "Dashboard", "count": 312, "percent": 78.4 },
{ "value": "Reports", "count": 287, "percent": 72.1 },
{ "value": "Integrations", "count": 164, "percent": 41.2 },
{ "value": "API", "count": 91, "percent": 22.9 }
]
}
Counts are per-selection (not per-session), so choices[].count can sum above total_answers — total_answers is the number of sessions that answered at all.
ranking — Drag-to-rankCreate / update payload:
{
"type": "ranking",
"title": "Rank these priorities",
"choices": ["Reliability", "Performance", "Ease of use", "Price"],
"randomize": true,
"image_choices": false
}
All defined choices must be ranked — there is no "Other".
Results JSON:
{
"hash": "jK5lM7nP9q",
"question": "Rank these priorities",
"type": "ranking",
"total_answers": 341,
"avg_time_seconds": 18.4,
"choices": [
{ "value": "Reliability", "avg_rank": 1.47, "position_counts": [189, 87, 43, 22] },
{ "value": "Performance", "avg_rank": 2.12, "position_counts": [87, 152, 78, 24] },
{ "value": "Ease of use", "avg_rank": 2.88, "position_counts": [43, 68, 141, 89] },
{ "value": "Price", "avg_rank": 3.53, "position_counts": [22, 34, 79, 206] }
]
}
position_counts[i] is how many sessions placed the choice at position i+1.
text — Single-line inputCreate / update payload:
{
"type": "text",
"title": "What's your email?",
"input_type": "email",
"placeholder": "you@example.com",
"min": "5",
"max": "100",
"regex": "^[^@]+@[^@]+\\.[^@]+$"
}
input_type is one of text (default), email, url, number, date, time, datetime-local. min / max mean character length for text inputs and numeric / temporal bounds for the rest. regex is only applied to input_type: "text".
Results JSON:
{
"hash": "lM8nO1pQ3r",
"question": "What's your email?",
"type": "text",
"total_answers": 372,
"avg_time_seconds": 5.1,
"word_cloud": [
{ "word": "gmail", "count": 198 },
{ "word": "com", "count": 201 },
{ "word": "outlook", "count": 47 },
{ "word": "yahoo", "count": 28 }
]
}
Top 20 words, stopwords removed. Empty array when the question has no answers.
textarea — Multi-line inputCreate / update payload:
{
"type": "textarea",
"title": "Any other feedback?",
"placeholder": "Share your thoughts…"
}
Results JSON:
{
"hash": "mN7pQ4rS8t",
"question": "Any other feedback?",
"type": "textarea",
"total_answers": 318,
"avg_time_seconds": 22.8,
"word_cloud": [
{ "word": "documentation", "count": 42 },
{ "word": "pricing", "count": 38 },
{ "word": "mobile", "count": 29 },
{ "word": "notifications", "count": 21 }
]
}
rating — Stars / smileys / labelsCreate / update payload:
{
"type": "rating",
"title": "Rate our service",
"rate_format": "stars",
"rate_max": 5
}
rate_format is stars (default), smileys, or labels. rate_max is 1–20.
Results JSON:
{
"hash": "rS2tU4vW6x",
"question": "Rate our service",
"type": "rating",
"total_answers": 419,
"avg_time_seconds": 2.9,
"rate_format": "stars",
"rate_max": 5,
"avg_rating": 4.2,
"choices": [
{ "value": "1", "count": 6, "percent": 1.4 },
{ "value": "2", "count": 15, "percent": 3.6 },
{ "value": "3", "count": 58, "percent": 13.8 },
{ "value": "4", "count": 142, "percent": 33.9 },
{ "value": "5", "count": 198, "percent": 47.3 }
]
}
Distribution covers 1..rate_max.
nps — Net Promoter ScoreCreate / update payload:
{
"type": "nps",
"title": "How likely are you to recommend us?",
"rate_max": 10,
"rate_format": "labels"
}
rate_max is 5 or 10 (default). Standard NPS is 0–10.
Results JSON:
{
"hash": "xY2zA6bC9d",
"question": "How likely are you to recommend us?",
"type": "nps",
"total_answers": 423,
"avg_time_seconds": 2.1,
"rate_max": 10,
"avg_score": 7.8,
"nps_score": 31.7,
"detractors": { "count": 67, "percent": 15.8 },
"passives": { "count": 155, "percent": 36.6 },
"promoters": { "count": 201, "percent": 47.5 },
"choices": [
{ "value": "0", "count": 1, "percent": 0.2 },
{ "value": "1", "count": 2, "percent": 0.5 },
{ "value": "6", "count": 30, "percent": 7.1 },
{ "value": "7", "count": 61, "percent": 14.4 },
{ "value": "8", "count": 94, "percent": 22.2 },
{ "value": "9", "count": 112, "percent": 26.5 },
{ "value": "10", "count": 89, "percent": 21.0 }
]
}
nps_score = promoters_percent - detractors_percent. Distribution covers 0..rate_max.
boolean — Yes / NoCreate / update payload:
{
"type": "boolean",
"title": "Have you used the new dashboard?",
"label_true": "Yes",
"label_false": "No"
}
Results JSON:
{
"hash": "eF3gH5iJ7k",
"question": "Have you used the new dashboard?",
"type": "boolean",
"total_answers": 405,
"avg_time_seconds": 1.8,
"choices": [
{ "value": "No", "count": 118, "percent": 29.1 },
{ "value": "Yes", "count": 287, "percent": 70.9 }
]
}
choices[].value reflects the label_true / label_false strings.
confirmation — Agree / Disagree (consent gate)Create / update payload:
{
"type": "confirmation",
"title": "Do you consent to the terms?",
"label_agree": "I consent",
"label_disagree": "I do not consent"
}
A disqualify-on-disagree logic rule is auto-injected — custom logic rules are allowed and merged with it.
Results JSON:
{
"hash": "pQ1rS3tU5v",
"question": "Do you consent to the terms?",
"type": "confirmation",
"total_answers": 511,
"avg_time_seconds": 3.5,
"choices": [
{ "value": "I do not consent", "count": 12, "percent": 2.3 },
{ "value": "I consent", "count": 499, "percent": 97.7 }
]
}
matrix_radio — Grid, single-select per rowCreate / update payload:
{
"type": "matrix_radio",
"title": "Rate each area",
"rows": [
{ "value": "quality", "text": "Quality" },
{ "value": "speed", "text": "Speed" }
],
"columns": [
{ "value": "good", "text": "Good" },
{ "value": "bad", "text": "Bad" }
]
}
rows / columns can be plain strings or {text, value} objects.
Results JSON:
{
"hash": "uV6wX8yZ0a",
"question": "Rate each area",
"type": "matrix_radio",
"total_answers": 381,
"avg_time_seconds": 14.2,
"matrix": {
"rows": [
{
"value": "Quality",
"columns": [
{ "value": "Good", "count": 340, "percent": 89.2 },
{ "value": "Bad", "count": 41, "percent": 10.8 }
]
},
{
"value": "Speed",
"columns": [
{ "value": "Good", "count": 298, "percent": 78.2 },
{ "value": "Bad", "count": 83, "percent": 21.8 }
]
}
]
}
}
matrix_checkbox — Grid, multi-select per rowSame payload shape as matrix_radio; respondents can tick multiple columns per row.
Results JSON: same matrix.rows[].columns shape as matrix_radio. Counts are per-selection, so percent values within a single row can sum above 100%.
matrix_linear — Likert gridCreate / update payload:
{
"type": "matrix_linear",
"title": "To what extent do you agree?",
"rows": [
"The team is responsive",
"The product is reliable",
"The docs are clear"
],
"columns": [
"Strongly disagree",
"Disagree",
"Neutral",
"Agree",
"Strongly agree"
]
}
Results JSON:
{
"hash": "aB2cD4eF6g",
"question": "To what extent do you agree?",
"type": "matrix_linear",
"total_answers": 412,
"avg_time_seconds": 17.1,
"matrix": {
"rows": [
{
"value": "The team is responsive",
"columns": [
{ "value": "Strongly disagree", "count": 4, "percent": 1.0 },
{ "value": "Disagree", "count": 17, "percent": 4.1 },
{ "value": "Neutral", "count": 59, "percent": 14.3 },
{ "value": "Agree", "count": 182, "percent": 44.2 },
{ "value": "Strongly agree", "count": 150, "percent": 36.4 }
]
}
]
}
}
signature — Canvas signature captureCreate / update payload:
{
"type": "signature",
"title": "Please sign below"
}
Plan requirement: Standard+. Logic rules are not supported.
Results JSON:
{
"hash": "bC4dE7fG1h",
"question": "Please sign below",
"type": "signature",
"total_answers": 142,
"avg_time_seconds": 8.7,
"signed_count": 110,
"not_signed_count": 32
}
click_heatmap — Clickable imageCreate / update payload:
{
"type": "click_heatmap",
"title": "Where would you click first?",
"image_url": "https://cdn.example.com/screenshots/homepage.png"
}
Plan requirement: Standard+. Logic rules are not supported.
Results JSON:
{
"hash": "hK8lM2nO5p",
"question": "Where would you click first?",
"type": "click_heatmap",
"total_answers": 89,
"avg_time_seconds": 5.4,
"image_url": "https://cdn.example.com/screenshots/homepage.png",
"clicks": [
{ "x": 0.45, "y": 0.62, "count": 28 },
{ "x": 0.80, "y": 0.15, "count": 17 },
{ "x": 0.12, "y": 0.88, "count": 9 }
]
}
x and y are fractions of the image's width and height (0.0–1.0).
address — Structured address inputCreate / update payload:
{
"type": "address",
"title": "Shipping address",
"address_placeholders": {
"address_line_1": "Street address",
"address_line_2": "Apt, suite, unit…",
"city": "City",
"state": "State / Province",
"zip_code": "ZIP / Postal code",
"country": "Country"
}
}
All address_placeholders keys are optional.
Results JSON:
{
"hash": "dE6fG8hI0j",
"question": "Shipping address",
"type": "address",
"total_answers": 204,
"avg_time_seconds": 19.7,
"word_cloud": [
{ "word": "street", "count": 78 },
{ "word": "avenue", "count": 42 },
{ "word": "new", "count": 35 },
{ "word": "york", "count": 29 }
]
}
Address lines are tokenised as free text.
captcha — Bot-check challengeCreate / update payload:
{
"type": "captcha",
"title": "Verify you're human"
}
Plan requirement: Standard+. No answer is persisted, so captcha never appears in results. Logic rules are not supported.
page — Static HTML element between questionsCreate / update payload:
{
"type": "page",
"content": "<h2>Section 2</h2><p>The following questions are about billing.</p>"
}
Plan requirement: Essentials+. Pages don't collect responses and never appear in results.