Skip to main content
Data Export API

Use Enterpret's Data Export API for exporting data asynchronously.

Team Enterpret avatar
Written by Team Enterpret
Updated over a month ago

Overview

The Data Export API enables you to automate the submission of data export requests and retrieve the results asynchronously. This approach ensures that large datasets can be processed efficiently without causing delays in the client application.

Authorization

To access the Data Export API, you need an API key. Please contact your Customer Success representative to obtain your unique API key for making data export requests.

Once you have your API key, include it as a Bearer token in the Authorization header of each API request, like this:

'Authorization': 'Bearer {your_api_key}'

API Endpoints

SubmitAsync

Endpoint: POST /export/external/v1/async/submit

This endpoint facilitates the submission of data export requests, providing a reference for the export in the response. This reference can then be utilized with the fetch API to retrieve the exported data at a later time.

The export request allows users to specify the data to be exported using various filtering options. Below are the details of the request model.

Request Message:

Export Request

Field

Type

IsRequired

Comment

listRecords

ListRecordsRequest

Yes

Specifies what records to fetch.

pageSize

Int

Yes

The maximum number of records is to be contained in a single file in the response.
i.e if the listRecords criteria match the 14k records, and pageSize is 5k, the response will contain 3 files.

ListRecordsRequest

Field

Type

IsRequired

Comment

filter

FilterQuery

No

Filters to narrow down the data. This is similar to filtering in the data using Enterpret query builder.

timerange

TimeRange

Yes

The time range for which the data is to be fetched.

FilterQuery

Please note that only of the filter, or operation field can be provided as part of this.

Field

Type

IsRequired

Comment

filter

Filter

No

An individual filter, like filtering on metadata, labels, etc.

operation

FilterQueryOperation

No

Multiple filters tied with operations like, OR, AND, etc.

FilterQueryOperation

Field

Type

IsRequired

Comment

op

String

Yes

Possible values are OR, or AND.

params

[]FilterQuery

Yes

List of filter queries. This allows building in nested operations by creating a recursive relation between FilterQuery and FilterQueryOperation.

Filter

Please note that only one of the following fields can be populated as part of a single filter clause.

Field

Type

IsRequired

Comment

metadata

MetadataFilter

No

Filter based on metadata.

languageISO6393

String

No

Filter feedback from a particular language.

source

string

Filter feedback from a particular source.

hasLabel

HasLabelFilter

No

Filter feedback that has a Keyword, Reason, or Sentiment present on it.

MetadataFilter

Field

Type

IsRequired

Comment

key

String

Yes

Metadata Key to filter on.

value

Value

Yes

Value for the key.

op

String

No

Possible values are EQ, GT, LT, GTE, LTE, where EQ is the default operator.

HasLabelFilter

Please note that only one of the following fields can be populated.

Field

Type

IsRequired

Comment

aspect

Boolean

No

Filter records with keywords.

reason

Boolean

No

Filter records with reasons.

sentiment

Boolean

No

Filter records with sentiment.

TimeRange

Either provide the startTime and the endTime or provide the relative time.

Field

Type

IsRequired

Comment

startTime

int64

No

Start time in epoch seconds.

endTime

int64

No

Endtime in epoch seconds.

relative

RelativeTimeRange

No

Relative time.

RelativeTimeRange

Field

Type

IsRequired

Comment

value

int64

Yes

unit

String

Yes

Possible values are, Hour, Day, Week, Month, Year.

Example Requests

To submit a data export request, send a POST request to /export/external/v1/async/submit with the following JSON payload:

{
"pageSize": 100,
"listRecords": {
// Your listRecords request payload
}
}

  • Request to get last 30 days of data from ZendeskSupport or Twitter.

{
"listRecords": {
"filter": {
"operation": {
"op": "OR",
"params": [
{
"filter": {
"source": "ZendeskSupport"
}
},
{
"filter": {
"source": "Twitter"
}
}
]
}
},
"timerange": {
"relative": {
"unit": "Day",
"value": "30"
}
}
},
"pageSize": "5000"
}

  • Request to get the last 2 months' data from ZendeskSupport or Twitter, only if it has a Reason prediction on it.

{
"listRecords": {
"filter": {
"operation": {
"op": "AND",
"params": [
{
"filter": {
"hasLabel": {
"reason": true
}
}
},
{
"operation": {
"op": "OR",
"params": [
{
"filter": {
"source": "ZendeskSupport"
}
},
{
"filter": {
"source": "Twitter"
}
}
]
}
}
]
}
},
"timerange": {
"relative": {
"unit": "Month",
"value": "2"
}
}
},
"pageSize": "5000"
}

Response Message:

Field

Type

IsRequired

Comment

queryID

String

Yes

Reference for the submitted query

Example Response

{
"queryID": "async-avi5jg"
}

FetchAsync

Fetches the results of an asynchronous data export request created with the previous API.

Endpoint: POST /export/external/v1/async/fetch

Request Message:

Field

Type

IsRequired

Comment

queryID

String

Yes

QueryID that we received as the response from the previous API.

Example Request

To fetch the results of a previously submitted data export request, send a POST request to /external/v1/async/fetch with the following JSON payload:

{
"queryID": "your-query-id"
}

e.g

{
"queryID": "async-avi5jg"
}

Response Message

If the export is in progress, the API returns an empty JSON, with a 200 Status Code. Once the export request is completed, it contains the following response.

Field

Type

IsRequired

Comment

responseFiles

[]String

Yes

List of files containing the feedback data. Do note that the link to these files expires in a few hours, so process the content before that.

The file content is JSON, which has the following schema.

File Content Schema

Field

Type

IsRequired

Comment

records

[]FeedbackRecord

Yes

List of feedback records.

FeedbackRecord

Field

Type

IsRequired

Comment

id

String

Yes

ID of the Record

source

String

Yes

Source of the Record

orgID

String

Yes

OrgID that record belongs to.

type

String

Yes

RecordTypeReview, RecordTypeConversation, RecordTypeSurvey, RecordTypeForumConversationThread

sourceTimestamp

int

Yes

sourceTimestamp is timestamp of the feedback as reported by the source

ingestedAt

int

Yes

timestamp when feedback was ingested to Enterpret.

languages

[]Language

Yes

languages detected in the original feedback.

originRecordID

String

Yes

Unique identifier for the records in the source system.

predictions

[]Prediction

No

Ids of the predictions for this Feedback Records.

We can resolve the label names and other details for these using the label API discussed later.

summary

[]String

No

Summaries for the feedback record.

text

TextContent

No

Present if feedback is of type review.

conversation

ConversationContent

No

Present if feedback is of type conversation, or forum thread.

survey

SurveyContent

No

Present if the feedback is of type survey.

TextContent

Field

Type

IsRequired

Comment

text

String

Yes

Text of feedback.

textEn

String

No

Text of feedback in English, if the original feedback is in non-english.

ConversationContent

Field

Type

IsRequired

Comment

conversation

Conversation

Yes

content of the feedback.

conversationEn

Conversation

No

content of feedback in English, if the original feedback is in non-English.

Conversation

Field

Type

IsRequired

Comment

msgs

[]ConversationMessage

Yes

msgs in the conversation

ConversationMessage

Field

Type

IsRequired

Comment

text

String

Yes

content of the feedback.

actor

String

Yes

user, agent, bot

actorID

String

No

Id of the actor.

SurveyContent

Field

Type

IsRequired

Comment

surveyResponse

SurveyResponse

Yes

content of the feedback.

surveyResponseEn

SurveyResponse

Yes

content of the feedback in English, if the original feedback is in non-English.

SurveyResponse

Field

Type

IsRequired

Comment

responses

[]SurveyQA

Yes

list of question answer in the survey

SurveyQA

Field

Type

IsRequired

Comment

question

String

Yes

answer

String

Yes

selectedOptions

[]String

No

for multi-select questions.

Example File Content

{
"records": [
{
"id": "f742cf1a-382c-5af1-9f3c-4989c40c95d1",
"source": "Playstore",
"orgID": "573c9d74-bdc4-4c6d-bd40-3465658ab47b",
"type": "RecordTypeReview",
"metadata": {
"keyValues": {
"App id": {
"s": [
"us.zoom.videomeetings"
]
},
"Id": {
"s": [
"b2578def-37c4-4afd-a763-bbc81a11a1cc"
]
},
"Language code": {
"s": [
"bg"
]
},
"Language name": {
"s": [
"Bulgarian"
]
},
"Rating": {
"n": [
1
]
},
"Reviewer": {
"s": [
"owilli samuel"
]
},
"Timestamp": {
"n": [
1721302100
]
},
"Useful": {
"n": [
0
]
},
"Version": {
"s": [
"6.0.12.22225"
]
}
}
},
"sourceTimestamp": "1721302089",
"ingestedAt": "1721389257",
"languages": [
{
"iso6393": "eng",
"iso6391": "en",
"source": "detected"
}
],
"predictions": [
{
"labelID": "b818b657-3d0f-4113-bb54-f4bf8a2bbdfa",
"sentiment": {}
},
{
"labelID": "f7794882-b8cc-42fa-afd3-8159cf618b21",
"reason": {
"units": [
{
"confidence": 1.0000001
}
]
}
}
],
"summary": [
"The user has an extremely positive experience with Zoom, but does not provide specific details.",
"happy with zoom::praise"
],
"text": {
"text": "Extremely good."
}
},
{
"id": "9c014e6d-475e-5589-bca6-3f5dd96f45e7",
"source": "Playstore",
"orgID": "573c9d74-bdc4-4c6d-bd40-3465658ab47b",
"type": "RecordTypeReview",
"metadata": {
"keyValues": {
"App id": {
"s": [
"us.zoom.videomeetings"
]
},
"Id": {
"s": [
"9d23cbf4-e8fd-4d36-8d78-45dd4915da75"
]
},
"Language code": {
"s": [
"uk"
]
},
"Language name": {
"s": [
"Ukrainian"
]
},
"Rating": {
"n": [
5
]
},
"Reviewer": {
"s": [
"Haribhan Bhatt"
]
},
"Timestamp": {
"n": [
1721300500
]
},
"Useful": {
"n": [
0
]
},
"Version": {
"s": [
"6.1.1.22710"
]
}
}
},
"sourceTimestamp": "1721300532",
"ingestedAt": "1721389333",
"languages": [
{
"iso6393": "eng",
"iso6391": "en",
"source": "detected"
}
],
"text": {
"text": "Nice"
}
},
{
"id": "acfc9e64-60f4-5098-9617-ffb1259b9d1b",
"source": "Playstore",
"orgID": "573c9d74-bdc4-4c6d-bd40-3465658ab47b",
"type": "RecordTypeReview",
"metadata": {
"keyValues": {
"App id": {
"s": [
"us.zoom.videomeetings"
]
},
"Id": {
"s": [
"577b0588-ff98-439b-8210-2bdcb6ad7bd9"
]
},
"Language code": {
"s": [
"vi"
]
},
"Language name": {
"s": [
"Vietnamese"
]
},
"Rating": {
"n": [
2
]
},
"Reviewer": {
"s": [
"Vivek Kamble"
]
},
"Timestamp": {
"n": [
1721300500
]
},
"Useful": {
"n": [
0
]
},
"Version": {
"s": [
"6.1.1.22710"
]
}
}
},
"sourceTimestamp": "1721300422",
"ingestedAt": "1721389333",
"languages": [
{
"iso6393": "eng",
"iso6391": "en",
"source": "detected"
}
],
"predictions": [
{
"labelID": "e9c9fc3b-093b-47b9-8432-534d40558e88",
"reason": {
"units": [
{
"confidence": 0.9706277
}
]
}
},
{
"labelID": "b21c5a63-db73-4d13-b4d4-3c30b85029d2",
"sentiment": {}
}
],
"summary": [
"The user is dissatisfied with the Zoom app's performance on Android mobiles.",
"issue with zoom app performance on android mobiles::issue"
],
"text": {
"text": "Poor app to use on android mobiles"
}
}
]
}

Labels API

https://api.enterpret.com/export/external/v1/labels/<labelid>

Response:

{
"label": {
"id": "88aea2c2-3fb6-489a-99b9-72adddc78e6f",
"displayName": "Issue With Uploading Documents",
"type": "REASON",
"description": "Customers face issues with the document upload process, encountering problems with the file upload feature provided by Enterpret Inc.",
"createdAt": "1699632396",
"isVisible": true,
"metadata": {
"reason": {
"sentiment": "NEUTRAL",
"representativeSentences": [
"issue uploading documents",
"issue with file upload feature"
],
"type": "MANUALLY_DEFINED"
}
}
}
}

Sample Scripts

Bash

#!/bin/bash

# Constants
API_TOKEN="Bearer <api-token>" # Replace with a valid API token
SUBMIT_URL="https://api.enterpret.com/export/external/v1/async/submit"
FETCH_URL="https://api.enterpret.com/export/external/v1/async/fetch"
CHECK_DELAY=10 # Delay in seconds between status checks

# JSON payload for the query
payload=$(cat <<EOF
{
"listRecords": {
"pageSize": 5000,
"filter": {
"operation": {
"op": "OR",
"params": [
{
"filter": {
"source": "Qualtrics"
}
}
]
}
},
"timerange": {
"relative": {
"unit": "Day",
"value": 30
}
}
}
}
EOF
)

# Function to submit the query and retrieve the query ID
submit_query() {
response=$(curl -s -X POST "$SUBMIT_URL" \
-H "Authorization: $API_TOKEN" \
-H "Content-Type: application/json" \
-d "$payload")

query_id=$(echo "$response" | jq -r '.queryID')

if [[ "$query_id" == "null" ]]; then
echo "Failed to retrieve query ID. Response: $response"
exit 1
fi

echo "Query submitted. Query ID: $query_id"
echo "$query_id"
}

# Function to fetch the results of the query
fetch_results() {
local query_id="$1"

while true; do
response=$(curl -s -X POST "$FETCH_URL" \
-H "Authorization: $API_TOKEN" \
-H "Content-Type: application/json" \
-d "{\"queryID\": \"$query_id\"}")

is_completed=$(echo "$response" | jq -r '.isCompleted')

if [[ "$is_completed" == "true" ]]; then
echo "Query completed. Response files:"
echo "$response" | jq -r '.responseFiles[]'
break
else
echo "Query still in progress. Checking again in $CHECK_DELAY seconds..."
sleep "$CHECK_DELAY"
fi
done
}

# Main script execution
query_id=$(submit_query)
fetch_results "$query_id"

Python

import requests
import time

# Constants
API_TOKEN = 'Bearer <api-token>' # Replace with a valid API token
SUBMIT_URL = 'https://api.enterpret.com/export/external/v1/async/submit'
FETCH_URL = 'https://api.enterpret.com/export/external/v1/async/fetch'
HEADERS = {'Authorization': API_TOKEN}

# Function to submit the query and retrieve query ID
def submit_query():
payload = {
"listRecords": {
"pageSize": 5000,
"filter": {
"operation": {
"op": "OR",
"params": [
{
"filter": {
"source": "Qualtrics"
}
}
]
}
},
"timerange": {
"relative": {
"unit": "Day",
"value": 30
}
}
}
}
response = requests.post(SUBMIT_URL, headers=HEADERS, json=payload)
if response.status_code == 200:
data = response.json()
return data.get('queryID')
else:
response.raise_for_status()

# Function to fetch query results once completed
def fetch_results(query_id):
while True:
response = requests.post(FETCH_URL, headers=HEADERS, json={"queryID": query_id})
if response.status_code != 200:
response.raise_for_status()

result = response.json()
if result.get("isCompleted"):
print("Query completed. Response files:")
for file_url in result.get("responseFiles", []):
print(file_url)
break
else:
print("Query still in progress. Checking again in 10 seconds...")
time.sleep(10)

# Run the query and retrieve results
query_id = submit_query()
if query_id:
print(f"Query submitted. Query ID: {query_id}")
fetch_results(query_id)
else:
print("Failed to retrieve Query ID.")

Golang

package main

import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"time"
)

// Constants
const (
apiToken = "Bearer <api-token>" // Replace with a valid API token
submitURL = "https://api.enterpret.com/export/external/v1/async/submit"
fetchURL = "https://api.enterpret.com/export/external/v1/async/fetch"
checkDelay = 10 * time.Second // Delay between checks for completion
)

// Payload for the query request
var payload = map[string]interface{}{
"listRecords": map[string]interface{}{
"pageSize": 5000,
"filter": map[string]interface{}{
"operation": map[string]interface{}{
"op": "OR",
"params": []map[string]interface{}{
{
"filter": map[string]interface{}{
"source": "Qualtrics",
},
},
},
},
},
"timerange": map[string]interface{}{
"relative": map[string]interface{}{
"unit": "Day",
"value": 30,
},
},
},
}

// Function to submit the query and retrieve the query ID
func submitQuery() (string, error) {
payloadBytes, _ := json.Marshal(payload)
req, err := http.NewRequest("POST", submitURL, bytes.NewBuffer(payloadBytes))
if err != nil {
return "", err
}

req.Header.Set("Authorization", apiToken)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("request failed with status %d", resp.StatusCode)
}

var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", err
}

queryID, ok := result["queryID"].(string)
if !ok {
return "", fmt.Errorf("queryID not found in response")
}

return queryID, nil
}

// Function to fetch the results of the query
func fetchResults(queryID string) error {
for {
// Set up the request body with the query ID
body, _ := json.Marshal(map[string]string{"queryID": queryID})
req, err := http.NewRequest("POST", fetchURL, bytes.NewBuffer(body))
if err != nil {
return err
}

req.Header.Set("Authorization", apiToken)
req.Header.Set("Content-Type", "application/json")

client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("fetch request failed with status %d", resp.StatusCode)
}

var result map[string]interface{}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return err
}

if isCompleted, ok := result["isCompleted"].(bool); ok && isCompleted {
fmt.Println("Query completed. Response files:")
if responseFiles, ok := result["responseFiles"].([]interface{}); ok {
for _, file := range responseFiles {
fmt.Println(file)
}
}
break
} else {
fmt.Println("Query still in progress. Checking again in 10 seconds...")
time.Sleep(checkDelay)
}
}
return nil
}

func main() {
queryID, err := submitQuery()
if err != nil {
log.Fatalf("Failed to submit query: %v", err)
}
fmt.Printf("Query submitted. Query ID: %s\n", queryID)

err = fetchResults(queryID)
if err != nil {
log.Fatalf("Failed to fetch results: %v", err)
}
}

JavaScript

const axios = require('axios');

// Constants
const API_TOKEN = 'Bearer <api-token>'; // Replace with a valid API token
const SUBMIT_URL = 'https://api.enterpret.com/export/external/v1/async/submit';
const FETCH_URL = 'https://api.enterpret.com/export/external/v1/async/fetch';
const CHECK_DELAY = 10000; // 10 seconds

// Payload for the query request
const payload = {
listRecords: {
pageSize: 5000,
filter: {
operation: {
op: "OR",
params: [
{
filter: {
source: "Qualtrics"
}
}
]
}
},
timerange: {
relative: {
unit: "Day",
value: 30
}
}
}
};

// Function to submit the query and retrieve the query ID
async function submitQuery() {
try {
const response = await axios.post(SUBMIT_URL, payload, {
headers: {
Authorization: API_TOKEN,
'Content-Type': 'application/json'
}
});

if (response.status === 200) {
return response.data.queryID;
} else {
throw new Error(`Request failed with status ${response.status}`);
}
} catch (error) {
console.error("Error submitting query:", error.message);
throw error;
}
}

// Function to fetch the results of the query
async function fetchResults(queryID) {
while (true) {
try {
const response = await axios.post(FETCH_URL, { queryID }, {
headers: {
Authorization: API_TOKEN,
'Content-Type': 'application/json'
}
});

if (response.status === 200) {
const result = response.data;

if (result.isCompleted) {
console.log("Query completed. Response files:");
result.responseFiles.forEach(fileUrl => {
console.log(fileUrl);
});
break;
} else {
console.log("Query still in progress. Checking again in 10 seconds...");
await new Promise(resolve => setTimeout(resolve, CHECK_DELAY));
}
} else {
throw new Error(`Fetch request failed with status ${response.status}`);
}
} catch (error) {
console.error("Error fetching results:", error.message);
throw error;
}
}
}

// Main function to run the query and fetch results
(async () => {
try {
const queryID = await submitQuery();
console.log(`Query submitted. Query ID: ${queryID}`);

await fetchResults(queryID);
} catch (error) {
console.error("Process failed:", error.message);
}
})();

Troubleshooting

If you encounter any issues while using the Data Export API, please refer to the following common problems and solutions:

  • Invalid API Key: Ensure that you have included the correct API key as Bearer token in the Authorization header.

  • Invalid Request: Make sure all required fields are included in your request payload.

  • Server Errors: If you receive a server error, contact Enterpret support for assistance.

For further assistance, please reach out to Enterpret support or your customer success representative.

Did this answer your question?