Developers
Developer API
Automate PollsLive from your own backend - create polls, read results, run live sessions, and receive webhooks. Available on Pro and Enterprise plans.
Overview
The Developer API is a REST API under /api/v1. All requests and responses are JSON. The full, interactive contract lives in the API reference; this page gets you to your first successful call.
Authentication
Authenticate with a workspace API key sent as a Bearer token. Generate one in Studio → Developers (Pro/Enterprise). The secret is shown once - store it somewhere safe; if you lose it, revoke the key and create a new one.
Authorization: Bearer plv_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxKeys act with admin authority within their workspace. Never expose a key in client-side code - call the API from your server.
You can also authenticate using an OAuth 2.0 Client Credentials access token for machine-to-machine integrations.
Quickstart
Create a poll, publish it, then read results:
curl -X POST https://pollslive.com/api/v1/polls \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Lunch vote",
"content": {
"questions": [{
"id": "q1", "kind": "multiple_choice", "text": "Where to?",
"type": "single",
"options": [
{ "id": "a", "label": "Tacos" },
{ "id": "b", "label": "Sushi" }
]
}]
}
}'curl -X PATCH https://pollslive.com/api/v1/polls/POLL_ID \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "publish": true }'curl https://pollslive.com/api/v1/polls/POLL_ID/results \
-H "Authorization: Bearer $POLLSLIVE_KEY"Question types
A poll is a deck of typed slides. Set kind on each slide. All seven kinds can be authored via the API:
multiple_choice-type(single/multiple),options[]. Votable async.quiz-options[],correctOptionIds[],timeLimitSec,points,speedBonus.scale-min,max,minLabel,maxLabel,statements[].ranking-options[](respondents order them).open_ended-maxLength,render(list/cloud).qa-moderated,allowUpvotes.content- display-only.
{
"questions": [
{
"id": "s1", "kind": "scale", "text": "How likely are you to recommend us?",
"min": 0, "max": 10, "minLabel": "Not likely", "maxLabel": "Very likely"
},
{
"id": "q2", "kind": "quiz", "text": "Capital of France?",
"options": [{ "id": "a", "label": "Paris" }, { "id": "b", "label": "Rome" }],
"correctOptionIds": ["a"], "timeLimitSec": 20, "points": 1000
},
{
"id": "q3", "kind": "ranking", "text": "Rank these features by importance",
"options": [
{ "id": "r1", "label": "Speed" },
{ "id": "r2", "label": "Reliability" },
{ "id": "r3", "label": "Price" }
]
},
{
"id": "q4", "kind": "open_ended", "text": "Any other feedback?",
"maxLength": 300, "render": "list"
},
{
"id": "q5", "kind": "qa", "text": "Questions for the presenter",
"moderated": true, "allowUpvotes": true
}
]
}Images, video & links
Any slide and any option can carry media. Upload or pass a URL to POST /media and embed the returned object. Images are downloaded, re-encoded to WebP, and re-hosted (smaller payloads). YouTube videos and web links keep their URL and render client-side, so you never pay to host video. You can also pass media: { type, url } inline on a slide/option and the API resolves it when you save.
# 1) Ingest an image (returns { data: { type, url, storedUrl, width, height } })
curl -X POST https://pollslive.com/api/v1/media \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "url": "https://example.com/cat.jpg" }'
# 2) Embed the storedUrl on a slide option
{ "id": "a", "label": "Option A", "media": { "type": "image", "url": "https://cdn.pollslive.com/media/abc.webp" } }
# Or pass the original URL inline - the API resolves it on save:
{ "id": "a", "label": "Option A", "media": { "type": "image", "url": "https://example.com/cat.jpg" } }
# YouTube on a question slide
{ "id": "q1", "kind": "content", "text": "Watch this first",
"media": { "type": "youtube", "url": "https://youtu.be/dQw4w9WgXcQ" } }Responses & results
multiple_choice slides take votes via POST /polls/{id}/votes. Every other kind takes async responses via POST /polls/{id}/responses - send the field matching the slide (scaleValue, rankingOrder, text, or optionIds). Read per-slide aggregates from GET /polls/{id}/results.
curl -X POST https://pollslive.com/api/v1/polls/POLL_ID/responses \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "voterId": "user-123", "slideId": "s1", "scaleValue": 9 }'curl -X POST https://pollslive.com/api/v1/polls/POLL_ID/responses \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{
"voterId": "user-123",
"slideId": "q3",
"rankingOrder": ["r2", "r1", "r3"]
}'curl -X POST https://pollslive.com/api/v1/polls/POLL_ID/responses \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "voterId": "user-123", "slideId": "q4", "text": "Great product!" }'Live sessions & session control
A live session lets you drive a poll in real time in front of an audience. Create a session via the API, share the PIN or QR code with your audience, then control pacing and results visibility with the control endpoint.
curl -X POST https://pollslive.com/api/v1/sessions \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "pollId": "POLL_ID" }'
# Response includes: id, pin, status, currentSlideIndex, hostToken (once)curl -X POST https://pollslive.com/api/v1/sessions/SESSION_ID/control \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{ "action": "next" }'All session control actions
POST to /sessions/{id}/control with one of these action strings:
| Action | Description |
|---|---|
start | Open the session and broadcast the first slide. |
next | Advance to the next slide. |
prev | Go back to the previous slide. |
goto | Jump to a specific slide - pass slideIndex (0-based) alongside action. |
showResults | Reveal live results for the current slide to participants. |
hideResults | Hide results (stop showing them to participants). |
lock | Lock answers - participants can no longer submit for the current slide. |
unlock | Unlock answers so participants can respond. |
end | Close the session. Participants see a results summary. |
{ "action": "goto", "slideIndex": 2 }AI branching
AI branching adds conditional skip logic to your poll with a plain-English instruction - no need to manually wire conditions. Pro and Enterprise only. The API attempts our AI provider (OpenRouter) and falls back to a rule-based parser when it's unavailable.
curl -X POST https://pollslive.com/api/v1/polls/POLL_ID/branch \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
-H "Content-Type: application/json" \
-d '{
"instruction": "If the respondent selects Yes on question 1, skip to question 3. Otherwise go to question 2."
}'
# Response: { data: { draftJson: { ... conditions: [...] }, source: "openai" | "rules" } }Exporting results
Download aggregate poll results as a UTF-8 RFC-4180 CSV file. Each row contains the slide number, question text, type, answer label, and count or value. Compatible with Excel, Google Sheets, and any CSV tool. Pro and Enterprise only.
curl https://pollslive.com/api/v1/polls/POLL_ID/export \
-H "Authorization: Bearer $POLLSLIVE_KEY" \
--output results.csv
# CSV structure:
# Slide, Question, Type, Answer / Label, Count / Value
# 1, "Where to?", multiple_choice, Tacos, 12
# 1, "Where to?", multiple_choice, Sushi, 8
# 2, "Rate your experience", scale, Average, 8.50
# 2, "Rate your experience", scale, Rating 10, 5Endpoints
- Polls -
GET/POST /polls,GET/PATCH/DELETE /polls/{id} - Results -
GET /polls/{id}/results,GET/POST /polls/{id}/responses - Votes -
GET/POST /polls/{id}/votes - Media -
POST /media - Sessions -
POST /sessions,GET /sessions/{id},POST /sessions/{id}/control - AI -
POST /polls/{id}/branch - Export -
GET /polls/{id}/export
See request/response shapes and try calls live in the API reference.
Errors & rate limits
Errors use a stable envelope:
{ "error": { "code": "not_found", "message": "Poll not found." } }Common codes: 401 invalid/missing key, 402 plan upgrade required, 404 not found, 409 conflict (e.g. already voted, poll closed), 422 validation failed, 429 rate limited.
Rate limits
The API allows 120 requests per minute per key for all endpoints. Media ingestion (POST /media) is separately capped at 30 requests per minute. Limits reset in 60-second sliding windows. HTTP 429 is returned when exceeded - back off and retry.
| Endpoint | Limit |
|---|---|
| All endpoints | 120 req / min |
POST /media | 30 req / min |
OAuth2 (Enterprise)
Enterprise workspaces can use OAuth2 client-credentials in place of static keys. This is rolling out - contact us to enable it for your account.
Still have questions?
Our team is happy to help.