Skip to main content

Survey Response Import Guide

Here we show how to import survey responses into Qualtrics. The assumption is that you have created the survey in Qualtrics and have created some other mechanism to gather responses. But you need to use a specific format in order to import those responses into Qualtrics.

There is not an API to give the survey structure in the format required for the input. So the procedure is to download a .csv file of the survey from the Data and Analysis screen to get the structure. Then you will have to write a program to provide responses in the same format and then call the Survey Response Import API to load those into Qualtrics.

Download Survey Structure​

If you have no responses to your survey yet, you need to complete the survey at least once on Qualtrics.com. Once you have one or more responses to the survey, you can download them in .csv format. This will provide you the structure you need for the import. The first three lines are required headers. Lines after that are data. You can use the structure of the .csv. to see how to format the responses you wish to import.

Screen Shot 2019-02-24 at 13.55.22.png

Pick the CSV button and then click Download-

Screen Shot 2019-02-15 at 11.35.23.png

The resulting file will look something like this. Next import this into Google Sheets or Microsoft Excel so that you can more easily see the columns.

StartDate,EndDate,Status,IPAddress,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,RecipientLastName,RecipientFirstName,RecipientEmail,ExternalReference,LocationLatitude,LocationLongitude,DistributionChannel,UserLanguage,Q1_1,Q1_2,Q1_3,Q3
Start Date,End Date,Response Type,IP Address,Progress,Duration (in seconds),Finished,Recorded Date,Response ID,Recipient Last Name,Recipient First Name,Recipient Email,External Data Reference,Location Latitude,Location Longitude,Distribution Channel,User Language,Are you happy with our service? - Delivery was good,Are you happy with our service? - Price is good,Are you happy with our service? - Product arrived undamaged,What is your favorite food?
"{""ImportId"":""startDate"",""timeZone"":""Europe/Berlin""}","{""ImportId"":""endDate"",""timeZone"":""Europe/Berlin""}","{""ImportId"":""status""}","{""ImportId"":""ipAddress""}","{""ImportId"":""progress""}","{""ImportId"":""duration""}","{""ImportId"":""finished""}","{""ImportId"":""recordedDate"",""timeZone"":""Europe/Berlin""}","{""ImportId"":""_recordId""}","{""ImportId"":""recipientLastName""}","{""ImportId"":""recipientFirstName""}","{""ImportId"":""recipientEmail""}","{""ImportId"":""externalDataReference""}","{""ImportId"":""locationLatitude""}","{""ImportId"":""locationLongitude""}","{""ImportId"":""distributionChannel""}","{""ImportId"":""userLanguage""}","{""ImportId"":""QID1_1""}","{""ImportId"":""QID1_2""}","{""ImportId"":""QID1_3""}","{""ImportId"":""QID3""}"
2018-08-08 13:19:59,2018-08-08 13:20:07,IP Address,213.170.48.166,100,8,True,2018-08-08 13:20:07,R_XSAgJXJtB6JykOR,,,,,40.416702270508,-3.6838073730469,anonymous,EN,Strongly Agree,Agree,Neither agree nor disagree,

Import Structure into Spreadsheet to View more Clearly.​

There are too many columns to show on one screen here, so we show them using several images.

Screen Shot 2019-02-24 at 14.07.17.png

Screen Shot 2019-02-24 at 14.08.15.png

Screen Shot 2019-02-24 at 14.08.57.png

The columns A through Q are the same for all surveys. The next columns are questions. Below is a survey with one matrix type question and one multiple choice.

You’ll notice the first question here is a matrix table, so the data is actually subdivided into four separate columns, one for each of the three matrix statements and one for the simpler question.

  1. Delivery Was Good
  2. Price is Good
  3. Product Arrived Undamaged
  4. What is your Favorite Food

In the spreadsheet these are shown as:

Q1_1, Q1_2, Q1_3, and Q3.

Screen Shot 2019-02-24 at 14.09.34.png

Here is what that survey looks like in Qualtrics.

Screen Shot 2019-02-24 at 13.16.44.png

Python Code to Generate Responses​

You can use this program to debug your own.

The code below reads the survey definition and then randomly picks from the possible answers to fill in these columns. For columns A through Q it generates a random value bases upon the column name and type.

To run this:

  1. Create a config file called config.yaml with the surveyId and put it in the same folder as the Python code.

  2. Rename the downloaded file, which is long and has spaces, survey.csv and put it in the same folder as the Python code.

  3. Run the program with the command line argument (survey downloaded csv) (number of records to create).. It will create the output in a file calls responses.csv.

  4. Import response.csv with this curl command:

curl -X POST -H 'X-API-TOKEN: <API Key>' 
-H 'Content-Type: text/csv; charset=UTF-8'
--data-binary '@responses.csv'
'https://co1.qualtrics.com/API/v3/surveys/(survey_ID)/import-responses'

It will respond like this. Notice the progress ID as you will need that below to retrieve the status.

{"result":{"progressId":"31c4bf2f-3a77-4ab3-98e1-8ecc147ab16d","percentComplete":0.0,"status":"inProgress"},"meta":{"requestId":"f636820d-13cf-4b54-b16f-064cc8a65673","httpStatus":"200 - OK"}}
surveyId: "SV_9HX9sLOh2vPCMZf"

Code and Qualtrics Support​

Our support team does not offer assistance or consultation on custom coding, but you can always try asking our community of dedicated users. If you’d like to know more about our custom coding services you can commission, please contact your Qualtrics Account Executive.

import string
import yaml
import os
import requests
import simplejson as json
from datetime import timedelta
import datetime
import time
import random
import struct
import socket
import sys

def strGenerator(size):
return ''.join(random.choice(string.ascii_lowercase) for _ in range(size))

def genAnswer(questionNumber,survey):
for s in survey:
for q in s.getQuestionNumber():
if (questionNumber == q):
possibleAnswers=s.getChoiceText()
return possibleAnswers[random.randint(0,len(possibleAnswers)-1)]

def addHeader(line1,line2,line3,f):

l = line1.strip('\').split(",")
row = []
f.write(line1)
f.write(line2)
f.write(line3)

def addColumns():

row = []

# StartDate
row.append((datetime.datetime.now() + timedelta(days=random.randint(-20, -10))).strftime('%Y-%m-%d %H:%M:%S'))

# EndDate
row.append((datetime.datetime.now() + timedelta(days=random.randint(-10, -1))).strftime('%Y-%m-%d %H:%M:%S'))

# Status
row.append("IP Address")

# IPAddress
row.append("10." + str(random.randint(0,254)) + "." + str(random.randint(0,254)) + "." + str(random.randint(0,254)))

# Progress
row.append(100)

# Duration (in seconds)
row.append(random.randint(5, 35))

# Finished
row.append("TRUE")

# RecordedDate
row.append((datetime.datetime.now() + timedelta(days=random.randint(-10, -1))).strftime('%Y-%m-%d %H:%M:%S'))

# RespondId
row.append("RS_"+ strGenerator(14))

# RecipientFirstName

row.append(strGenerator(random.randint(3, 7)))

# RecipientLastName

row.append(strGenerator(random.randint(3, 7)))

# RecipientEmail

row.append(strGenerator(random.randint(3, 7)) + "qualtrics.com")

# ExternalReference
row.append("")

# LocationLatitude
row.append(random.uniform(-180,180))

# LocationLongitude
row.append(random.uniform(-180,180))

# DistributionChannel
row.append("anonymous")

# UserLanguage
row.append("EN")

return row

def writeRow(f,row):

line = ""
for r in row:
line=line + str(r) + ","

l = line[:-1]
l = l + '\'
f.write(l)

def addRows(line1,f,survey):

l = line1.strip('\').split(",")

row = addColumns()

for i in range(17,len(l)):
answer = genAnswer(l[i],survey)
row.append(answer)
writeRow(f,row)

class Question():


def __init__(self,questionId):
self.questionText = "";
self.questionId = questionId
self.questionNumber = []
self.choiceText = []
self.subQuestions = []

def addQuestionType(self,questionType):
self.questionType = questionType['type']

def addQuestionText(self,questionText):
self.questionText = questionText

def addChoiceText(self,choiceText):
for k,v in choiceText.items():
self.choiceText.append(v['description'])

def addQuestionNumber(self,exportColumnMap):
for k,v in exportColumnMap.items():
if v['question'] == self.questionId:
self.questionNumber.append(k)

def addSubQuestions(self,subQuestions):
for k,v in subQuestions.items():
self.subQuestions.append(v['description'])

def getQuestionText(self):
return self.questionText

def getChoiceText(self):
return self.choiceText

def getQuestionNumber(self):
return self.questionNumber

def getQuestionId(self):
return self.questionId

def getQuestionType(self):
return self.questionType

def getSubQuestions(self):
return self.subQuestions

def getQuestion(self):
return self

def readStructure(surveyId, apiToken, dataCenter):

headers = {'X-API-TOKEN': apiToken}

url = "https://" + dataCenter + ".qualtrics.com/API/v3/surveys/" + surveyId

response = requests.get(url, headers=headers)

d = json.JSONDecoder()
res = d.decode(response.text)

r = res['result']


questions = r['questions']
exportColumnMap=r['exportColumnMap']

survey = []

for k, v in questions.items():
question = Question(k)
question.addQuestionNumber(exportColumnMap)
question.addChoiceText(v['choices'])
survey.append(question)
question.addQuestionType(v['questionType'])
if question.getQuestionType() == "Matrix":
question.addSubQuestions(v['subQuestions'])

return survey

def main():

try:
apiToken = os.environ['APIKEY']
dataCenter = os.environ['DATACENTER']
except KeyError:
print("set environment variables APIKEY and DATACENTER")
sys.exit(2)

try:
numLines = sys.argv[2]
file = sys.argv[1]
except IndexError:
print ("usage: <survey file>, <number of responses to generate>")
sys.exit(2)

config = yaml.load(open('config.yaml'))
surveyId = config['surveyId']
survey = readStructure(surveyId, apiToken, dataCenter)

i = open(file,"r")
lines = i.readlines()
i.close()

out = open("responses.csv","w")

addHeader(lines[0],lines[1],lines[2],out)

for i in range(1,int(numLines)):
addRows(lines[0],out,survey)

out.close()


if __name__ == "__main__":
main()


Creates this file:

StartDate,EndDate,Status,IPAddress,Progress,Duration (in seconds),Finished,RecordedDate,ResponseId,RecipientLastName,RecipientFirstName,RecipientEmail,ExternalReference,LocationLatitude,LocationLongitude,DistributionChannel,UserLanguage,Q1_1,Q1_2,Q1_3,Q3
Start Date,End Date,Response Type,IP Address,Progress,Duration (in seconds),Finished,Recorded Date,Response ID,Recipient Last Name,Recipient First Name,Recipient Email,External Data Reference,Location Latitude,Location Longitude,Distribution Channel,User Language,Are you happy with our service? - Delivery was good,Are you happy with our service? - Price is good,Are you happy with our service? - Product arrived undamaged,What is your favorite food?
"{""ImportId"":""startDate"",""timeZone"":""Europe/Berlin""}","{""ImportId"":""endDate"",""timeZone"":""Europe/Berlin""}","{""ImportId"":""status""}","{""ImportId"":""ipAddress""}","{""ImportId"":""progress""}","{""ImportId"":""duration""}","{""ImportId"":""finished""}","{""ImportId"":""recordedDate"",""timeZone"":""Europe/Berlin""}","{""ImportId"":""_recordId""}","{""ImportId"":""recipientLastName""}","{""ImportId"":""recipientFirstName""}","{""ImportId"":""recipientEmail""}","{""ImportId"":""externalDataReference""}","{""ImportId"":""locationLatitude""}","{""ImportId"":""locationLongitude""}","{""ImportId"":""distributionChannel""}","{""ImportId"":""userLanguage""}","{""ImportId"":""QID1_1""}","{""ImportId"":""QID1_2""}","{""ImportId"":""QID1_3""}","{""ImportId"":""QID3""}"
2019-02-06 16:03:46,2019-02-20 16:03:46,IP Address,75.158.16.92,100,22,TRUE,2019-02-20 16:03:46,RS_tsgzpgpumptvor,cut,lkg,ealqualtrics.com,,-73.4154993016592,-158.406828770645,anonymous,EN,Disagree,Somewhat disagree,Somewhat agree,rice

Get Import Progress​

Having loaded the responses.csv as shown below, note the progress ID and then use the next curl to retrieve the progress.

curl -X POST -H 'X-API-TOKEN: <API Token>' 
-H 'Content-Type: text/csv; charset=UTF-8'
--data-binary '@responses.csv' 'https://co1.qualtrics.com/API/v3/surveys/SV_5gX7HWxwYs6Jt3f/import-responses'
{
"result": {
"progressId": "c9cccfe6-f05a-4248-a9fb-c913c5dee2b7",
"percentComplete": 0,
"status": "inProgress"
},
"meta": {
"requestId": "f433c48d-0b7a-49b7-81c4-8724b787832d",
"httpStatus": "200 - OK"
}
}
curl -H 'X-API-TOKEN: <API Token>' 
'https://co1.qualtrics.com/API/v3/surveys/SV_5gX7HWxwYs6Jt3f/import-responses/c9cccfe6-f05a-4248-a9fb-c913c5dee2b7'

Will give a response:

{
"result": {
"percentComplete": 100,
"status": "success"
},
"meta": {
"requestId": "67769750-1fb0-4535-8cd9-de5c74577b66",
"httpStatus": "200 - OK"
}
}

If you encounter a failing call​

The requestId is unique to the call that was executed. If you contact support about an error, please be sure to provide the requestId and the timestamp of the request so that they can help investigate the reason for the call failure.

If you to go back and look at the Data and Analysis screen you would see now that any records you have imported will say imported in the Response Type field:

Screen Shot 2019-02-24 at 16.10.05.png