Viewpoints AI Studies API
Create studies and retrieve results
Create studies and retrieve results
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/viewpoints-ai-studies-api"
curl "https://apis.io/api/v1/apis?limit=25"
Discovery needs no key. Ratings and market analysis are Pro.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
description: 'The Viewpoints Study API allows you to programmatically create and retrieve AI-powered research studies with simulated participants.
## Authentication
- **API Key** — pass your key in the `X-API-Key` header
To obtain an API key, visit your account settings in the Viewpoints dashboard.
## Workflow
1. **Upload stimuli** (optional, not needed for text only stimuli) — use the presigned URL endpoint to upload images, video, audio, or PDFs to S3
2. **Create a study** — submit your study configuration with audience segments, assigned materials, and questions
3. **Poll for completion** — use the job status endpoint to track progress and get completed study ID
4. **Retrieve results** — once complete, fetch the full study results with all participant responses using the study ID'
title: Viewpoints Study Studies API
version: 1.0.0
servers:
- url: https://api.viewpoints.ai
description: Base URL declared by the provider in apis.yml (roadmap#122).
security:
- apiKey: []
- bearerAuth: []
tags:
- description: Create studies and retrieve results
name: Studies
paths:
/v1/studies:
post:
description: Launch a new study with the given parameters. Upload file stimuli via the presigned URL endpoint first and include the resulting S3 keys as URLs. Returns a job ID for tracking study creation progress via the job status endpoint.
operationId: post__v1_studies
parameters: []
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateStudyRequest'
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/CreateStudyResponse'
description: OK
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Validation error (invalid fields, too many characters, etc.)
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Authentication failed
'402':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Entitlement limit exceeded (participants, questions, or question type not allowed by current plan)
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Not authorized
summary: Create a new study
tags:
- Studies
/v1/studies/jobs/{job_id}:
get:
description: Poll the status of a study creation job. Returns `processing` while the study is being generated, `completed` with the study ID when done, or `failed` if an error occurred.
operationId: get__v1_studies_jobs_{job_id}
parameters:
- description: The job ID returned by the create study endpoint
in: path
name: job_id
required: true
schema:
type: string
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/JobStatusResponse'
description: Job completed or failed
'202':
content:
application/json:
schema:
$ref: '#/components/schemas/JobStatusResponse'
description: Job still processing
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Bad request
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Authentication failed
'404':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Job not found
summary: Get study creation job status
tags:
- Studies
/v1/studies/{id}:
get:
description: Retrieve study configuration and all participant responses.
operationId: get__v1_studies_{id}
parameters:
- description: The UUID of the study to retrieve
in: path
name: id
required: true
schema:
type: string
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/GetStudyResponse'
description: OK
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Bad request
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Authentication failed
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Not authorized to view this study
'404':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Study not found
summary: Get study results
tags:
- Studies
put:
description: 'Replace the full configuration of an existing draft study. The same `launch` flag accepted by POST /v1/studies controls whether this also enqueues the background job: launch=false keeps the study as a draft, launch=true flips it to running and starts execution. Returns 409 if the study has already been launched (status != ''draft''), or if `expected_version` is provided and another save has bumped the draft''s version since it was read.'
operationId: put__v1_studies_{id}
parameters:
- description: ''
in: path
name: id
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CreateStudyRequest'
required: true
responses:
'200':
content:
application/json:
schema:
$ref: '#/components/schemas/CreateStudyResponse'
description: OK
'400':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Validation error
'401':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Authentication failed
'402':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Entitlement limit exceeded
'403':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Not authorized
'404':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Study not found
'409':
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
description: Study is not a draft
summary: Replace a draft study
tags:
- Studies
components:
schemas:
TextCaptionItem:
description: Text stimulus to be included in the study
properties:
caption:
default: ''
title: Caption
type: string
content:
default: ''
title: Content
type: string
is_sequential:
default: false
title: Is Sequential
type: boolean
name:
title: Name
type: string
required:
- name
title: TextCaptionItem
type: object
StudyDetailsResponse:
properties:
added_captions:
items:
$ref: '#/components/schemas/CaptionResponse'
title: Added Captions
type: array
age_parameters:
$ref: '#/components/schemas/AgeParametersItem'
assigned_materials:
items:
$ref: '#/components/schemas/AssignedMaterialsItem'
title: Assigned Materials
type: array
jury_panel_size:
anyOf:
- type: integer
- type: 'null'
default: null
title: Jury Panel Size
log_to_memory:
default: true
title: Log To Memory
type: boolean
num_participants_in_paper:
anyOf:
- type: integer
- type: 'null'
default: null
title: Num Participants In Paper
number_participants:
title: Number Participants
type: integer
persona_context:
default: ''
title: Persona Context
type: string
question_blocks:
anyOf:
- additionalProperties: true
type: object
- type: 'null'
default: null
title: Question Blocks
question_groups:
items:
$ref: '#/components/schemas/QuestionItem'
title: Question Groups
type: array
research_goal:
default: ''
title: Research Goal
type: string
segments:
items:
$ref: '#/components/schemas/SegmentItem'
title: Segments
type: array
stimuli_prompt_append:
default: ''
title: Stimuli Prompt Append
type: string
title:
title: Title
type: string
uploaded_audios:
anyOf:
- items:
$ref: '#/components/schemas/UploadedMediaItem'
type: array
- type: 'null'
default: null
title: Uploaded Audios
uploaded_images:
items:
$ref: '#/components/schemas/UploadedMediaItem'
title: Uploaded Images
type: array
uploaded_participants:
anyOf:
- items: {}
type: array
- type: 'null'
default: null
title: Uploaded Participants
uploaded_pdfs:
anyOf:
- items:
$ref: '#/components/schemas/UploadedMediaItem'
type: array
- type: 'null'
default: null
title: Uploaded Pdfs
uploaded_videos:
anyOf:
- items:
$ref: '#/components/schemas/UploadedMediaItem'
type: array
- type: 'null'
default: null
title: Uploaded Videos
required:
- title
- number_participants
- age_parameters
- assigned_materials
- uploaded_images
- added_captions
- segments
- question_groups
title: StudyDetailsResponse
type: object
AssignedMaterialsItem:
properties:
materials_name:
title: Materials Name
type: string
stimuli:
items:
$ref: '#/components/schemas/StimulusItem'
title: Stimuli
type: array
required:
- materials_name
- stimuli
title: AssignedMaterialsItem
type: object
MediaItem:
description: Media item that can be either a URL string or an object with url/type/name.
properties:
name:
default: ''
title: Name
type: string
type:
default: ''
title: Type
type: string
url:
title: Url
type: string
required:
- url
title: MediaItem
type: object
CaptionResponse:
properties:
caption:
default: ''
title: Caption
type: string
content:
title: Content
type: string
name:
title: Name
type: string
required:
- name
- content
title: CaptionResponse
type: object
QuestionItem:
additionalProperties: false
properties:
allowMultiple:
default: false
description: Only applies when scale is 'multiple_choice'. When false (default), each persona selects exactly one option and the response is a single string. When true, each persona may select one or more options and the response is a JSON array of strings.
title: Allowmultiple
type: boolean
choice_matrix_rows:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: Row labels for question_subtype 'choice_matrix'. Each row receives exactly one choice.
title: Choice Matrix Rows
choices:
anyOf:
- items:
type: string
type: array
- type: 'null'
default: null
description: Required when scale is 'multiple_choice' or 'allocation' (party names, minimum 2 required). The list of answer options.
title: Choices
name:
title: Name
type: string
question_subtype:
anyOf:
- type: string
- type: 'null'
default: null
description: Optional subtype for question variants. Use 'choice_matrix' with scale 'multiple_choice' to ask one single-select choice per row.
title: Question Subtype
questions:
items:
type: string
title: Questions
type: array
scale:
description: 'The response scale type. Either a point scale (2 - 100 points) in the format: ''<N> point scale (1 = <low label>; <N> = <high label>)'', or amount scale in the format: ''amount scale (<$ or £ or ¥><min amount> - <$ or £ or ¥><max amount>)'', or one of the keywords: ''qualitative'', ''multiple_choice'', ''allocation''.'
examples:
- qualitative
- multiple_choice
- 2 point scale (1 = No; 2 = Yes)
- 3 point scale (1 = 1 star; 3 = 3 stars)
- 5 point scale (1 = 1 star; 5 = 5 stars)
- 7 point scale (1 = Not at all appealing; 7 = Extremely appealing)
- 9 point scale (1 = Not at all likely; 9 = Extremely likely)
- 100 point scale (1 = Not at all likely; 100 = Extremely likely)
- amount scale ($5 - $100)
- amount scale (£20 - £200)
- amount scale ($)
- allocation
pattern: ^(?:qualitative|ranking|points_allocation|allocation|matrix|multiple_choice|(?:100|[1-9]\d|[2-9]) point scale \(1 = [^\r\n;]+; (?:100|[1-9]\d|[2-9]) = [^\r\n)]+\)|amount scale \(\$-?\d+(?:\.\d+)? - \$-?\d+(?:\.\d+)?\)|amount scale \(£-?\d+(?:\.\d+)? - £-?\d+(?:\.\d+)?\)|amount scale \(¥-?\d+(?:\.\d+)? - ¥-?\d+(?:\.\d+)?\)|amount scale \(\$\)|amount scale \(£\)|amount scale \(€\)|amount scale \(¥\))$
title: Scale
type: string
sequenceOpenTextMode:
anyOf:
- enum:
- per_stimulus
- overall
type: string
- type: 'null'
default: null
description: Only applies to qualitative questions in sequence studies. 'per_stimulus' asks once for each sequence item; 'overall' asks one overall open-ended question.
title: Sequenceopentextmode
required:
- name
- questions
- scale
title: QuestionItem
type: object
SegmentSubCategoryItem:
properties:
name:
title: Name
type: string
percentage:
title: Percentage
type: integer
required:
- name
- percentage
title: SegmentSubCategoryItem
type: object
CreateStudyResponse:
properties:
job_id:
anyOf:
- type: string
- type: 'null'
default: null
title: Job Id
study_id:
title: Study Id
type: string
version:
anyOf:
- type: integer
- type: 'null'
default: null
title: Version
required:
- study_id
title: CreateStudyResponse
type: object
JobStatusResponse:
properties:
analyze_done:
anyOf:
- type: integer
- type: 'null'
default: null
title: Analyze Done
analyze_item_elapsed_ms:
anyOf:
- type: integer
- type: 'null'
default: null
title: Analyze Item Elapsed Ms
analyze_item_expected_ms:
anyOf:
- type: integer
- type: 'null'
default: null
title: Analyze Item Expected Ms
analyze_total:
anyOf:
- type: integer
- type: 'null'
default: null
title: Analyze Total
analyze_video_seconds:
anyOf:
- type: integer
- type: 'null'
default: null
title: Analyze Video Seconds
error_message:
anyOf:
- type: string
- type: 'null'
default: null
title: Error Message
has_analyzing_media:
anyOf:
- type: boolean
- type: 'null'
default: null
title: Has Analyzing Media
has_enriched_context:
anyOf:
- type: boolean
- type: 'null'
default: null
title: Has Enriched Context
has_life_stories:
anyOf:
- type: boolean
- type: 'null'
default: null
title: Has Life Stories
has_media:
anyOf:
- type: boolean
- type: 'null'
default: null
title: Has Media
has_qualitative_questions:
anyOf:
- type: boolean
- type: 'null'
default: null
title: Has Qualitative Questions
jury_panel_size:
anyOf:
- type: integer
- type: 'null'
default: null
title: Jury Panel Size
media_done:
anyOf:
- type: integer
- type: 'null'
default: null
title: Media Done
media_item_elapsed_ms:
anyOf:
- type: integer
- type: 'null'
default: null
title: Media Item Elapsed Ms
media_item_expected_ms:
anyOf:
- type: integer
- type: 'null'
default: null
title: Media Item Expected Ms
media_total:
anyOf:
- type: integer
- type: 'null'
default: null
title: Media Total
number_participants:
anyOf:
- type: integer
- type: 'null'
default: null
title: Number Participants
phase:
anyOf:
- type: string
- type: 'null'
default: null
title: Phase
phase_entered_at_ms:
anyOf:
- type: integer
- type: 'null'
default: null
title: Phase Entered At Ms
progress:
anyOf:
- type: number
- type: 'null'
default: null
title: Progress
qual_questions_count:
anyOf:
- type: integer
- type: 'null'
default: null
title: Qual Questions Count
status:
enum:
- processing
- preparing_context
- completed
- failed
title: Status
type: string
study_id:
anyOf:
- type: string
- type: 'null'
default: null
title: Study Id
title:
anyOf:
- type: string
- type: 'null'
default: null
title: Title
required:
- status
title: JobStatusResponse
type: object
ParticipantResultItem:
properties:
assigned_materials:
title: Assigned Materials
type: string
id:
title: Id
type: integer
participant_assigned_materials:
items:
$ref: '#/components/schemas/AssignedMaterialsItem'
title: Participant Assigned Materials
type: array
participant_response:
additionalProperties:
anyOf:
- type: string
- type: integer
- type: number
- items: {}
type: array
- additionalProperties: true
type: object
- type: 'null'
title: Participant Response
type: object
public_participant_id:
anyOf:
- type: string
- type: 'null'
default: null
title: Public Participant Id
public_response_id:
title: Public Response Id
type: string
response_valid:
title: Response Valid
type: boolean
segments:
additionalProperties:
anyOf:
- type: string
- type: integer
- type: number
- items: {}
type: array
- additionalProperties: true
type: object
- type: 'null'
title: Segments
type: object
required:
- id
- public_response_id
- assigned_materials
- response_valid
- segments
- participant_assigned_materials
- participant_response
title: ParticipantResultItem
type: object
CreateStudyRequest:
examples:
- added_captions:
- content: A new smartphone with a 6.5 inch display...
name: Product description
age_parameters:
max_age: 65
min_age: 18
assigned_materials:
- materials_name: Control
stimuli:
- media:
- A new smartphone with a 6.5 inch display...
stimulus_name: Product description
stimulus_type: text
number_participants: 50
question_groups:
- name: Purchase Intent
questions:
- How likely are you to purchase this product?
scale: 7 point scale (1 = Not at all likely; 7 = Extremely likely)
research_goal: ''
segments:
- name: Gender
subcategories:
- name: Male
percentage: 50
- name: Female
percentage: 50
title: Example Study
uploaded_audios: []
uploaded_images: []
uploaded_pdfs: []
uploaded_videos: []
properties:
added_captions:
items:
$ref: '#/components/schemas/TextCaptionItem'
title: Added Captions
type: array
age_parameters:
$ref: '#/components/schemas/AgeParametersItem'
assigned_materials:
items:
$ref: '#/components/schemas/AssignedMaterialsItem'
title: Assigned Materials
type: array
expected_version:
anyOf:
- type: integer
- type: 'null'
default: null
title: Expected Version
generate_life_stories:
default: true
title: Generate Life Stories
type: boolean
launch:
default: true
title: Launch
type: boolean
number_participants:
maximum: 1000
minimum: 1
title: Number Participants
type: integer
persona_context:
default: ''
maxLength: 10000
title: Persona Context
type: string
question_groups:
items:
$ref: '#/components/schemas/QuestionItem'
title: Question Groups
type: array
research_goal:
default: ''
maxLength: 10000
title: Research Goal
type: string
segments:
items:
$ref: '#/components/schemas/SegmentItem'
title: Segments
type: array
title:
maxLength: 70
minLength: 1
title: Title
type: string
uploaded_audios:
items:
$ref: '#/components/schemas/UploadedMediaItem'
title: Uploaded Audios
type: array
uploaded_images:
items:
$ref: '#/components/schemas/UploadedMediaItem'
title: Uploaded Images
type: array
uploaded_pdfs:
items:
$ref: '#/components/schemas/UploadedMediaItem'
title: Uploaded Pdfs
type: array
uploaded_videos:
items:
$ref: '#/components/schemas/UploadedMediaItem'
title: Uploaded Videos
type: array
required:
- title
- uploaded_images
- uploaded_videos
- uploaded_audios
- uploaded_pdfs
- added_captions
- segments
- age_parameters
- assigned_materials
- question_groups
- number_participants
title: CreateStudyRequest
type: object
StimulusItem:
properties:
caption:
default: ''
title: Caption
type: string
materials_name:
anyOf:
- type: string
- type: 'null'
default: null
title: Materials Name
media:
description: Text stimulus, a URL string, or an object with url/type/name
items:
anyOf:
- type: string
- $ref: '#/components/schemas/MediaItem'
title: Media
type: array
stimulus_name:
title: Stimulus Name
type: string
stimulus_type:
enum:
- text
- image
- video
- audio
- pdf
title: Stimulus Type
type: string
required:
- stimulus_name
- stimulus_type
- media
title: StimulusItem
type: object
UploadedMediaItem:
description: Media stimulus to be included in the study
properties:
caption:
default: ''
title: Caption
type: string
is_sequential:
default: false
title: Is Sequential
type: boolean
name:
title: Name
type: string
url:
title: Url
type: string
required:
- name
- url
title: UploadedMediaItem
type: object
ErrorResponse:
properties:
details:
anyOf:
- type: string
- type: 'null'
default: null
title: Details
error:
title: Error
type: string
required:
- error
title: ErrorResponse
type: object
SegmentItem:
properties:
name:
title: Name
type: string
subcategories:
items:
$ref: '#/components/schemas/SegmentSubCategoryItem'
title: Subcategories
type: array
required:
- name
- subcategories
title: SegmentItem
type: object
GetStudyResponse:
properties:
completed_at:
anyOf:
- format: date-time
type: string
- type: 'null'
default: null
title: Completed At
created_at:
format: date-time
title: Created At
type: string
folder_id:
anyOf:
- type: integer
- type: 'null'
default: null
title: Folder Id
is_shared:
default: false
title: Is Shared
type: boolean
results:
items:
$ref: '#/components/schemas/ParticipantResultItem'
title: Results
type: array
status:
title: Status
type: string
study_details:
$ref: '#/components/schemas/StudyDetailsResponse'
user_id:
title: User Id
type: string
version:
default: 0
title: Version
type: integer
required:
- study_details
- results
- created_at
- status
- user_id
title: GetStudyResponse
type: object
AgeParametersItem:
properties:
max_age:
title: Max Age
type: integer
mean:
anyOf:
- type: number
- type: 'null'
default: null
title: Mean
min_age:
title: Min Age
type: integer
st_dev:
anyOf:
- type: number
- type: 'null'
default: null
title: St Dev
venue:
anyOf:
- maxLength: 200
type: string
- type: 'null'
default: null
title: Venue
required:
- min_age
- max_age
title: AgeParametersItem
type: object
securitySchemes:
apiKey:
in: header
name: X-API-Key
type: apiKey
bearerAuth:
bearerFormat: JWT
scheme: bearer
type: http