Polls
A poll is a single question with a fixed set of options. All poll endpoints require your API key and are scoped to your own polls.
A mismatched poll returns 404, not 403 — no information about other accounts' data leaks. Casting a vote is handled separately — see Votes.
| Method | Endpoint | Description |
|---|---|---|
| POST | /polls | Create a poll |
| GET | /polls | List all of your polls |
| GET | /polls/identifier/{identifier} | Get one of your polls by its identifier |
| GET | /polls/{poll} | Get one of your polls by ID |
| PATCH | /polls/{poll} | Update a poll (PUT also accepted) |
| DELETE | /polls/{poll} | Delete a poll (also deletes its options and votes) |
/polls
API key
Create a poll
| Field | Type | Rules |
|---|---|---|
| identifier | string | Optional, max 255, letters/numbers/dashes/underscores, unique per account (not globally). Behaves like a post slug — if omitted, it's generated from title, with -2, -3, etc. appended on collision. |
| title | string | Required, max 255 |
| description | string | Optional |
| metadata | object | Optional — a flexible, freeform JSON object for your own custom data. Not used or validated beyond being a valid JSON object; returned back to you as-is. |
| site_id | integer | Optional, must belong to one of your own sites. Ignored (your key's own site is used instead) if you're authenticating with a site-scoped key. |
| requires_captcha | boolean | Optional, defaults to false. When true, casting a vote on this poll requires a valid Turnstile captcha_token — see Votes. Rejected with 422 unless you've configured a Turnstile secret key from the dashboard's API health & keys page. |
| is_closed | boolean | Optional, defaults to false. When true, casting a vote on this poll is rejected — see Votes. |
| response_limit | integer | Optional, nullable, minimum 1. Once the poll has received this many votes, further votes are rejected the same way as is_closed. |
| options | array of strings | Required, at least 2, each unique within the poll |
curl -X POST https://qnaapi.com/api/v1/polls \
-H "X-API-Key: qna_..." \
-H "Content-Type: application/json" \
-d '{
"identifier": "favorite-color",
"title": "What is your favorite color?",
"description": "Pick one.",
"options": ["Red", "Blue"],
"metadata": { "campaign": "spring-2026", "internal_id": 42 }
}'{
"data": {
"id": 2,
"identifier": "favorite-color",
"title": "What is your favorite color?",
"description": "Pick one.",
"metadata": { "campaign": "spring-2026", "internal_id": 42 },
"site_id": null,
"requires_captcha": false,
"is_closed": false,
"response_limit": null,
"is_accepting_responses": true,
"options": [
{ "id": 3, "label": "Red", "votes_count": 0 },
{ "id": 4, "label": "Blue", "votes_count": 0 }
],
"created_at": "2026-08-14T22:44:43.000000Z",
"updated_at": "2026-08-14T22:44:43.000000Z"
}
}A duplicate identifier on your own account returns 422 Unprocessable Entity with a validation error on the identifier field.
is_accepting_responses is read-only and computed on every response — false whenever is_closed is true or the poll's vote count has reached response_limit. Toggle is_closed/response_limit instead of trying to set it directly.
/polls
API key
List your polls
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls
Returns { "data": [ ...poll objects... ] }. A site-scoped key only returns that site's polls; a master key sees every poll on the account, optionally narrowed with ?site_id=.
/polls/identifier/{identifier}
API key
Get a poll by identifier
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls/identifier/favorite-color
/polls/{poll}
API key
Get a poll by ID
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls/2
/polls/{poll}
API key
Update a poll
Every field is optional — send only what you want to change; anything omitted is left as-is.
| Field | Type | Rules |
|---|---|---|
| identifier | string | Optional, same rules as create, unique per account excluding this poll itself. Sending an explicit empty/null value regenerates it from the (possibly also-updated) title. |
| title | string | Optional, max 255, cannot be set to an empty string if provided |
| description | string | Optional, nullable |
| metadata | object | Optional, nullable |
| site_id | integer | Optional, nullable, must belong to one of your own sites. Ignored (your key's own site is used) for a site-scoped key. |
| requires_captcha | boolean | Optional — same rules as create. |
| is_closed | boolean | Optional — same rules as create. |
| response_limit | integer | Optional, nullable — same rules as create. |
| options | array | Optional — same rules as create. Only accepted while the poll has no votes yet; replaces the entire option list. |
curl -X PATCH https://qnaapi.com/api/v1/polls/2 \
-H "X-API-Key: qna_..." \
-H "Content-Type: application/json" \
-d '{ "title": "What'"'"'s your favorite color?" }'options in an update is rejected (422) — changing an option's label after votes have been cast against it, or removing an option outright, would silently invalidate that vote. Delete and recreate the poll if you need to change the options after that point.
/polls/{poll}
API key
Delete a poll
curl -X DELETE -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls/2
204 No Content on success. Deleting a poll cascades — its options and all votes cast on it are removed too.