Skip to main content

Retrieve Real Time Survey Responses in Real Time

Here we show how to create an event listener in Qualtrics that will kick off a webhook callback when a survey response is received. That way the user can pull those responses immediately into whatever system they wish to plug them once they have been entered in Qualtrics.

APIs Used​

We use these APIs in the example below:

  • Retrieve Response
    • Retrieve response once response ID is obtained from Webhook callback.
  • Create Event Listener
    • Use curl or indicate that you want to listen for completed surveys or other events for this survey.
  • Events (aka Topics or Web Hooks are) listed here
    • There are lots of events, called topics, generated as surveys are filled out. The one we are interested here is surveyengine.completedResponse.{SurveyID} as that signals that a survey has been completed.

Create Event Subscription​

Event parameters can be wildcards, including the survey ID surveyengine.completedResponse.SV_ebdf20QralHTzQF.

curl -X POST -H 'X-API-TOKEN: yourapitoken'  -H 'Content-Type: application/json' -d '{
"topics": "surveyengine.completedResponse.SV_ebdf20QralHTzQF",
"publicationUrl": "http://samplelistener.com:8080",
"encrypt": false
}' 'https://co1.qualtrics.com/API/v3/eventsubscriptions/'


Fields Returned by Listener​

FieldDescription
Completed Date2019-03-29+16:00:00
StatusCompleted
ResponseIDR_2wi681bbsyaTItU
BrandIDsamplebrand
Topicsamplebrand.surveyengine.completedResponse.SV_ebdf20QralHTzQF
SurveyIDSV_ebdf20QralHTzQF

Create Web Listener​

When you create a listener you need to stand up a web server or write a program exposed to the internet that will receive Qualtrics events and then handle them. Below is a sample.

There are only a few requirements:

  • Program does not need to support SSL.
  • It should be on a publicly-available IP address, reachable by Qualtrics.com. For this example, we created a domain name. (The domain name is not necessary. You can use an IP address)
  • The web server URL must be able to handle HTTP POST of x-www-form-urlencoded data.

This Python code works by extending the BaseHTTPRequestHandler. We do that by creating a class called Handler class Handler(BaseHTTPRequestHandler) and then create a do_POST method there. The posted data is in scope in the self field.

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.

from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse
import urllib
import sys
import requests
import os
import simplejson as json

def getReponse(d,dataCenter,apiToken):
responseId = d['ResponseID']
surveyId = d['SurveyID']

headers = {
"content-type": "application/json",
"x-api-token": yourapitoken,
}

url = "https://{0}.qualtrics.com/API/v3/surveys/{1}/responses/{2}".format(dataCenter,surveyId,responseId)


rsp = requests.get(url, headers=headers)
print(rsp.json())


def parsey(c):
x=c.decode().split("&")
d = {}
for i in x:
a,b = i.split("=")
d[a] = b

d['CompletedDate'] = urllib.parse.unquote(d['CompletedDate'])

return d

class Handler(BaseHTTPRequestHandler):

# GET
def do_POST(self):
content_length = int(self.headers['Content-Length'])
post_data = self.rfile.read(content_length)
d = parsey(post_data)

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

getReponse(d,dataCenter,apiToken)

def run():

print('starting server...')
server_address = ('172.31.46.15', 8080)

httpd = HTTPServer(server_address, Handler)
print('running server...')
httpd.serve_forever()


try:
run()
except KeyboardInterrupt:
sys.exit(0)

Below is the response. The header is included so you can see that as well.

Eliminate Duplicates​

Note that the webhook callback can make two calls for every completed survey. You can mask that behavior by putting the responses into a Set data structure. Sets do not allow duplicates.

{
'meta': {
'requestId': 'c28bad3b-feb6-43cb-8975-1d20423d38be',
'httpStatus': '200 - OK'
},
'result': {
'labels': {
'QID2_DO': ['Coffee', 'Tea', 'Orange Juide'],
'QID1': 'Eggs',
'QID1_DO': ['Bacon', 'Eggs', 'Chocolate'],
'finished': 'True',
'status': 'Spam',
'QID2': 'Coffee'
},
'responseId': 'R_1QasAWMu5RPGgTs',
'displayedFields': ['QID1', 'QID2'],
'displayedValues': {
'QID1': [1, 2, 3],
'QID2': [1, 2, 3]
},
'values': {
'distributionChannel': 'anonymous',
'QID2_DO': ['1', '2', '3'],
'endDate': '2019-04-05T13:39:59Z',
'duration': 5,
'progress': 100,
'locationLongitude': '-73.5682983398',
'status': 8,
'QID2': 1,
'startDate': '2019-04-05T13:39:54Z',
'recordedDate': '2019-04-05T13:39:59.900Z',
'QID1': 2,
'locationLatitude': '45.5115051270',
'ipAddress': '70.80.9.27',
'userLanguage': 'EN',
'QID1_DO': ['1', '2', '3'],
'finished': 1
}
}
}