Skip to content

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/surveys

API Key Format

Keys follow the format st_XXXX_YYYY where:

  • st_ — Fixed prefix identifying SurveyThis keys
  • XXXX — 8-character hex prefix (visible in dashboard)
  • YYYY — 48-character hex secret

Creating API Keys

  1. Navigate to Dashboard → Settings.
  2. Find the API Access section (Business plan).
  3. Enter a name for the key and click Generate Key.
  4. 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:

ScopeGrants Access To
surveys:readList and view surveys and their questions
surveys:writeCreate and modify surveys (reserved for future use)
responses:readView survey responses and individual answers
analyses:readView AI analysis results

Default scopes when creating a key: surveys:read, responses:read.

Base URL

https://surveythis-production.up.railway.app/api/v1

Error Handling

All errors return a JSON object with an error field:

{
  "error": "Survey not found"
}
StatusMeaning
200Success
401Missing or invalid API key
403Insufficient scope or plan doesn't include API access
404Resource not found
500Internal server error

List Surveys

GET /api/v1/surveys

Scope: surveys:read

Query Parameters

ParameterTypeDefaultDescription
statusstringFilter by status: draft, active, paused, closed
limitinteger50Number of surveys per page
offsetinteger0Pagination 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/:id

Scope: 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/responses

Scope: responses:read

Returns all responses for a survey, including individual answers.

Query Parameters

ParameterTypeDefaultDescription
completedstringtrue or false to filter by completion status
limitinteger100Responses per page
offsetinteger0Pagination 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/analyses

Scope: 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.