Export Contact List and Download the csv

Hi There
Has anyone successfully managed to download a contact list to a desired location using a Python API
I have a Python script that collects the list of contact list ID's and then iterates through them to supply the download url, I can paste the url into a browser and it downloads the csv

However when i try to automate this it seems to download an html file instead of the .csv

I have tried this using authorisation with an Oauth app to no avail and even this basic script does the same thing, any help would be appreciated

import requests

url='url of the contact list'

response = requests.get(url)

if response.status_code == 200:

with open("data.csv", "wb") as f:

    f.write(response.content)

print("file downloaded succesfully")

else:

print("Failed to download file. Error code:{response.status_code}")

Any help would be appreciated

Regards

Andrew

The request to download the file should probably have an authorization header present. If you look at the responses for your request, you're probably being redirected to the login server since you didn't provide a valid auth token with your request. If you can post the URL you're using to download the file, that would help shed some light on what request it is you're making.

@tim.smith

Hi Tim

I cant seem to get the header in the correct format, how do i format the headers part

OAuth when using Client Credentials

api_client = PureCloudPlatformClientV2.api_client.ApiClient().get_client_credentials_token(CLIENT_ID, CLIENT_SECRET)

authApi = PureCloudPlatformClientV2.AuthorizationApi(api_client)

url='https://api.mypurecloud.com.au/api/v2/downloads/f5dc242c4e7ca65e'

headers={"Authorization": 'api_client'}

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

if response.status_code == 200:

with open("Test.csv", "wb") as f:

    f.write(response.content)

print("file downloaded succesfully")

else:

print("Failed to download file. Error code:{response.status_code}")

Regards

Andrew

I've asked the SDK team to assist you with formatting your code.

Hey @Andrew_Russell1

After you get your download URL, take the download ID (in your case above it is 'f5dc242c4e7ca65e') and use it in a request to GET /api/v2/downloads/{downloadId} This will return the download URI that should work for you in your original code snippet. It may not work first time if the path parameters issueRedirect and redirectToAuth are not set correctly, so be sure to play around with them if you encounter issues.

Hope this helps
Charlie

1 Like

Hi Andrew,

Are you sure about this line headers={"Authorization": 'api_client'}. In the example code you are giving here you are going to set the "Authorization" header to be the string 'api_client'. The api_client returned is a python class so you will need to pull the token out of it and not just set the whole object on the Authorization header.

Thanks,
John Carnell
Director, Developer Engagement

Hi John

No I am not sure about the headers part at all, I am trying to download a contact list in .csv format, i have done the same kind of thing with Agent Scripts but I notice with the Agent Script URl it already has some kind of authentication in the URL whereas the contact list uri\URL doesnt
I have no idea what to do with this
To be clear I already have the download uri but instead of pasting it into a Browser (which works fine as it uses the current logged in users credentials i assume) I want to automate the download with a Python script
I am new to Python so I would need some help on where to start

Regards

Andrew

Hi Andrew,

You'll need to send your access token with the header, which can be got from the api client. Try writing it like this.

headers = {
    'authorization': "bearer {}".format(api_client.access_token)
}

Regards,
Declan

1 Like

@Andrew_Russell1

In case you missed or misunderstood my previous reply.

After making a call to GET /api/v2/outbound/contactlists/<contactListID>/export you received back a uri that looks like this: "https://api.mypurecloud.com/api/v2/downloads/<downloadID>"

Using python logic, you will then need to retrieve the downloadId from the end of the URL and use it in the following API call:

GET /api/v2/downloads/<downloadID>?issueRedirect=false

This will return another url. This is value you should use in your original code snippet;

response = requests.get(url)

Hope this helps
Charlie

1 Like

Hi Charlie

I think I finally got it, thanks for your help
I used this to extract the last 16 digits of the uri which gives me my download id ( I hope that stays at 16 digits)
uri=api_response.uri
downloadID=(uri[-16:])

So there is 4 steps to this

Get a list of the Contact List ID's
From that do a Post to initiate the export process for each contact list
Run a get to retrieve the export url and finally
Download the file

I opted for wget instead of requests

What a palaver

I just need to figure out a better way to name the actual .csv that i download

Thanks to everyone for their input

Regards

Andrew

2 Likes

Hey @charlie.conneely , got a similar problem here. Developing a client app, using Implicit login + javascript SDK. I got this piece of code to try to download the export of a contact list:

function downloadExportedCsv(platformClient, contactListId, jobId, clientId, tries = 0) {
const apiInstance = new platformClient.OutboundApi();
let opts = {
"download": "true"
};
apiInstance.getOutboundContactlistExport(contactListId, opts)
.then(response => {
console.log('response.uri:', response.uri);
return fetch(response.uri, {mode: "no-cors"});
})
.then(response => {
console.log('Trabajo de exportación completado, archivo descargado:', response);
csvData = response.body;
showContactListRecords(csvData);
})
.catch(error => {
console.error('Error al descargar el CSV de la contact list exportado. Reintentando en 2 segundos...', error);
if (tries < 3) {
setTimeout(() => {
downloadExportedCsv(platformClient, contactListId, jobId, tries + 1);
}, 2000);
} else {
console.error('Error exportando el csv de la contact list. Máximo número de reintentos alcanzado');
}
});
}

As you can see (in bold), once I get the download URI from the job, I do a fetch for that URI (no-cors, so no AUTH headers are used), but I get an error from AWS:

InvalidArgumentOnly one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specifiedAuthorizationBearer zYeihP36...............................3ZA0..............fhCrREbgGhBkV9..................................

I've tried so many variants...but I have never managed to get the content of the csv. I have also tried as you suggest to get the downloadId and use Download API as an extra step, but I get the same result. Any ideas? I feel I'm quite close but heavily stuck at 99% of the way... Thanks in advance

PS: download flag set to false doesn't throw any error but I only got an empty csv
let opts = {
"download": "false"
};

Hello

You might find this answer to a previous forum post to be helpful. My first instinct is that you might be missing one step which is listed here. In this case, the user is seeing the same error message as you.

Hope this helps,
Charlie.

Mentioning @Jerome.Saint-Marc just in case he can also help, because I'm totally frustrated with this, cannot make it work. Thanks a lot both in advance.

I've tried to follow what Jerome describes in the other post. This is my javascript code to try to get the csv resulting from the contact list export:

let selectedContactListId;

let csvData;



function handleContactListSelection(platformClient, contactListId, clientId) {

  selectedContactListId = contactListId;

  initiateContactListExport(platformClient, contactListId, clientId);

}



function initiateContactListExport(platformClient, contactListId, clientId) {

  const apiInstance = new platformClient.OutboundApi();

  apiInstance.postOutboundContactlistExport(contactListId)

.then(response => {

  console.log('Export initiated:', response);

  setTimeout(() => {

    getDownloadUrl(platformClient, contactListId, clientId);

  }, 2000);

})

.catch(error => console.error('Error initiating contact list export:', error));

}



function getDownloadUrl(platformClient, contactListId, clientId, tries = 0) {

  const apiInstance = new platformClient.OutboundApi();

  apiInstance.getOutboundContactlistExport(contactListId) 

.then((data) => {

  console.log(`getOutboundContactlistExport success! data: ${JSON.stringify(data, null, 2)}`);

  console.log('Download URL retrieved:', data.uri);

  const modifiedUrl = data.uri + '?issueRedirect=false';

  console.log('Modified URL:', modifiedUrl);

  downloadExportedCsv(modifiedUrl);

})

.catch((err) => {

  console.log("failure getting export URL");

  console.error(err);

});

}



async function downloadExportedCsv(uri) {

  try {

const response = await fetch(uri);

if (!response.ok) {

  throw new Error(`Error when downloading CSV: ${response.statusText}`);

}



const csvData = await response.text();

showContactListRecords(csvData);

  } catch (error) {

console.error('Error:', error);

  }

}

Here the sequence of API calls I see in browser debugger, network tab:

  1. Request URL:

api/v2/outbound/contactlists/cd95e9e............/export

  1. Request Method:

POST

  1. Status Code:

200 OK


  1. Request URL:

api/v2/outbound/contactlists/cd95e9e6-................/export

  1. Request Method:

GET

  1. Status Code:

200 OK


  1. Request URL:

api/v2/downloads/96894..........?issueRedirect=false

  1. Request Method:

GET

  1. Status Code:

303 See Other


Request URL: xxxxxxxx/authorize?response_type=code&redirect_uri=https%3A%2F%2Fapi.mypurecloud.de%2Fapi%2Fv2%2Fdownloads%2Fcallback&state=968944a.......&client_id=181dfaa.....

Request Method: GET

Status Code: 302


Error on console tab:

Access to fetch at '......./authorize?response_type=code&redirect_uri=https%3A%2F%2Fapi.mypurecloud.de%2Fapi%2Fv2%2Fdownloads%2Fcallback&state=9689......&client_id=181df.......' (redirected from 'api/v2/downloads/96894......?issueRedirect=false') from origin 'xxxxxxxxx.github.io' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

I have tried many variants, but it always end in CORS error or AWS error ( <?xml version="1.0" encoding="UTF-8"?> InvalidArgument Only one auth mechanism allowed; only the X-Amz-Algorithm query parameter, Signature query string parameter or the Authorization header should be specified Authorization ...). Those variants include also not appending "?issueRedirect=false" in the URL, and also setting the download flag to false, and then another different try with that flag set to true:

let opts = {

  "download": "false"

};



apiInstance.getOutboundContactlistExport(contactListId, opts)

Nothing worked...

Might be worth checking that the Contact List names dont have any spaces in them, this was causing us issues with the last step of downloading them

I'm not sure if/how this would fix the problem, but have you tried including redirectToAuth=false in your call to the downloads API?

This topic was automatically closed 31 days after the last reply. New replies are no longer allowed.