Skip to content

Instantly share code, notes, and snippets.

Last active March 4, 2024 15:31
Show Gist options
  • Save fpcorso/702b80f162b2984fbd87a273af1a6f85 to your computer and use it in GitHub Desktop.
Save fpcorso/702b80f162b2984fbd87a273af1a6f85 to your computer and use it in GitHub Desktop.
Download conversations from a Help Scout mailbox and save as CSV file
Download conversations from a Help Scout mailbox and save to CSV file.
Be sure to replace the App ID and Secret in the authorization call as well
as the Mailbox ID in the conversations API call.
Python: 3.9.0
import csv
import datetime
import requests
# The token endpoint.
auth_endpoint = ''
# Preparing our POST data.
post_data = ({
'grant_type': 'client_credentials',
# Send the data.
r =, data=post_data)
# Save our token.
token = r.json()['access_token']
all_conversations = False
page = 1
# Prepare our headers for all endpoints using token.
endpoint_headers = {
'Authorization': 'Bearer {}'.format(token)
# Creates our file, or rewrites it if one is present.
with open('conversations.csv', mode="w", newline='', encoding='utf-8') as fh:
# Define our columns.
columns = ['ID', 'Customer Name', 'Customer email addresses', 'Assignee', 'Status', 'Subject', 'Created At',
'Closed At', 'Closed By', 'Resolution Time (seconds)']
csv_writer = csv.DictWriter(fh, fieldnames=columns) # Create our writer object.
csv_writer.writeheader() # Write our header row.
while not all_conversations:
# Prepare conversations endpoint with status of conversations we want and the mailbox.
conversations_endpoint = '{}'.format(
r = requests.get(conversations_endpoint, headers=endpoint_headers)
conversations = r.json()
# Cycle over conversations in response.
for conversation in conversations['_embedded']['conversations']:
# If the email is missing, we won't keep this conversation.
# Depending on what you will be using this data for,
# You might omit this.
if 'email' not in conversation['primaryCustomer']:
print('Missing email for {}'.format(customer_name))
# Prepare customer name.
customer_name = '{} {}'.format(
# Prepare assignee, subject, and closed date if they exist.
assignee = '{} {}'.format(conversation['assignee']['first'], conversation['assignee']['last']) \
if 'assignee' in conversation else ''
subject = conversation['subject'] if 'subject' in conversation else 'No subject'
closed_at = conversation['closedAt'] if 'closedAt' in conversation else ''
# If the conversation has been closed, let's get the resolution time and who closed it.
closed_by = ''
resolution_time = 0
if 'closedByUser' in conversation and conversation['closedByUser']['id'] != 0:
closed_by = '{} {}'.format(
conversation['closedByUser']['first'], conversation['closedByUser']['last']
createdDateTime = datetime.datetime.strptime(conversation['createdAt'], "%Y-%m-%dT%H:%M:%S%z")
closedDateTime = datetime.datetime.strptime(conversation['closedAt'], "%Y-%m-%dT%H:%M:%S%z")
resolution_time = (closedDateTime - createdDateTime).total_seconds()
'ID': conversation['id'],
'Customer Name': customer_name,
'Customer email addresses': conversation['primaryCustomer']['email'],
'Assignee': assignee,
'Status': conversation['status'],
'Subject': subject,
'Created At': conversation['createdAt'],
'Closed At': closed_at,
'Closed By': closed_by,
'Resolution Time (seconds)': resolution_time
if page == conversations['page']['totalPages']:
all_conversations = True
page += 1
Copy link

Hello. Maybe you can answer. I've tried your code to download conversations from helpscout, but i've got an error

line r = requests.get(conversations_endpoint, headers=endpoint_headers) gets me this b'{"logRef":"ccd8d588-3c77-43b8-85f3-c619c1e5c980#7219551","message":"Bad request","_embedded":{"errors":[{"path":"mailbox","message":"must be comma separated list of ids","rejectedValue":"u3h423u4buoi43","source":"query parameter","_links":{"about":{"href":""}}}]},"_links":{"about":{"href":""}}}'

u3h423u4buoi43 - mail_id (i've changed that, it is not real, but i've used real one). Do you know what could be wrong?

Copy link

Hello. Tell me why there is an empty array in the response {'_embedded': {'conversations': []}

Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment