Access Patterns

Due to Jobalyzer's async access, some patterns may be useful as a reference.

Authentication

Authentication is handled by the service at the URL https://accounts.payscale.com/connect/token, as shown in the code below. The response from this system is a json object containing the following keys:

  • access_token: This is your access token, good for 10 minutes.

  • expires_in: This is how long your token is good for, in seconds. Generally 3600.

  • token_type: This is how you will use your token. Generally, this is "Bearer" type.

  • scope: This is the service your token is good for. Generally, this is just Jobalyzer.

import requests
import time

auth_url = 'https://accounts.payscale.com'
get_token_url = f'{auth_url}/connect/token'
client_id = '<your-client-id>'
client_secret = '<your-client-secret>'

# get oauth token using given clientId and clientSecret
payload = {
    'grant_type': 'client_credentials',
    'scope': 'jobalyzer',
    'client_id': client_id,
    'client_secret': client_secret
}
token_response = requests.post(get_token_url, data=payload)
if token_response.status_code == 200:
    token = token_response.json()['access_token']
else:
    print(f'failed to get token: {token_response.text}')

Async Access

Jobalyzer is a completely asynchronous service, meaning that the requests for generating a report and the requests for pulling down a report will complete whether the report is ready or not. In order to successfully retrieve a completed report, you must wait until the returned status code is 200.

First, we will take a look at submitting the Report Generation Request:

import requests
import time

jobalyzer_url = 'https://jobalyzer.payscale.com'
generate_reports_url = f'{jobalyzer_url}/jobalyzer/v1/reports'
client_id = '<your-client-id>'
client_secret = '<your-client-secret>'
# make a request for reports using the new token
# make a request for reports using the new token
data = {
  "customerId": client_id,
  "user": client_id,
  "AutoResolveJobTitle": True, # optional, tries to automatch submitted job title
  "requestedReports": ["yoe", "pay"],
  "answers": {
    "JobTitle": "Software Developer",
    "City": "Seattle", # optional
    "State": "Washington", # optional
    "Country": "United States",
    "YearsExperience": 5, # optional, effects Pay report
    "HighestDegreeEarned": "Bachelors" # optional
  }
}
headers = {'Authorization': f'Bearer {token}'}
request_response = requests.post(generate_reports_url, json=data, headers=headers)

The response will look like the following:

{
    "Links": {
        "Self": "https://jobalyzer.payscale.com/jobalyzer/v2/reports/<unique-ID>",
        "PayReport": "https://jobalyzer.payscale.com/jobalyzer/v1/reports/<unique-ID>/pay",
        "YearsExperienceReport": "https://jobalyzer.payscale.com/jobalyzer/v2/reports/<unique-ID>/yoe",
    },
    "Warnings": null,
    "Errors": null
}

Any errors will be present in "Errors". Common errors include:

  • Submitting an Unconfirmed Answer. This means that the answer does not appear in our database, and cannot be used to describe our data.

  • Expired token: This will result in a 401 Unauthorized error. Please refresh your token and try again.

Retrying

When the report links are generated, the reports are not done. Occasionally, depending upon load, there can be a minor delay between the link being generated and the report being ready. In these instances, it's fine to keep hitting the same link until the report is returned.

# parse the returned links to receive the reports
yoe_report_url = request_response.json()['Links']['YearsExperienceReport']
pay_report_url = request_response.json()['Links']['PayReport']

# request the completed reports until a 200 is returned
report_urls = {'Years of Experience': yoe_report_url,
             'Pay': pay_report_url}
reports = {}
for key in report_urls.keys():
  while reports.get(key) is None:
      response = requests.get(f'{report_urls[key]}?customerId={client_id}&user={client_id}', headers=headers)
      if response.status_code == 200:
          print(f'{key} report returned.')
          reports[key] = response.json()

      elif response.status_code == 202:
          print(f'{key} not ready, sleeping')
          time.sleep(response.json()['RetryAfterMilliseconds']/1000)
      else:
          print(f'failed for some other reason: {response.text}')
          break

print(reports)

Note

In both instances, a json object will be returned, and the status code should be used to differentiate between these events.

Once a report is collected, it can be collected again for a period of time, so expecting an error after a successful return is also no a viable strategy.

Sync Access

Some endpoints are synchronous, like the /jobalyzer/v1/impact/skills and /jobalyzer/v1/impact/certs endpoints. These still require the authentication token, but do not require polling for the result.