Votes
Casting a vote is public — no API key required — since real voters are your end-users, not you. Reading and removing votes is developer-only.
All vote endpoints are nested under a poll. voter_identifier is a string you choose to represent one of your end-users (their user ID, a session ID, etc). It's used to prevent the same voter from voting twice on the same poll, and to look their vote(s) back up later.
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /polls/{poll}/votes | public | Cast a vote |
| GET | /polls/{poll}/votes | API key | List all votes on a poll |
| GET | /polls/{poll}/votes/identifier/{voter_identifier} | API key | Find votes by a voter identifier |
| GET | /polls/{poll}/votes/{vote} | API key | Get a single vote |
| DELETE | /polls/{poll}/votes/{vote} | API key | Remove a vote |
/polls/{poll}/votes
Public
Cast a vote
No API key required — call this directly from your end-user's browser or app.
| Field | Type | Rules |
|---|---|---|
| poll_option_id | integer | Required, must be an option belonging to this poll |
| voter_identifier | string | Required, max 255 |
| captcha_token | string | Required only if this poll has requires_captcha: true (see Polls) — a token from your embedded Cloudflare Turnstile widget, verified server-side before the vote is accepted. |
curl -X POST https://qnaapi.com/api/v1/polls/2/votes \
-H "Content-Type: application/json" \
-d '{ "poll_option_id": 3, "voter_identifier": "end-user-42" }'{
"data": {
"id": 1,
"poll_id": 2,
"poll_option_id": 3,
"voter_identifier": "end-user-42",
"created_at": "2026-08-14T22:44:53.000000Z"
}
}If voter_identifier has already voted on this poll, the response is 409 Conflict. If poll_option_id doesn't belong to this poll, it's 422 Unprocessable Entity. Rate-limited to 30 requests/minute/IP.
requires_captcha: true, a missing, invalid, or Cloudflare-rejected captcha_token returns 422 Unprocessable Entity with a validation error on captcha_token — same shape as any other field error. See Error Responses.
is_closed: true, or its response_limit has been reached — a vote is rejected with 422 Unprocessable Entity and a validation error on closed. Check is_accepting_responses on the poll (see Polls) before showing your vote form to avoid this.
/polls/{poll}/votes
API key
List votes on a poll
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls/2/votes
/polls/{poll}/votes/identifier/{voter_identifier}
API key
Find votes by voter identifier
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls/2/votes/identifier/end-user-42
Returns a list ("data": [...]) — in practice 0 or 1 items, since a voter can't vote twice on the same poll.
/polls/{poll}/votes/{vote}
API key
Get a single vote
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls/2/votes/1
Returns 404 if the vote doesn't belong to that poll.
/polls/{poll}/votes/{vote}
API key
Remove a vote
On top of the API key, deleting a vote still requires the matching voter_identifier used to cast it — a small extra safety check against deleting the wrong vote by ID.
curl -X DELETE https://qnaapi.com/api/v1/polls/2/votes/1 \
-H "X-API-Key: qna_..." \
-H "Content-Type: application/json" \
-d '{ "voter_identifier": "end-user-42" }'- 204 No Content — removed, and the option's vote count is decremented.
- 403 Forbidden — the vote exists but
voter_identifierdoesn't match. - 404 Not Found — the vote doesn't exist, or doesn't belong to this poll, or the poll isn't yours.