Docs
Pricing Log in
Documentation

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.

MethodEndpointDescription
POST/pollsCreate a poll
GET/pollsList 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)
POST /polls API key

Create a poll

FieldTypeRules
identifierstringOptional, 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.
titlestringRequired, max 255
descriptionstringOptional
metadataobjectOptional — 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_idintegerOptional, 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_captchabooleanOptional, 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_closedbooleanOptional, defaults to false. When true, casting a vote on this poll is rejected — see Votes.
response_limitintegerOptional, nullable, minimum 1. Once the poll has received this many votes, further votes are rejected the same way as is_closed.
optionsarray of stringsRequired, at least 2, each unique within the poll
REQUEST
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 }
  }'
201 CREATED
{
  "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.
GET /polls API key

List your polls

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

GET /polls/identifier/{identifier} API key

Get a poll by identifier

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

Get a poll by ID

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

Update a poll

Every field is optional — send only what you want to change; anything omitted is left as-is.

FieldTypeRules
identifierstringOptional, 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.
titlestringOptional, max 255, cannot be set to an empty string if provided
descriptionstringOptional, nullable
metadataobjectOptional, nullable
site_idintegerOptional, nullable, must belong to one of your own sites. Ignored (your key's own site is used) for a site-scoped key.
requires_captchabooleanOptional — same rules as create.
is_closedbooleanOptional — same rules as create.
response_limitintegerOptional, nullable — same rules as create.
optionsarrayOptional — same rules as create. Only accepted while the poll has no votes yet; replaces the entire option list.
REQUEST
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?" }'
A poll's options can be freely edited up until the moment it receives its first vote. Once someone has voted, sending 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.
DELETE /polls/{poll} API key

Delete a poll

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