API Reference

Programmatically create surveys, launch them to targeted audiences, and export responses.

Base URL

https://api.chorusresearch.io

The Chorus Research API provides 12 endpoints across 6 groups:

  • Audiences — Browse available respondent panels
  • Surveys — Create and manage survey projects
  • Pricing — Estimate launch costs before committing
  • Launches — Two-step launch workflow with cost confirmation
  • Responses — Export collected survey data

Authentication

All API requests (except /health) require a Bearer token in the Authorization header. API keys are 40+ character strings with a 16-character prefix.

curl
curl -X GET https://api.chorusresearch.io/surveys \
  -H 'Authorization: Bearer YOUR_API_KEY'
JavaScript
const response = await fetch('https://api.chorusresearch.io/surveys', {
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json'
  }
});
const data = await response.json();

You can obtain an API key from the API Keys section in the left navigation menu of the Chorus Research app. Keep your key secret — do not expose it in client-side code or public repositories.

Rate Limiting

Requests are rate-limited per API key. Exceeding your limit returns a 429 Too Many Requests response.

  • Standard endpoints: 60 requests per minute
  • Launch endpoints: 10 requests per minute

Rate limit info is returned in response headers:

HeaderDescription
RateLimit-LimitMaximum requests per window
RateLimit-RemainingRequests remaining in current window
RateLimit-ResetUnix timestamp when the window resets

When rate limited, wait until the reset time before retrying:

429 Response
{
  "error": "Too Many Requests",
  "message": "Rate limit exceeded (60 req/min)",
  "code": "RATE_LIMIT_EXCEEDED",
  "limit": 60,
  "windowMs": 60000,
  "retryAfter": 60
}

Errors

The API returns consistent JSON error responses. Every error includes an error label, a human-readable message, and a machine-readable code.

Validation Error
{
  "error": "Bad Request",
  "message": "Survey name is required",
  "code": "VALIDATION_ERROR",
  "field": "name",
  "details": [
    {
      "field": "name",
      "message": "Survey name is required"
    }
  ]
}

Error Codes

HTTP StatusMeaning
400Bad Request — invalid parameters or body
401Unauthorized — missing or invalid API key
403Forbidden — token does not belong to you
404Not Found — resource does not exist
409Conflict — confirmation token already used
413Payload Too Large — request exceeds 10 MB
429Too Many Requests — rate limit exceeded
500Internal Server Error — something went wrong on our end

Audiences

Audiences represent respondent panels you can target when launching a survey. Each audience has its own cost-per-response, completion limits, and estimated fielding times.

GET/audiences

Returns all available audiences with targeting details, cost information, and estimated fielding times.

Requires authentication

curl
curl https://api.chorusresearch.io/audiences \
  -H 'Authorization: Bearer YOUR_API_KEY'
200Success
{
  "audiences": [
    {
      "id": "genpop",
      "name": "General Population",
      "description": "Adults 18+ in the United States",
      "costPerResponse": 1,
      "minTargetCompletes": 5,
      "maxTargetCompletes": 5000,
      "averageLengthOfInterview": 10,
      "targeting": {
        "age_min": 18,
        "age_max": 99,
        "country": "US"
      },
      "estimatedFieldingTime": {
        "50": "1-2 hours",
        "100": "2-4 hours",
        "500": "1-2 days",
        "1000": "2-3 days",
        "5000": "1-2 weeks"
      }
    }
  ],
  "count": 1
}
GET/audiences/{id}

Returns a single audience by its identifier.

Requires authentication

ParameterTypeRequiredDescription
idstringRequiredAudience identifier (e.g., "genpop")
curl
curl https://api.chorusresearch.io/audiences/genpop \
  -H 'Authorization: Bearer YOUR_API_KEY'
404Audience not found
{
  "error": "Not Found",
  "message": "Audience not found: invalid-id",
  "code": "AUDIENCE_NOT_FOUND"
}

Surveys

Create and manage survey projects. Surveys use the Chorus Research survey format for question definitions.

POST/surveys

Creates a new survey project. The survey body must follow the Chorus Research survey format.

Requires authentication

Request Body

ParameterTypeRequiredDescription
namestringRequiredSurvey name (1-255 characters)
surveyobjectRequiredSurvey definition in Chorus Research format (see Survey Format)
targetCompletesintegerOptionalTarget number of completed responses (min: 5, $5 minimum purchase)
descriptionstringOptionalSurvey description (max 1000 characters)
Request
{
  "name": "Customer Satisfaction Survey",
  "survey": {
    "title": "Customer Satisfaction Survey",
    "pages": [
      {
        "name": "page1",
        "elements": [
          {
            "type": "rating",
            "name": "satisfaction",
            "title": "How satisfied are you with our service?",
            "rateMax": 5
          }
        ]
      }
    ]
  },
  "targetCompletes": 100,
  "description": "Q1 2026 customer feedback survey"
}
201Survey created
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "name": "Customer Satisfaction Survey",
  "survey": {
    "title": "Customer Satisfaction Survey",
    "pages": [
      "..."
    ]
  },
  "targetCompletes": 100,
  "description": "Q1 2026 customer feedback survey",
  "status": "draft",
  "createdAt": "2026-01-03T10:00:00.000Z",
  "updatedAt": "2026-01-03T10:00:00.000Z"
}
GET/surveys

Lists all surveys with pagination and optional filtering by status.

Requires authentication

Query Parameters

ParameterTypeRequiredDescription
statusstringOptionalFilter by status: draft, live, complete, cancelled
pageintegerOptionalPage number Default: 1
pageSizeintegerOptionalResults per page (max 100) Default: 20
sortBystringOptionalSort field: createdAt, updatedAt, name, status Default: createdAt
sortOrderstringOptionalSort direction: asc or desc Default: desc
curl
curl 'https://api.chorusresearch.io/surveys?status=live&page=1&pageSize=10' \
  -H 'Authorization: Bearer YOUR_API_KEY'
200Success
{
  "surveys": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "name": "Customer Satisfaction Survey",
      "targetCompletes": 100,
      "description": "Q1 2026 customer feedback",
      "status": "draft",
      "createdAt": "2026-01-03T10:00:00.000Z",
      "updatedAt": "2026-01-03T10:00:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 20,
    "total": 1,
    "totalPages": 1
  }
}
GET/surveys/{id}

Returns full survey details including the complete survey definition.

Requires authentication

ParameterTypeRequiredDescription
iduuidRequiredSurvey UUID
curl
curl https://api.chorusresearch.io/surveys/550e8400-e29b-41d4-a716-446655440000 \
  -H 'Authorization: Bearer YOUR_API_KEY'

Survey Format

The survey field in POST /surveys accepts a JSON object describing your survey's pages and questions. This section documents the full structure, all 10 supported question types, and the platform policies that Chorus Research applies automatically.

Platform Policies

  • All input questions are automatically set to isRequired: true at publish time (image elements are exempt).
  • Maximum 10 questions per survey (across all pages).
  • Maximum 100 pages per survey.
  • Maximum 50 elements per page.
  • Maximum 10 levels of nesting depth.
  • Maximum 5 MB total survey definition size.
  • All URLs must use HTTP or HTTPS schemes.

Survey Root Object

The survey object contains a pages array — an ordered list of survey pages, each containing question elements.

Page Object

ParameterTypeRequiredDescription
namestringRequiredUnique page identifier within the survey
titlestringOptionalPage title displayed to respondents
descriptionstringOptionalPage description displayed below the title
elementsarrayRequiredOrdered list of question elements on this page (max 50)
visibleIfstringOptionalExpression controlling page visibility, e.g. "{age} >= 18"

Common Question Properties

All question types share these base properties:

ParameterTypeRequiredDescription
typestringRequiredQuestion type: text, comment, radiogroup, checkbox, dropdown, tagbox, boolean, rating, ranking, image
namestringRequiredUnique identifier within the survey — used as the key in response data
titlestringOptionalQuestion text displayed to respondents. If omitted, name is displayed
descriptionstringOptionalHelp text displayed below the question title
isRequiredbooleanOptionalWhether the question requires an answer (auto-set to true at publish time) Default: true
visiblebooleanOptionalWhether the question is visible Default: true
visibleIfstringOptionalExpression controlling visibility, e.g. "{satisfaction} < 3"
enableIfstringOptionalExpression controlling whether the question is editable
defaultValueanyOptionalDefault value pre-filled for this question
validatorsarrayOptionalValidation rules applied to the response (see Validators)

text — Single-Line Input

Response format: string

ParameterTypeRequiredDescription
inputTypestringOptionalHTML input type: text, date, datetime-local, email, number, range, url Default: text
placeholderstringOptionalPlaceholder text shown when the input is empty
maxLengthintegerOptionalMaximum character length
minanyOptionalMinimum value (for number, range, date types)
maxanyOptionalMaximum value (for number, range, date types)

comment — Multi-Line Text Area

Response format: string

ParameterTypeRequiredDescription
placeholderstringOptionalPlaceholder text shown when the input is empty
rowsintegerOptionalNumber of visible text rows Default: 4
maxLengthintegerOptionalMaximum character length

radiogroup — Single Select

Response format: string (selected choice value)

ParameterTypeRequiredDescription
choicesarrayRequiredList of answer options (strings or {value, text} objects)
choicesOrderstringOptionalDisplay order: none, asc, desc, random Default: none
showOtherItembooleanOptionalShow an "Other" option with free-text input Default: false
otherTextstringOptionalLabel for the "Other" option

checkbox — Multi Select

Response format: array of strings

ParameterTypeRequiredDescription
choicesarrayRequiredList of answer options (strings or {value, text} objects)
choicesOrderstringOptionalDisplay order: none, asc, desc, random Default: none
showOtherItembooleanOptionalShow an "Other" option with free-text input Default: false
showNoneItembooleanOptionalShow a "None of the above" option that deselects all others Default: false

dropdown — Single Select Dropdown

Response format: string (selected choice value)

ParameterTypeRequiredDescription
choicesarrayRequiredList of answer options (strings or {value, text} objects)
placeholderstringOptionalPlaceholder text shown before selection Default: Select...
showOtherItembooleanOptionalShow an "Other" option with free-text input Default: false
showNoneItembooleanOptionalShow a "None" option Default: false

tagbox — Multi-Select Dropdown

Response format: array of strings

ParameterTypeRequiredDescription
choicesarrayRequiredList of answer options (strings or {value, text} objects)
placeholderstringOptionalPlaceholder text shown before selection
choicesOrderstringOptionalDisplay order: none, asc, desc, random Default: none

boolean — Yes / No Toggle

Response format: boolean

ParameterTypeRequiredDescription
labelTruestringOptionalLabel for the "true" option Default: Yes
labelFalsestringOptionalLabel for the "false" option Default: No

rating — Rating Scale

Response format: integer

ParameterTypeRequiredDescription
rateTypestringOptionalDisplay type: labels (numeric scale) or stars (star icons) Default: labels
rateMinintegerOptionalMinimum rating value Default: 1
rateMaxintegerOptionalMaximum rating value Default: 5
minRateDescriptionstringOptionalLabel at the minimum end of the scale
maxRateDescriptionstringOptionalLabel at the maximum end of the scale

ranking — Drag-and-Drop Ranking

Response format: array of strings (ranked order, first = highest)

ParameterTypeRequiredDescription
choicesarrayRequiredItems to rank (min 2). Strings or {value, text} objects

image — Display-Only Image

No response collected. Exempt from isRequired auto-enforcement.

ParameterTypeRequiredDescription
imageLinkstringOptionalURL of the image to display. Must use HTTP or HTTPS

Choice Items

Choices can be simple strings (value and display text are the same) or objects with separate value and text fields.

Choice Formats
// Simple strings — value and display text are the same
["Very Satisfied", "Satisfied", "Neutral", "Dissatisfied"]

// Objects — separate stored value and display text
[
  { "value": "very_satisfied", "text": "Very Satisfied" },
  { "value": "satisfied",      "text": "Satisfied" },
  { "value": "neutral",        "text": "Neutral" },
  { "value": "dissatisfied",   "text": "Dissatisfied" }
]

Validators

Optional validation rules that can be attached to any question via the validators array:

ParameterTypeRequiredDescription
typestringRequiredValidator type: numeric, text, regex, email, expression
textstringOptionalError message displayed when validation fails
minValuenumberOptionalMinimum allowed value (numeric validator)
maxValuenumberOptionalMaximum allowed value (numeric validator)
minLengthintegerOptionalMinimum text length (text validator)
maxLengthintegerOptionalMaximum text length (text validator)
regexstringOptionalRegular expression pattern (regex validator)

Complete Example

A multi-page survey demonstrating several question types with conditional logic:

Survey Definition
{
  "pages": [
    {
      "name": "experience",
      "elements": [
        {
          "type": "rating",
          "name": "overall_rating",
          "title": "How would you rate your overall experience?",
          "rateMin": 1,
          "rateMax": 5,
          "minRateDescription": "Very Poor",
          "maxRateDescription": "Excellent"
        },
        {
          "type": "dropdown",
          "name": "usage_frequency",
          "title": "How often do you use our product?",
          "choices": [
            "Daily",
            "Weekly",
            "Monthly",
            "Rarely"
          ]
        }
      ]
    },
    {
      "name": "details",
      "elements": [
        {
          "type": "checkbox",
          "name": "liked_features",
          "title": "Which features do you value most?",
          "choices": [
            "Speed",
            "Reliability",
            "Design",
            "Support",
            "Pricing"
          ],
          "showOtherItem": true
        },
        {
          "type": "ranking",
          "name": "improvement_priorities",
          "title": "Rank these areas by improvement priority",
          "choices": [
            "Performance",
            "Documentation",
            "Onboarding",
            "Pricing"
          ]
        }
      ]
    },
    {
      "name": "followup",
      "visibleIf": "{overall_rating} <= 3",
      "elements": [
        {
          "type": "comment",
          "name": "improvement_feedback",
          "title": "What could we do better?",
          "rows": 4,
          "maxLength": 2000
        },
        {
          "type": "boolean",
          "name": "contact_ok",
          "title": "May we follow up with you?",
          "labelTrue": "Yes, contact me",
          "labelFalse": "No thanks"
        }
      ]
    }
  ]
}

Pricing

Estimate survey costs before launching. Pricing is simple: $1.00 per completed response × target completes.

POST/pricing/estimate

Calculates the estimated cost to launch a survey based on audience and target completes.

Requires authentication

Request Body

ParameterTypeRequiredDescription
audienceIdstringRequiredTarget audience identifier
targetCompletesintegerRequiredDesired number of completed responses (min: 5, $5 minimum purchase)
projectIduuidRequiredSurvey project ID for the estimate
Request
{
  "audienceId": "genpop",
  "targetCompletes": 100,
  "projectId": "550e8400-e29b-41d4-a716-446655440000"
}
200Cost estimate with full breakdown
{
  "audienceId": "genpop",
  "targetCompletes": 100,
  "baseCostPerComplete": 1,
  "subtotal": 100,
  "total": 100,
  "currency": "USD"
}
GET/pricing/config

Returns the current pricing configuration including cost per response and currency.

Requires authentication

curl
curl https://api.chorusresearch.io/pricing/config \
  -H 'Authorization: Bearer YOUR_API_KEY'
200Pricing configuration
{
  "costPerResponse": 1,
  "currency": "USD"
}

Launches

Launching a survey is a two-step process to prevent accidental charges. First, request a cost estimate and receive a confirmation token. Then, confirm the launch with the token to execute payment and begin fielding.

Two-Step Launch Workflow

  1. Step 1: POST /surveys/{'{id}'}/launch → Get cost estimate + confirmation token (valid 5 min)
  2. Step 2: POST /surveys/{'{id}'}/launch/confirm → Execute launch with token + payment
POST/surveys/{id}/launch10 req/min

Step 1: Get a cost estimate and confirmation token for launching a survey. The token is valid for 5 minutes.

Requires authentication

Request Body

ParameterTypeRequiredDescription
iduuidRequiredSurvey UUID (path parameter)
audienceIdstringRequiredTarget audience identifier
targetCompletesintegerRequiredDesired number of completed responses (min: 5, $5 minimum purchase)
Request
{
  "audienceId": "genpop",
  "targetCompletes": 100
}
200Launch estimate with confirmation token
{
  "estimatedCost": 100,
  "breakdown": {
    "audienceId": "genpop",
    "targetCompletes": 100,
    "baseCostPerComplete": 1,
    "subtotal": 100,
    "total": 100,
    "currency": "USD"
  },
  "confirmToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "expiresAt": "2026-01-04T10:05:00.000Z"
}
POST/surveys/{id}/launch/confirm10 req/min

Step 2: Execute the launch using the confirmation token from Step 1. Charges payment and begins fielding.

Requires authentication

Request Body

ParameterTypeRequiredDescription
iduuidRequiredSurvey UUID (path parameter)
confirmTokenstringRequiredToken received from Step 1 (valid 5 minutes)
paymentMethodIdstringOptionalStripe payment method ID (uses default if omitted)
Request
{
  "confirmToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "paymentMethodId": "pm_1234567890abcdef"
}
200Launch successful
{
  "success": true,
  "publishedSurveyId": "660e8400-e29b-41d4-a716-446655440001",
  "workflowId": "launch-550e8400-...-1735992000000",
  "paymentIntentId": "pi_1234567890abcdef",
  "amountCharged": 100,
  "currency": "usd",
  "targetCompletes": 100
}
400Token expired
{
  "error": "Bad Request",
  "message": "Confirmation token has expired. Please request a new estimate.",
  "code": "INVALID_CONFIRM_TOKEN"
}
409Token already used (replay protection)
{
  "error": "Conflict",
  "message": "This confirmation token has already been consumed",
  "code": "TOKEN_ALREADY_CONSUMED"
}

Automatic refunds: If a launch fails after payment is charged, the charge is automatically refunded.

GET/surveys/{id}/status

Returns real-time fielding status including completion progress. Results are cached for 30 seconds.

Requires authentication

ParameterTypeRequiredDescription
iduuidRequiredSurvey UUID
curl
curl https://api.chorusresearch.io/surveys/550e8400-e29b-41d4-a716-446655440000/status \
  -H 'Authorization: Bearer YOUR_API_KEY'
200Fielding status
{
  "projectId": "550e8400-e29b-41d4-a716-446655440000",
  "publishedSurveyId": "660e8400-e29b-41d4-a716-446655440001",
  "status": "live",
  "completes": 45,
  "target": 100,
  "progress": 45,
  "lastUpdated": "2026-01-04T10:00:00.000Z",
  "estimatedCompletion": "2026-01-05T10:00:00.000Z"
}

Responses

Export collected survey responses with pagination. Only responses from published (launched) surveys are returned. By default, only completed responses are included.

GET/surveys/{id}/responses

Exports survey responses with pagination. Returns response data, metadata, and completion status.

Requires authentication

Query Parameters

ParameterTypeRequiredDescription
iduuidRequiredSurvey UUID (path parameter)
pageintegerOptionalPage number Default: 1
pageSizeintegerOptionalResults per page (max 1000) Default: 100
statusstringOptionalFilter by: complete, partial, incomplete Default: complete
curl
curl 'https://api.chorusresearch.io/surveys/550e8400-.../responses?page=1&pageSize=50' \
  -H 'Authorization: Bearer YOUR_API_KEY'
200Paginated responses
{
  "responses": [
    {
      "id": "770e8400-e29b-41d4-a716-446655440002",
      "publishedSurveyId": "660e8400-e29b-41d4-a716-446655440001",
      "responseData": {
        "satisfaction": "Very satisfied",
        "recommend": "Yes"
      },
      "metadata": {
        "rdud": "respondent-uuid-12345",
        "completedAt": "2026-01-04T09:30:00.000Z"
      },
      "status": "complete",
      "supplier": "cint",
      "createdAt": "2026-01-04T09:25:00.000Z",
      "completedAt": "2026-01-04T09:30:00.000Z"
    }
  ],
  "pagination": {
    "page": 1,
    "pageSize": 100,
    "total": 45,
    "totalPages": 1
  }
}

Pagination Example

For large datasets, iterate through pages until all responses are collected:

JavaScript
async function getAllResponses(surveyId, apiKey) {
  const responses = [];
  let page = 1;

  while (true) {
    const res = await fetch(
      `https://api.chorusresearch.io/surveys/${surveyId}/responses?page=${page}&pageSize=1000`,
      { headers: { Authorization: `Bearer ${apiKey}` } }
    );
    const data = await res.json();
    responses.push(...data.responses);

    if (page >= data.pagination.totalPages) break;
    page++;
  }

  return responses;
}

Health Check

The health endpoint is public and does not require authentication. Use it to verify API availability.

GET/health

Returns the service health status.

curl
curl https://api.chorusresearch.io/health
200Service is healthy
{
  "status": "healthy",
  "service": "public-api",
  "timestamp": "2026-01-04T10:00:00.000Z"
}