Get survey answers for a respondent
Route: https://api.conjoint.ly/api/experiments/experimentId/quality-checker/survey-answers/participantId
Action: GET
Returns the answers that one participant (whose ID is participantId) gave to the survey questions in an experiment (whose ID is experimentId). This is the same data shown in the Quality Checker view.
Path parameters
| Parameter | Description |
|---|---|
experimentId | Numerical ID of the experiment. You can find it in the URL of the experiment in Conjointly, or by listing your experiments. |
participantId | Numerical ID of the participant whose answers you want to retrieve. You can find it on the Participants page or in exported respondent data. |
Both IDs are numeric, but insert them into the URL exactly as they appear. Do not round or reformat them, and in languages where large numbers become floating point values, keep them as strings.
No query string or request body is required. Send the standard authentication headers with the request.
Response
The response contains a data array with one object per recorded question, in survey order. Every survey element the respondent passed through is included, not only questions that ask for an answer, so intro screens and conjoint blocks appear alongside ordinary questions.
| Field | Description |
|---|---|
id | Numerical ID of this answer record. |
additionalQuestionId | Numerical ID of the survey question that was answered. This is the same across all respondents, so use it to match answers to the same question. |
participantId | Numerical ID of the participant, matching the participantId in the request. |
order | Position of the element in the respondent’s survey. Values can have gaps, because elements the respondent never reached, for example those skipped by display logic, are not recorded and so are not returned. |
questionType | Type of the survey element (see below). This determines whether the answer arrives in items or in answer. |
duration | Time the respondent spent on the question, in seconds, returned as a string (for example "8.457"). Can be null, for example on conjoint blocks. |
questionText | Text of the question as shown to the respondent. Often contains HTML markup, such as the wrapping <p> tags, so strip or render it before displaying it elsewhere. |
deletable | Whether this answer record can be deleted. |
items | Array of the answer options, for question types that offer options (see below). Absent for other types. |
answer | The answer as a single string, for question types that do not offer options (see below). Absent for option-based types. |
Two answer shapes
An object carries either items or answer, depending on questionType. Do not assume items is always present.
Option-based questions (for example multiple) return an items array, with one object per answer option:
| Field | Description |
|---|---|
id | Numerical ID of the answer option. |
order | Position of the option within the question. |
answer | Text of the answer option. |
value | Whether the respondent selected this option: 1 if selected, 0 if not. |
Everything else returns a single answer string. What that string holds depends on the type:
questionType | Example answer | Meaning |
|---|---|---|
capture | "orange again" | The respondent’s open-text answer. |
nps | "5 / 10" | The rating chosen on a Net Promoter Score question, followed by the top of the scale. Take the number before the slash to get the score. |
intro | "1 second" | Time spent on an intro screen, which takes no answer. |
conjoint | "12 choice sets. 29 seconds total." | A summary of the conjoint block. Use the respondent information export to get the underlying choices. |
These are examples rather than a complete list of question types. When you build against this endpoint, make one test call with a real participant ID and check which shape each of your own questions returns.
Sample response
This respondent passed an intro screen, answered two multiple-choice questions and an NPS question, worked through a conjoint block, and finished with an open-text question. Note the null duration on the conjoint block, the HTML in questionText, and the gap in order where elements were not reached.
{
"data": [
{
"id": 1523491200,
"additionalQuestionId": 5592533,
"participantId": 269265040,
"order": 1,
"questionType": "intro",
"duration": "1.000",
"questionText": "<p>Welcome to this study. We appreciate your participation.</p>",
"deletable": true,
"answer": "1 second"
},
{
"id": 2045437398,
"additionalQuestionId": 6681045,
"participantId": 269265040,
"order": 2,
"questionType": "multiple",
"duration": "8.457",
"questionText": "How familiar are you with Product A?",
"deletable": true,
"items": [
{"id": 18557025, "order": 1, "answer": "I've never heard of it", "value": 0},
{"id": 18557026, "order": 2, "answer": "I've heard of it but never used it", "value": 1},
{"id": 18557027, "order": 3, "answer": "I've tried it once or twice", "value": 0},
{"id": 18557028, "order": 4, "answer": "I use it occasionally", "value": 0},
{"id": 18557029, "order": 5, "answer": "I use it regularly", "value": 0}
]
},
{
"id": 2045437401,
"additionalQuestionId": 6681048,
"participantId": 269265040,
"order": 5,
"questionType": "multiple",
"duration": "3.515",
"questionText": "Would you consider buying Product A in the next 12 months?",
"deletable": true,
"items": [
{"id": 18557041, "order": 1, "answer": "Yes", "value": 0},
{"id": 18557042, "order": 2, "answer": "No", "value": 0},
{"id": 18557043, "order": 3, "answer": "I'm not sure / No opinion", "value": 1}
]
},
{
"id": 1523491203,
"additionalQuestionId": 6733981,
"participantId": 269265040,
"order": 6,
"questionType": "nps",
"duration": "1.393",
"questionText": "<p>How likely are you to recommend this company to your friend or colleague?</p>",
"deletable": true,
"answer": "5 / 10"
},
{
"id": 1523491201,
"additionalQuestionId": 5592534,
"participantId": 269265040,
"order": 12,
"questionType": "conjoint",
"duration": null,
"questionText": "Conjoint block",
"deletable": true,
"answer": "12 choice sets. 29 seconds total."
},
{
"id": 1523491202,
"additionalQuestionId": 5592535,
"participantId": 269265040,
"order": 13,
"questionType": "capture",
"duration": "7.000",
"questionText": "<p>Would you like to share any other thoughts about this product?</p>",
"deletable": true,
"answer": "orange again"
}
]
}
Example
Here is how to use R to get the survey answers of one respondent. Because the two answer shapes do not fit a single set of columns, the function returns a list with one element per question rather than a table:
CONJOINTLY_TOKEN <- "YOUR_TOKEN" # Get a token from https://run.conjoint.ly/utilities/tokens
getSurveyAnswers <- function(experiment_id, participant_id, TOKEN = CONJOINTLY_TOKEN) {
library(httr)
headers <- add_headers(
`Authorization` = paste("Bearer", TOKEN),
`Content-Type` = "application/json",
`Accept` = "application/json",
`X-Requested-With` = "XMLHttpRequest"
)
request <- GET(
sprintf(
"https://api.conjoint.ly/api/experiments/%s/quality-checker/survey-answers/%s",
experiment_id, # Keep IDs as they are: do not coerce them to numeric
participant_id
),
headers
)
stop_for_status(request) # Fail loudly on an invalid token or unknown IDs
result <- content(request, "parsed", encoding = "UTF-8")
# A participant with no recorded answers returns an empty `data` array,
# in which case this yields an empty list:
lapply(result$data, function(question) {
# Presence of `items` is what marks an option-based question, so test for the
# field rather than its length: an option list can legitimately be empty.
if (!is.null(question$items)) {
# Option-based question: keep every option the respondent selected.
selected <- Filter(function(item) item$value == 1, question$items)
answers <- vapply(selected, `[[`, character(1), "answer")
} else {
# Everything else carries a single `answer` string, which can be absent.
answers <- if (is.null(question$answer)) character() else question$answer
}
list(
questionId = question$additionalQuestionId,
order = question$order,
questionType = question$questionType,
# `duration` is null on some elements, such as conjoint blocks:
duration = if (is.null(question$duration)) NA_real_ else as.numeric(question$duration),
questionText = question$questionText, # May contain HTML markup
answers = answers # Character vector: empty, one, or several values
)
})
}
answers <- getSurveyAnswers("123456", "1234567") # Replace with your experiment and participant IDs
# Pick out a single element by its sequential order number, which is unique
# within one response. Here, order 13 is the open-text question:
Filter(function(question) question$order == 13, answers)[[1]]$answers
When you compare the same question across respondents, or store answers between waves, match on questionId (the additionalQuestionId from the response) instead. Unlike order, it does not shift when the questionnaire is edited.
Related requests
Two sibling endpoints return the rest of the Quality Checker data for the same participant:
https://api.conjoint.ly/api/experiments/experimentId/quality-checker/locations/participantIdreturns the recorded locations.https://api.conjoint.ly/api/experiments/experimentId/quality-checker/risk-signals/participantIdreturns the risk signals.
See Get respondent information for an R example that calls all three at once, or Export respondent information to get answers for every respondent in one file.