Developers / API reference

Forms API

v4

Create polls and forms, publish them, collect responses and read the results from your own code.

Base URL
https://api.strawpoll.com/v4
Spec
OpenAPI 3

Overview

The Forms API (v4) lets your own code create polls and forms, publish them, collect responses and read the results. It is the API the StrawPoll editor itself uses.

  • Base URL: https://api.strawpoll.com/v4 (also served at https://strawpoll.com/api/v4)
  • Format: JSON in, JSON out. Timestamps are ISO 8601 strings, ids are UUIDs.
  • Machine-readable spec: OpenAPI 3.1

Every form has a short public id, the slug, as in https://strawpoll.com/r/AZPg4n1Td3-hnVAj6Q8p. All form endpoints take the slug.

Authentication

Create a key in Settings → API keys. It is shown once, so store it safely. Send it with every request:

bash
curl https://api.strawpoll.com/v4/me 
  -H "X-API-Key: sp_your_key"

Authorization: Bearer sp_your_key works as well. A key acts as you: it can manage every form you can manage in the dashboard, including those in workspaces you belong to. Delete a key in Settings to revoke it immediately.

The respondent endpoints, submit and verify, need no key from a browser. Requests from scripts, HTTP libraries, headless browsers, bots and AI agents, recognized by their user agent, are refused with automated unless they send a key.

Errors

Errors use the HTTP status and a JSON body:

json
{ "error": "This poll is closed.", "code": "closed" }

error is written for people; code, when present, is meant for your code.

StatusMeaning
400The request is invalid. Vote errors also name the question in step.
401No API key, or invalid_api_key.
403You cannot manage this form, or it does not accept this response.
404Not found.
409Conflict, such as a duplicate response.
429rate_limited: wait for Retry-After seconds.
503busy: lots of votes are arriving at once; retry shortly.

Rate limits

120 requests per minute per key. Without a key, requests from a browser count per signed-in user or browser (its voter cookie), up to 3,000 per minute for all browsers on one IP address; requests with neither count per IP address. Each response reports your budget in X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds).

How a form is built

A form is a list of sections. Question sections hold fields, and choice fields hold choices.

Section typeWhat it asksFields
multiple_choicePick one or more options; properties.layout dropdown shows one answer as a selectone multiple_choice field with choices
picture_choicePick imagesone multiple_choice field, choices with mediaId
rankingOrder options, points per placeone ranking field with choices
ranked_choiceRanked choice ballot, counted as an instant runoffone ranked_choice field with choices
meetingFind a timeone date field per day (label YYYY-MM-DD), one choice per time (label 09:00-10:00 or All day)
contentText, or one question with properties.variant set to yes_no, rating, scale, text, long_text, name, email or word_cloudone field for the question
contact_infoName and emailtext_small and email fields
page_break, welcome_screen, thank_you_screen, end_screen, redirectLayout and endingsnone

Form-wide settings live in properties. The most used:

PropertyValuesDefault
resultsVisibilitypublic, public_after_response, public_after_close, privatepublic
responsesVisibilitypublic lists names next to results, private does notprivate
responseDuplicateChecknone, session, ip, name, email, codeip
responseDuplicateCheckIntervalminutes until the same voter may respond again, or nullnull
opensAt, closesAtISO timestamps, or nullnull
responseLimitmaximum number of responses, or nullnull
allowComments, commentsVisibilitytrue/false, public/privatefalse, private
editResponsePermissionsadmin_own_responses, own_responses, admin, nobodyadmin_own_responses
blockVPNUsers, useCaptchaVerificationtrue/falsetrue, false

The OpenAPI spec lists every property of sections, fields and choices.

Create a poll

bash
curl https://api.strawpoll.com/v4/forms 
  -H "X-API-Key: sp_your_key" 
  -H "Content-Type: application/json" 
  -d '{
    "title": "Where should we go for lunch?",
    "status": "published",
    "properties": { "responseDuplicateCheck": "ip", "resultsVisibility": "public" },
    "sections": [{
      "type": "multiple_choice",
      "fields": [{
        "type": "multiple_choice",
        "label": "Where should we go for lunch?",
        "properties": { "isMultipleChoice": false },
        "choices": [{ "label": "Pizza" }, { "label": "Sushi" }, { "label": "Tacos" }]
      }]
    }]
  }'

The response is the full form, with generated ids and its links in urls.formUrl and urls.resultsUrl. Without "status": "published" a form starts as a draft that only you can see.

A meeting poll offers times on dates, in the section’s time zone:

json
{
	"title": "Team sync",
	"status": "published",
	"sections": [
		{
			"type": "meeting",
			"properties": { "timezone": "Europe/Berlin", "duration": 60, "location": "Room 1" },
			"fields": [
				{
					"type": "date",
					"label": "2026-11-02",
					"choices": [{ "label": "09:00-10:00" }, { "label": "14:00-15:00" }]
				},
				{ "type": "date", "label": "2026-11-03", "choices": [{ "label": "09:00-10:00" }] }
			]
		}
	]
}

AI assistants (MCP)

StrawPoll runs a Model Context Protocol server, so AI assistants such as Claude, ChatGPT or Cursor can create polls and read results for you.

  • Server URL: https://strawpoll.com/mcp (Streamable HTTP)
  • Sign-in: OAuth. Add the URL as a custom connector in your assistant and sign in to StrawPoll when it asks; you choose whether to allow it. Clients without OAuth can send an API key as X-API-Key or Authorization: Bearer sp_your_key instead.
ToolWhat it does
create_pollOne question with options: a vote, a ranking or a meeting poll. Published.
create_formA survey with several questions, from multiple_choice to matrix. A draft.
publish_pollPublish a draft.
list_pollsYour polls, newest first, with response counts.
get_resultsVotes per option, average ranks, meeting availability and recent text answers.
set_poll_openClose a poll or reopen it.

Polls made by an assistant are yours like any other, and use your defaults for new polls. An assistant can create up to 30 an hour.

Assistants without tools can hand you a link that opens the editor with the poll filled in, ready to check and publish:

undefined
https://strawpoll.com/forms/new?title=Where%20should%20we%20eat%3F&options=Pizza|Sushi|Tacos

options is split on | (or repeat option=). Add type=ranking or type=meeting (options like 2026-11-03 or 2026-11-03 14:00-15:00, with timezone=Europe/Berlin), multiple=1 to allow several answers and other=1 for an own answer.

Endpoints

Account

GET/meThe key’s owner and their workspaces.

Forms

GET/formsYour forms, newest first. ?workspaceId= lists a workspace’s forms.
POST/formsCreate a form. Optional workspaceId; defaults to your first workspace.
GET/forms/{slug}The live form with sections, fields, choices and tallies.
PUT/forms/{slug}Replace the live form (see below).
PATCH/forms/{slug}Change name, isOpen or properties only.
DELETE/forms/{slug}Delete the form and its responses for good.

PUT replaces everything: sections, fields and choices you leave out are deleted, with their answers. Send back the ids of the parts you keep, so their responses stay. Forms taken down by moderation (deleted, blocked) answer 403.

Drafts and publishing

The editor saves changes as a draft and publishes them in one step. Respondents keep seeing the live form until then.

PUT/forms/{slug}/draftSave title, themeId, properties and sections as the draft.
DELETE/forms/{slug}/draftThrow the draft away.
POST/forms/{slug}/publishMake the draft live. A body with sections is saved first.

Responses

GET/forms/{slug}/responsesResponses, newest first. limit (up to 100) and offset.
DELETE/forms/{slug}/responsesReset: delete every response and zero the tallies.

Totals per option are on the form itself: responseCount on each field and choice, and score for rankings. A response’s fields holds its answers in the shape described under Submit a response.

Submit a response

POST/forms/{slug}/submit sends a response the way the voting page does, with every rule of the form applied: schedule, limits, one response per person, captcha, VPN blocking, codes and email verification. Browsers need no key; scripts and other automated clients must send one.

json
{
	"fields": {
		"<choice field id>": ["<choice id>"],
		"<ranking field id>": ["<best choice id>", "<next choice id>", "<last choice id>"],
		"<meeting date field id>": [{ "choiceId": "<choice id>", "status": "yes" }],
		"<yes/no field id>": "yes",
		"<text field id>": "Free text"
	},
	"name": "Ada",
	"comment": "Optional",
	"code": "Optional unique code"
}
AnswerShape
Choice and picture questionslist of choice ids, plus one text when write-ins are allowed
Rankingevery choice id, best first
Ranked choicethe choice id in each place, best first, null for a place left empty
Meetingper date field, a list of choiceId and status (yes, if_need_be, no); name is required
Yes/no"yes" or "no"
Rating and scalethe number as a string, like "4"
Text and contact fieldsa string

A successful submit returns the response id. Refusals carry a code: upcoming, closed, full, unavailable, decided, captcha, vpn, code, verify_email, disposable, automated (all 403), duplicate (409) and busy (503).

The voter’s browser gets an sp_voter cookie. With it, PUT/forms/{slug}/submit and the response id change that response, if the form allows voters to edit.

For forms limited to one response per email address, verify the address first with POST/forms/{slug}/verify: send action: "send" and the email, then action: "verify" with the 6-digit code the voter received.

Codes and invites

GET/forms/{slug}/codesUnique codes.
POST/forms/{slug}/codesGenerate count codes of length characters, with optional style and prefix.
DELETE/forms/{slug}/codesDelete codes by ids.
GET/forms/{slug}/invitesEmail invites.
POST/forms/{slug}/invitesAdd one invite, or up to 1000 as invites.
PATCH/forms/{slug}/invites/{id}Change an invite.
DELETE/forms/{slug}/invites/{id}Delete an invite.
DELETE/forms/{slug}/invitesDelete invites by ids.
POST/forms/{slug}/invites/importInvite everyone on an invite list (inviteListId).
POST/forms/{slug}/invites/sendEmail invites with subject and message.

Invite messages may use the placeholders name, form and link in double braces.

Media and themes

POST/mediaUpload an image (multipart file, up to 5 MB). Use its id as a choice’s mediaId.
GET/themesYour themes, your workspaces’ themes and the built-in ones; ?workspaceId= for one workspace’s.
POST/themesCreate a theme with name and colour properties; add workspaceId to share it with a workspace you own or manage.
PUT/themes/{id}Change a theme. workspaceId shares it with a workspace, null makes it the owner’s own again.
DELETE/themes/{id}Delete a theme. A workspace’s themes can be changed and deleted by its owner and admins.

Coming from the Poll API (v3)

The Poll API v3 keeps working, with the same paths, keys and polls. See the v3 compatibility guide for what maps where.