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
| Field | Description |
|---|---|
| Completed Date | 2019-03-29+16:00:00 |
| Status | Completed |
| ResponseID | R_2wi681bbsyaTItU |
| BrandID | samplebrand |
| Topic | samplebrand.surveyengine.completedResponse.SV_ebdf20QralHTzQF |
| SurveyID | SV_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
}
}
}