Survey Response Export Guide
This guide describes how to export survey responses with the new Qualtrics APIs. We show the process using both Python and Java examples. Unlike legacy endpoints, these new APIs offer a more efficient and reliable way to extract data, including additional fields not previously available.
Retrieving Survey Responses
The workflow to retrieve survey responses has four steps.
Step 1: Creating Data Export
To start the survey response export, first call the Create Response Export API. This API generates a file with the survey responses you request. You can apply various filters to the request to limit the data set and accelerate the process. Refer to Step 1: Creating Data Export in the Python code sample for guidance.
Step 2: Checking Data Export Progress
Monitor the export progress with the Get Response Export Progress API. The API will return an object containing a status field. Continue to poll this field until it displays the value complete. Include a delay between each request to avoid exceeding rate limits.
Step 3: Downloading File
After the export process is complete, look for the fileId field in the returned object from the Get Response Export Progress API. Use this fileId to retrieve the generated export file via the Get Response Export File API. The code samples that follow show an alternative method for exporting the file by creating a URL based on the id returned in step 1.
Step 4: Decompressing (Unzipping) File
Decompress the downloaded file to access the survey responses in the format you specified (CSV, TSV, or SPSS).
Sample Code
Python
Below is a Python code snippet for exporting survey responses. This code performs all the aforementioned steps.
If Using the Following Sample Code, use an Exponential Backoff Delay Algorithm
If you use the sample code without using an Exponential Backoff Delay algorithm, you risk exceeding your Qualtrics rate limits and failing other API calls.
import requests
import zipfile
import json
import io, os
import sys
# Setting user Parameters
try:
apiToken = os.environ['APIKEY']
except KeyError:
print("set environment variable APIKEY")
sys.exit(2)
surveyId = "SV_6hsqQZccKfnIKGN"
dataCenter = 'co1'
# Setting static parameters
requestCheckProgress = 0.0
progressStatus = "inProgress"
url = "https://{0}.qualtrics.com/API/v3/surveys/{1}/export-responses/".format(dataCenter, surveyId)
headers = {
"content-type": "application/json",
"x-api-token": apiToken,
}
# Step 1: Creating Data Export
data = {
"format": "csv",
"seenUnansweredRecode": 2
}
downloadRequestResponse = requests.request("POST", url, json=data, headers=headers)
print(downloadRequestResponse.json())
try:
progressId = downloadRequestResponse.json()["result"]["progressId"]
except KeyError:
print(downloadRequestResponse.json())
sys.exit(2)
isFile = None
max_retries = 5
retry_count = 0
# Step 2: Checking on Data Export Progress and waiting until export is ready
while progressStatus != "complete" and progressStatus != "failed" and isFile is None:
if isFile is None:
print("File not ready")
else:
print("ProgressStatus=", progressStatus)
requestCheckUrl = url + progressId
requestCheckResponse = requests.request("GET", requestCheckUrl, headers=headers)
try:
isFile = requestCheckResponse.json()["result"]["fileId"]
except KeyError:
1==1
print(requestCheckResponse.json())
requestCheckProgress = requestCheckResponse.json()["result"]["percentComplete"]
print("Download is " + str(requestCheckProgress) + " complete")
progressStatus = requestCheckResponse.json()["result"]["status"]
if progressStatus not in ["complete", "failed"]:
# Implement exponential backoff with a maximum number of retries
retry_count += 1
if retry_count > max_retries:
print("Exceeded maximum retries. Exiting.")
sys.exit(1)
# Calculate the next sleep interval using exponential backoff (The first retry will occur after 200 seconds, the second after 400 seconds, the third after 600 seconds, and so on, until the max_retries limit is reached)
sleep_interval = 200 ** retry_count
print(f"Retrying in {sleep_interval} seconds...")
time.sleep(sleep_interval)
# Step 3: Downloading file
requestDownloadUrl = url + fileId + '/file'
requestDownload = requests.request("GET", requestDownloadUrl, headers=headers, stream=True)
# Step 4: Unzipping the file
zipfile.ZipFile(io.BytesIO(requestDownload.content)).extractall("MyQualtricsDownload")
print('Complete')
If Using the Prior Sample Code, use an Exponential Backoff Delay Algorithm
If you use the sample code without using an Exponential Backoff Delay algorithm, you risk exceeding your Qualtrics rate limits and failing other API calls.
To create a response export you will need to send a POST request to the Start Response Export endpoint. You will receive an ID for the asynchronous export request.
{"result":{"progressId":"ES_6nZ9gWOgTs9hi0R","percentComplete":0.0,"status":"inProgress"},"meta":{"requestId":"cfdb9cfb-0dc2-403f-8835-a6db74a164d3","httpStatus":"200 - OK"}}
progressStatus= inProgress
Download is 100.0 complete
Complete
To retrieve the status of a recently initiated export, issue a GET request that targets that specific response export. Upon completion of the export, the status field will display complete, and the fileId field will contain the ID required to download the survey response file.
{
"result": {
"percentComplete": 100.0,
"fileId": "b4a753b8-8e27-43c2-824e-2b033fcf6bd8",
"status": "complete"
},
"meta": {
"httpStatus": "200 - OK",
"requestId": "555b3857-db1e-492d-9288-47652c3cc6b0"
}
}
The resulting file will be a ZIP file containing the file in the specified format.
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.
Java
Here's how to achieve the same in Java. This code was tested with version 1.8 of the Java Development Kit (JDK).
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.entity.StringEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ExportClient
{
private static final String SURVEY_ID = "<SURVEY_ID>";
private static final String API_TOKEN = "<API_TOKEN>";
private static final String DATA_CENTER = "<DATA_CENTER_ID>";
private static HttpClient httpClient;
private static ObjectMapper mapper;
public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException, Exception
{
httpClient = HttpClientBuilder.create().build();
mapper = new ObjectMapper();
// Step 1: Start the export
URI uri = new URIBuilder()
.setScheme("https")
.setHost(String.format("%s.qualtrics.com", DATA_CENTER))
.setPath("/API/v3/surveys/" + SURVEY_ID + "/export-responses")
.build();
HttpPost post = new HttpPost(uri);
post.setHeader("X-API-TOKEN", API_TOKEN);
post.setHeader("Content-Type", "application/json");
StringEntity body = new StringEntity(String.format("{ "format": "csv" }"));
post.setEntity(body);
JsonNode responseJson = sendRequest(post);
String progressId = responseJson.get("result").get("progressId").asText();
URI exportStatusUrl = new URIBuilder()
.setScheme("https")
.setHost(String.format("%s.qualtrics.com", DATA_CENTER))
.setPath("/API/v3/surveys/" + SURVEY_ID + "/export-responses/" + progressId)
.build();
// Step 2: Wait for the export to complete
String fileId;
JsonNode statusJson = null;
String progressStatus;
progressStatus = "InProgress";
while (!"complete".equals(progressStatus) & !"failed".equals(progressStatus))
{
HttpGet statusGet = new HttpGet(exportStatusUrl);
statusGet.setHeader("X-API-TOKEN", API_TOKEN);
statusJson = sendRequest(statusGet);
progressStatus = statusJson.get("result").get("status").asText();
Thread.sleep(1000);
}
if ("Failed".equals(progressStatus))
{
// end here
throw new java.lang.Exception("Export Failed.");
}
fileId = statusJson.get("result").get("fileId").asText();
// Step 3: Download the exported file
URI fileUrl = new URIBuilder()
.setScheme("https")
.setHost(String.format("%s.qualtrics.com", DATA_CENTER))
.setPath("/API/v3/surveys/" + SURVEY_ID + "/export-responses/" + fileId + "/file")
.build();
HttpGet statusGet = new HttpGet(fileUrl);
statusGet.setHeader("X-API-TOKEN", API_TOKEN);
HttpResponse response = httpClient.execute(statusGet);
// Step 4: Extract exported file
ZipInputStream zs = new ZipInputStream(response.getEntity().getContent());
ZipEntry entry;
while ((entry = zs.getNextEntry()) != null)
{
FileOutputStream out = new FileOutputStream("./" + entry.getName());
IOUtils.copy(zs, out);
out.close();
}
}
private static JsonNode sendRequest(HttpRequestBase httpRequest) throws IOException
{
HttpResponse response = httpClient.execute(httpRequest);
String body = EntityUtils.toString(response.getEntity());
return mapper.readTree(body);
}
}
Replace the value of <SURVEY_ID> with the correct value for your account. The two other values (<API_TOKEN> and <DATA_CENTER_ID>) should be obtained from your environment. See Finding Your Qualtrics IDs for more information.
The following JAR files are necessary:
- commons-io-2.5.jar
- commons-logging-1.2.jar
- httpcore-4.4.6.jar
- org.apache.httpcomponents.httpclient_4.5.3.jar
- jackson-annotations-2.9.1.jar
- jackson-core-2.9.1.jar
- jackson-databind-2.9.1.jar
Copy the JAR files to a folder. In this example, the JAR files are in a folder on the desktop called Libraries. The source file (ExportClient.java) is in another folder on the desktop, from which you are running the following commands. Both javac and java need to be in your path.
Below are examples for compiling the Java snippet on Linux or Mac and Windows:
- Bash (Linux/macOS)
- Windows PowerShell
javac -extdirs "~/Desktop/Libraries/" ExportClient.java
javac -extdirs "%USERPROFILE%\Desktop\Libraries" ExportClient.java
Run the generated .class file by typing the following:
- Bash (Linux/macOS)
- Windows PowerShell
## classpath must include both the folder with the Jars, and the folder with the class
java -cp "~/Desktop/Libraries/*;." ExportClient
## classpath must include both the folder with the Jars, and the folder with the class
java -cp "%USERPROFILE%\Desktop\Libraries\*;." ExportClient
Wildcards in Classpath
Do not use
.jar(or similar) in the classpath (-cp) because the wildcard character has a special interpretation in Java 6 and above. It expands to the JAR files in the specified folder.
When the program completes, the downloaded survey results will be available in the same folder as the program.
The Qualtrics API offers a range of endpoints for batch exporting of survey responses. While the default setting exports all responses in a survey, you can also specify a date range to narrow down the export.
Need to filter responses based on specific criteria like answer choices or spam flags? Use the export API's filtering option to obtain only the responses that meet your specified conditions.
Survey Structure
To apply filters based on values, column names, or other parameters, you need to understand the survey structure. You can obtain this information by running the provided code with only the format parameter set to json. This will output the survey's raw data, which you can then inspect to identify fields like QID1 and the range of choices for each question (e.g., 1, 2, 3).
data = {
"format": "json"
}
In this example, the field labels are displayed as QID1, QID2, and QID3. The corresponding choices for these fields are also indicated.
},
"displayedFields": ["QID1", "QID3", "QID2"],
"displayedValues": {
"QID1": [1, 2],
"QID3": [1, 2, 3],
"QID2": [1, 2, 3]
}
The complete output looks like this:
{
"responses": [{
"responseId": "R_1Cx8FIukucgqM94",
"values": {
"startDate": "2019-05-02T14:06:49Z",
"endDate": "2019-05-02T14:06:58Z",
"status": 0,
"ipAddress": "24.197.127.176",
"progress": 100,
"duration": 9,
"finished": 1,
"recordedDate": "2019-05-02T14:06:59.208Z",
"_recordId": "R_1Cx8FIukucgqM94",
"locationLatitude": "34.8307952880859375",
"locationLongitude": "-82.35070037841796875",
"distributionChannel": "anonymous",
"userLanguage": "EN",
"QID2": 2,
"QID3": 2
},
"labels": {
"status": "IP Address",
"finished": "True",
"QID2": "fair",
"QID3": "Maybe"
},
"displayedFields": ["QID1", "QID3", "QID2"],
"displayedValues": {
"QID1": [1, 2],
"QID3": [1, 2, 3],
"QID2": [1, 2, 3]
}
}]
}
Review the complete output to get a comprehensive understanding of how the survey is structured. This knowledge will inform your filter creation and editing.
Survey Structure
For an in-depth understanding of survey structure, including display logic and field names, see our Managing Surveys guide, which also covers how to use survey definition APIs like the Start Response Export endpoint.
Filtering Responses
Creating a Filter
To export filtered responses, first create a filter in the Data & Analysis tab. Each survey supports its own set of filters; sharing between surveys is not available. For detailed instructions, refer to the Filtering Responses documentation.
Retrieving Filter IDs
You can identify filter IDs via the List Survey Filters API or the user interface. When you make changes to an existing filter, the filter ID appears directly below the filter name in the UI.

For automated retrieval of filter IDs, you can use the List Survey Filters API endpoint. A call to this endpoint returns a list of filters for a specified survey, complete with their filter IDs and names. Use the filterId to initiate a response export that incorporates the corresponding filter.
As example, the following shell command:
curl -H 'X-API-TOKEN: <APITOKEN>' 'https://yourdatacenterid.qualtrics.com/API/v3/surveys/SV_123456/filters'
might return:
{
"result": {
"elements": [
{
"filterId": "94ec2e58-2b42-45e8-a52f-5615b5e93033",
"filterName": "Only non-spam responses",
"creationDate": "2010-01-05T03:37:31Z"
},
{
"filterId": "df946afa-2acd-4f48-b0df-34b290479819",
"filterName": "Only survey responses from promoters",
"creationDate": "2010-01-01T09:37:31Z"
}
],
"nextPage": null
},
"meta": {
"requestId": "900df19-75fd-479d-b4c2-6fd1f4b7b3e0"
}
}
This shows two filters that have already been created through the UI for this survey.
Exporting with a Filter
To initiate a filtered response export, use the Create Response Export endpoint. Include the filterId in the request body under the filterId parameter. The export begins as usual, and you can track its progress the same way as any standard response export. Upon completion, the resulting file contains only the responses compliant with the specified filter.
For example, consider a survey with the ID SV_123456 and the "Only non-spam responses" filter from the previous example. To start an export based on this filter, use the following shell command:
curl -X POST -H 'X-API-TOKEN: <APITOKEN>' -H 'Content-Type: application/json' -d '{
"format": "csv",
"filterId": "94ec2e58-2b42-45e8-a52f-5615b5e93033"
}' 'https://yourdatacenterid.qualtrics.com/API/v3/surveys/SV_012345678912345/export-responses'
Additional Parameters and Scenarios
This section covers additional parameters like seenUnansweredRecode, which recodes unanswered questions, and useLabels, which displays text labels instead of internal numerical identifiers.
Below we show some sample body parameter scenarios. These are designed to support new and legacy file formats and parameters for backward compatibility with the old API. The complete list of parameters is shown in the Start Response Export endpoint documentation.
We use this survey as an example:

seenUnansweredRecode
Recode functionality enables brands to modify the value of unanswered questions (recode it), simplifying integration with their systems. For example, setting the value to -1 replaces blank answers with -1; otherwise, the response will appear blank.
This parameter cannot be used for JSON or NDJSON (newline delimited JSON) formats.
data = {
"format": "csv",
"seenUnansweredRecode": -1
}
The results are shown in this CSV file snippet.

useLabels
This says whether to display the text labels shown to the user rather then the internal numbers defined in the survey structure.
data = {
"format": "csv",
"startDate": "2019-04-30T07:31:43Z",
"useLabels": True
}
The results are shown in this CSV file snippet. Note that text is shown instead of a numerical response.

JSON Example
The following is a example of JSON output when using the startDate filter.
startDate
The time format displayed is in Coordinated Universal Time (UTC), also known as Zulu time.
data = {
"format": "json",
"startDate": "2019-04-30T07:31:43Z"
}
The JSON output appears as follows:
{
"responses": [{
"responseId": "R_1Cx8FIukucgqM94",
"values": {
"startDate": "2019-05-02T14:06:49Z",
"endDate": "2019-05-02T14:06:58Z",
"status": 0,
"ipAddress": "24.197.127.176",
"progress": 100,
"duration": 9,
"finished": 1,
"recordedDate": "2019-05-02T14:06:59.208Z",
"_recordId": "R_1Cx8FIukucgqM94",
"locationLatitude": "34.8307952880859375",
"locationLongitude": "-82.35070037841796875",
"distributionChannel": "anonymous",
"userLanguage": "EN",
"QID2": 2,
"QID3": 2
},
"labels": {
"status": "IP Address",
"finished": "True",
"QID2": "fair",
"QID3": "Maybe"
},
"displayedFields": ["QID1", "QID3", "QID2"],
"displayedValues": {
"QID1": [1, 2],
"QID3": [1, 2, 3],
"QID2": [1, 2, 3]
}
}]
}