Docs
Pricing Log in
Documentation

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.

MethodEndpointAuthDescription
POST/polls/{poll}/votespublicCast a vote
GET/polls/{poll}/votesAPI keyList all votes on a poll
GET/polls/{poll}/votes/identifier/{voter_identifier}API keyFind votes by a voter identifier
GET/polls/{poll}/votes/{vote}API keyGet a single vote
DELETE/polls/{poll}/votes/{vote}API keyRemove a vote
POST /polls/{poll}/votes Public

Cast a vote

No API key required — call this directly from your end-user's browser or app.

FieldTypeRules
poll_option_idintegerRequired, must be an option belonging to this poll
voter_identifierstringRequired, max 255
captcha_tokenstringRequired 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.
REQUEST — NO API KEY
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" }'
201 CREATED
{
  "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.

If the poll has 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.
If the poll is closed — 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.
GET /polls/{poll}/votes API key

List votes on a poll

REQUEST
curl -H "X-API-Key: qna_..." https://qnaapi.com/api/v1/polls/2/votes
GET /polls/{poll}/votes/identifier/{voter_identifier} API key

Find votes by voter identifier

REQUEST
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.

GET /polls/{poll}/votes/{vote} API key

Get a single vote

REQUEST
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.

DELETE /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.

REQUEST
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_identifier doesn't match.
  • 404 Not Found — the vote doesn't exist, or doesn't belong to this poll, or the poll isn't yours.