Python meets Salesforce

I needed to do some Salesforce automation (running an existing report and then manipulating the data before send off to a different API) and, already having a CentOS/Python2-based automation VM up in Google cloud, used Python2 as a starting point.

Token Postman

First step, have the SF team create an SF account for the service “user.” They also told me they created a “connected app” but I don’t have details on that.

Suffice to say, I was give a SF password for the email address of my user account plus a Consumer Key and Consumer Secret. These are needed to generate a short-time access token used for the actual request.

Working with the SF developer I got the token request working in Postman (note this is very redacted, everything other than port URL, grant_type, token_type and issued_at are altered)

Then I had to get that working in Python which turned out to be a pain in the ass, thus this post so if I ever need to do it again I have some help.

Token Python Developer

First, I made the four components needed into environment variables so they’re not in the .py or github . Then loaded some needed modules.

import json
import os
import requests
from requests.auth import HTTPBasicAuth

Before grabbing those environment variables.

SFCLIENT_ID = os.getenv('SFclient_id')
SFCLIENT_SECRET = os.getenv('SFclient_secret')
SFUSERNAME = os.getenv('SFusername')
SFPASSWORD = os.getenv('SFpassword')

Then create the json-ish payload for the request

SFPayload = {
"grant_type" : "password",
"client_id" : settings.SFCLIENT_ID,
"client_secret" : settings.SFCLIENT_SECRET,
"username" : settings.SFUSERNAME,
"password" : settings.SFPASSWORD

Set the URL and make the call

SFTokenURL = ""
response =, SFPayload)

Run the report and no token jokes.

Grab the returned access token

SFToken = response.json()['access_token']

And the instance URL, which is where the report will be run from, which I use to create my report request string.

SFReportURL = response.json()['instance_url'] + "/services/data/v46.0/analytics/reports/<reportID>?includeDetails=true"

The includeDetails=true is needed if you want all the rows and not just the summary. My initial run was stymied as the report was saved last year, and the dates are set when its manually run. I saved a copy of it with a date of “last 7 days” and voila, now its always current. The Report ID (the <reportID> above) just look in the URL when you are looking at the end of the URL when you open the report in SalesForce.

Make a header with the token.

headers = {'Authorization': 'Bearer ' + SFToken}

Send the header with the URL

response = requests.get(SFReportURL, headers=headers, verify=False)

The returned JSON is the report you ran.

SFReport = response.json()

The data is contained in the rows section of the JSON.

rows = SFReport['factMap']['0!T']['rows']

Sanitized pic of the report. Many of the “values” contained the same unique user ID while the “label” contained the data I needed:

Token Unicode

Bonus, one of the fields contains unicode data.

Grabbing it out of the report row (using for rowloop in rows):

UserFirst = (rowloop['dataCells'][2]['label'])

Preparing to send it on to the next API:

payload = {
"issued_to_first_name": UserFirst.encode('utf-8'),

Again, Python2 so YMMV.

This entry was posted in API, Python, Scripting. Bookmark the permalink.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.