API Reference
Programmatic access to your surveys, responses, and analyses via the SurveyThis REST API. Business plan required.
Authentication
All API v1 endpoints use API key authentication. Include your key in the x-api-key header:
curl -H "x-api-key: st_a1b2c3d4_e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8" \
https://surveythis-production.up.railway.app/api/v1/surveysAPI Key Format
Keys follow the format st_XXXX_YYYY where:
st_— Fixed prefix identifying SurveyThis keysXXXX— 8-character hex prefix (visible in dashboard)YYYY— 48-character hex secret
Creating API Keys
- Navigate to Dashboard → Settings.
- Find the API Access section (Business plan).
- Enter a name for the key and click Generate Key.
- Copy the key immediately — it's shown only once and cannot be retrieved later.
Security: API keys are stored as SHA-256 hashes. The full key is never stored and is only shown at creation time. Treat your API key like a password — never commit it to source control or expose it in client-side code.
Key Limits
- Up to 5 API keys per account
- Keys can be revoked at any time from the dashboard
- Each key tracks its last used timestamp
Scopes
API keys are created with specific scopes that limit what they can access:
| Scope | Grants Access To |
|---|---|
surveys:read | List and view surveys and their questions |
surveys:write | Create and modify surveys (reserved for future use) |
responses:read | View survey responses and individual answers |
analyses:read | View AI analysis results |
Default scopes when creating a key: surveys:read, responses:read.
Base URL
https://surveythis-production.up.railway.app/api/v1Error Handling
All errors return a JSON object with an error field:
{
"error": "Survey not found"
}| Status | Meaning |
|---|---|
200 | Success |
401 | Missing or invalid API key |
403 | Insufficient scope or plan doesn't include API access |
404 | Resource not found |
500 | Internal server error |
List Surveys
GET /api/v1/surveysScope: surveys:read
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
status | string | — | Filter by status: draft, active, paused, closed |
limit | integer | 50 | Number of surveys per page |
offset | integer | 0 | Pagination offset |
Response
{
"surveys": [
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Customer Satisfaction Q1 2026",
"description": "Quarterly CSAT survey",
"status": "active",
"share_id": "aBcDeFgHiJ",
"response_count": 347,
"settings": {
"theme_color": "#0d9488",
"show_progress_bar": true,
"show_question_numbers": true,
"thank_you_message": "Thank you for your feedback!"
},
"created_at": "2026-02-15T10:00:00.000Z",
"updated_at": "2026-03-01T12:00:00.000Z"
}
],
"limit": 50,
"offset": 0
}Example
# List active surveys
curl -H "x-api-key: st_a1b2c3d4_..." \
"https://surveythis-production.up.railway.app/api/v1/surveys?status=active"Get Survey
GET /api/v1/surveys/:idScope: surveys:read
Returns a single survey with its questions.
Response
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"title": "Customer Satisfaction Q1 2026",
"description": "Quarterly CSAT survey",
"status": "active",
"share_id": "aBcDeFgHiJ",
"response_count": 347,
"settings": { ... },
"created_at": "2026-02-15T10:00:00.000Z",
"updated_at": "2026-03-01T12:00:00.000Z",
"questions": [
{
"id": "question-uuid",
"type": "nps",
"text": "How likely are you to recommend us?",
"description": null,
"required": true,
"order_index": 0,
"options": null,
"settings": null
},
{
"id": "question-uuid-2",
"type": "textarea",
"text": "What could we improve?",
"description": "Be as specific as possible",
"required": false,
"order_index": 1,
"options": null,
"settings": null
}
]
}Get Responses
GET /api/v1/surveys/:id/responsesScope: responses:read
Returns all responses for a survey, including individual answers.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
completed | string | — | true or false to filter by completion status |
limit | integer | 100 | Responses per page |
offset | integer | 0 | Pagination offset |
Response
{
"responses": [
{
"id": "response-uuid",
"completed": true,
"respondent_email": null,
"metadata": {
"userAgent": "Mozilla/5.0...",
"referrer": ""
},
"started_at": "2026-03-01T11:55:00.000Z",
"completed_at": "2026-03-01T12:00:00.000Z",
"answers": [
{
"question_id": "question-uuid",
"value": "9",
"numeric_value": 9,
"selected_options": null
},
{
"question_id": "question-uuid-2",
"value": "The mobile app could use a dark mode.",
"numeric_value": null,
"selected_options": null
}
]
}
],
"total": 347,
"limit": 100,
"offset": 0
}Pagination Example
# Get first 100 completed responses
curl -H "x-api-key: st_a1b2c3d4_..." \
"https://surveythis-production.up.railway.app/api/v1/surveys/SURVEY_ID/responses?completed=true"
# Get next page
curl -H "x-api-key: st_a1b2c3d4_..." \
"https://surveythis-production.up.railway.app/api/v1/surveys/SURVEY_ID/responses?completed=true&offset=100"Get Analyses
GET /api/v1/surveys/:id/analysesScope: analyses:read
Returns all AI analysis results for a survey.
Response
{
"analyses": [
{
"id": "analysis-uuid",
"analysis_type": "summary",
"query": null,
"result": {
"executiveSummary": "...",
"keyFindings": [...],
"recommendations": [...],
"overallScore": 78
},
"tokens_used": 3847,
"created_at": "2026-03-01T13:00:00.000Z"
}
]
}The result field contains the full structured JSON output. See Analysis Types for the complete schema of each analysis type.
Code Examples
JavaScript / Node.js
const API_KEY = process.env.SURVEYTHIS_API_KEY;
const BASE = 'https://surveythis-production.up.railway.app/api/v1';
async function fetchSurveys() {
const res = await fetch(`${BASE}/surveys?status=active`, {
headers: { 'x-api-key': API_KEY }
});
const { surveys } = await res.json();
return surveys;
}
async function fetchResponses(surveyId) {
let allResponses = [];
let offset = 0;
while (true) {
const res = await fetch(
`${BASE}/surveys/${surveyId}/responses?completed=true&limit=100&offset=${offset}`,
{ headers: { 'x-api-key': API_KEY } }
);
const { responses, total } = await res.json();
allResponses.push(...responses);
if (allResponses.length >= total) break;
offset += 100;
}
return allResponses;
}
// Usage
const surveys = await fetchSurveys();
for (const survey of surveys) {
const responses = await fetchResponses(survey.id);
console.log(`${survey.title}: ${responses.length} responses`);
}Python
import requests
import os
API_KEY = os.environ['SURVEYTHIS_API_KEY']
BASE = 'https://surveythis-production.up.railway.app/api/v1'
HEADERS = {'x-api-key': API_KEY}
def get_surveys(status=None):
params = {'status': status} if status else {}
r = requests.get(f'{BASE}/surveys', headers=HEADERS, params=params)
r.raise_for_status()
return r.json()['surveys']
def get_responses(survey_id, completed_only=True):
responses = []
offset = 0
while True:
params = {'completed': 'true', 'limit': 100, 'offset': offset}
r = requests.get(
f'{BASE}/surveys/{survey_id}/responses',
headers=HEADERS, params=params
)
r.raise_for_status()
data = r.json()
responses.extend(data['responses'])
if len(responses) >= data['total']:
break
offset += 100
return responses
def get_analyses(survey_id):
r = requests.get(
f'{BASE}/surveys/{survey_id}/analyses',
headers=HEADERS
)
r.raise_for_status()
return r.json()['analyses']
# Usage
surveys = get_surveys(status='active')
for survey in surveys:
responses = get_responses(survey['id'])
analyses = get_analyses(survey['id'])
print(f"{survey['title']}: {len(responses)} responses, {len(analyses)} analyses")cURL
# List surveys
curl -s -H "x-api-key: $API_KEY" \
"$BASE/surveys" | jq '.surveys[] | {title, status, response_count}'
# Get responses as CSV-like output
curl -s -H "x-api-key: $API_KEY" \
"$BASE/surveys/SURVEY_ID/responses?completed=true" \
| jq -r '.responses[] | [.completed_at, (.answers[] | .value)] | @csv'Rate Limits
The API currently does not enforce strict rate limits, but we recommend:
- No more than 60 requests per minute
- Use pagination to avoid large responses
- Cache responses where possible
If you need higher throughput, contact priority support.