Cisco Secure Access Reporting API sample script

Reporting API Guide

This guide provides Python client samples for the Cisco Secure Access Reporting API.

Note: Your Secure Access API key must have the permissions to read and write on the reports key scope. For more information about the API key scopes, see Secure Access OAuth 2.0 Scopes.

First get your Secure Access API key, set up your environment, and install the Secure Access API client. For more information, see Samples Overview.

Run the Script

  1. Copy the script to a local file called main.py. Locate the script in your environment in a directory above the cisco directory.
  2. Run python3 main.py.

main.py

"""
Copyright (c) 2025 Cisco and/or its affiliates.
This software is licensed to you under the terms of the Cisco Sample
Code License, Version 1.1 (the "License"). You may obtain a copy of the
License at

https://developer.cisco.com/docs/licenses

All use of the material herein must be in accordance with the terms of
the License. All rights not expressly granted by the License are
reserved. Unless required by applicable law or agreed to separately in
writing, software distributed under the License is distributed on an "AS
IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
or implied.
"""

from datetime import datetime
import pandas as pd
import matplotlib.pyplot as plt
from requests_toolbelt import MultipartEncoder
import requests
import json
import os
from dotenv import load_dotenv

from cisco.secure_access import API
from cisco.secure_access import reports
from cisco.secure_access import GET
from cisco.secure_access import POST
from cisco.secure_access import token_url
from cisco.secure_access import client_id
from cisco.secure_access import client_secret

# requires limit, offset
reporting_limit_offset_endpoints = [
    "applications",
    "identities",
    "categories"
]

# requires to, from
reporting_to_from_endpoints = [
    "requests-summary/appconnector-groups",
    "summary",
    "total-requests",
    "identity-distribution",
    "deployment-status"
]

# requires to, from, limit, offset
reporting_to_from_limit_offset_endpoints = [
    "rules-activity",
    "top-urls",
    "top-destinations",
    "top-identities",
    "top-resources",
    "requests-by-appconnector",
    "requests-by-appconnector-group"
]

# requires to, from, agentIds (list of resource connector IDs)
reporting_to_from_agentids_endpoints = [
    "app-connectors/detailed-stats-timerange"
]

# requires to, from, privateresourceId (private resource ID)
reporting_to_from_privateresourceId_endpoints = [
    "private-resources/detailed-stats-timerange",
    "private-resources/detailed-stats-identities"
]

# requires to, from, privateresourceIds (list of private resource ID)
reporting_to_from_privateresourceIds_endpoints = [
    "private-resources/summary-stats"
]

# requires to, from, ruleIds (list of rule IDs)
reporting_to_from_ruleids_endpoints = [
    "summaries-by-rule/firewall-hitcount",
    "summaries-by-rule/hitcount",
    "summaries-by-rule/intrusion"
]

# requires to, from, list of group IDs (groupIds)
reporting_to_from_groupids_endpoints = [
    "app-connectors/groups/detailed-stats-timerange"
]

# requires to, from, cputhreshold
reporting_to_from_cputhreshold_endpoints = [
    "app-connectors/groups/overloaded-count"
]

# requires to, from, limit
reporting_to_from_limit_endpoints = [
    "activity",
    "activity/dns",
    "activity/proxy",
    "activity/firewall",
    "activity/ztna",
    "activity/intrusion",
    "activity/decryption",
    "top-threats",
    "top-threat-types",
    "threat-types",
    "threat-names",
    "top-ips",
    "top-ips/internal",
    "top-files",
    "top-eventtypes",
    "top-dns-query-types",
    "requests-by-hour",
    "requests-by-timerange",
    "categories-by-hour",
    "categories-by-timerange",
    "bandwidth-by-hour",
    "bandwidth-by-timerange",
    "summaries-by-category",
    "summaries-by-destination",
    "remote-access-events",
    "unique-resources"
]

# requires type
reporting_endpoints_with_type = [
    "top-resources/{}",
    "top-threats/{}",
    "top-identities/{}",
    "top-destinations/{}"
    "top-categories/{}"
    "top-threats-types/{}",
    "summary/{}",
    "summaries-by-category/{}",
    "summaries-by-destination/{}",
    "identity-distribution/{}",
    "requests-by-hour/{}",
    "requests-by-timerange/{}",
    "categories-by-hour/{}",
    "categories-by-timerange/{}",
    "total-requests/{}"
]

# requires identityId
reporting_endpoints_with_id = [
    "identities/{}"
]

# required threattypeid
reporting_endpoints_with_threattypeid = [
    "threat-types/{}"
]

# requires threatnameId
reporting_endpoints_with_threatnameid = [
     "threat-names/{}"
]

# required query parameters and sample values
to_param = "to"
from_param = "from"
now_param = "now"
minus_seven_param = "-7days"
limit_param = "limit"
offset_param = "offset"
equals = "="
ampersand = "&"
question_mark = "?"
rule_ids = "ruleIds"
group_ids = "groupIds"
cputhreshold = "cputhreshold"
agent_ids = "agentIds"
private_resource_id = "privateresourceId"
private_resource_ids = "privateresourceIds"
identity_type = "network"

to_map = {}
from_map = {}
limit_map = {}
offset_map = {}
ruleids_map = {}
groupids_map = {}
agentids_map = {}
cputhreshold_map = {}
privateresourceId_map = {}
privateresourceIds_map = {}

# sample values
to_map[to_param] = now_param
from_map[from_param] = minus_seven_param
limit_map[limit_param] = '100'
offset_map[offset_param] = '0'
ruleids_map[rule_ids] = '1'
groupids_map[group_ids] = '1,2'
agentids_map[agent_ids] = '1'
cputhreshold_map[cputhreshold] = '80'
privateresourceId_map[private_resource_id] = '1'
privateresourceIds_map[private_resource_ids] = '1,2'

# file count
csv_file_count = 0

# combinations of required query parameters
to_from_limit_required_parameters = {}
to_from_limit_offset_required_parameters = {}
limit_offset_required_parameters = {}
to_from_required_parameters = {}
to_from_ruleids_required_parameters = {}
to_from_groupids_required_parameters = {}
to_from_agentids_required_parameters = {}
to_from_cputhreshold_required_parameters = {}
to_from_privateresourceId_required_parameters = {}
to_from_privateresourceIds_required_parameters = {}

for i in [to_map, from_map, limit_map]:
    to_from_limit_required_parameters.update(i)
for i in [to_map, from_map, limit_map, offset_map ]:
    to_from_limit_offset_required_parameters.update(i)
for i in [limit_map, offset_map]:
    limit_offset_required_parameters.update(i)
for i in [to_map, from_map]:
    to_from_required_parameters.update(i)
for i in [to_map, from_map, groupids_map]:
    to_from_groupids_required_parameters.update(i)
for i in [to_map, from_map, ruleids_map]:
    to_from_ruleids_required_parameters.update(i)
for i in [to_map, from_map, agentids_map]:
    to_from_agentids_required_parameters.update(i)
for i in [to_map, from_map, cputhreshold_map]:
    to_from_cputhreshold_required_parameters.update(i)
for i in [to_map, from_map, privateresourceId_map]:
    to_from_privateresourceId_required_parameters.update(i)
for i in [to_map, from_map, privateresourceIds_map]:
    to_from_privateresourceIds_required_parameters.update(i)

# The directory where to write out files
output_dir = os.environ.get('OUTPUT_DIR') or os.get_env['OUTPUT_DIR']

# sample csv file
reporting_csv_file = output_dir + "/reporting.csv"

load_dotenv()

def get_csv_filepath():
    global csv_file_count
    csv_file_count += 1
    return output_dir + "/reporting_" + str(csv_file_count) + ".csv"

def get_required_parameters(parameters):
    p = question_mark
    for k, v in parameters.items():
        p += k + equals + v + ampersand
    return p[:-1] if len(p) > 1 else ''

def get_reporting(api, endpoint=None, parameters=None, csv_filepath=None):
    ''' Get data for a Reporting API endpoint. '''
    try:
        # Get data from Reporting API.
        url = endpoint + get_required_parameters(parameters)
        response = api.Query(reports, url, GET)

        # Check if the API request was successful
        if response.status_code == 200:
            print(f"Success. GET {url}.")
            df = pd.json_normalize(response.json()['data'])
            df.to_csv(csv_filepath(), mode='w')
            print(f"get reporting data in DataFrame:{df}")
            return response.json()
        else:
            print(f"Failed to get the reporting data for {url}. Status code: {response.status_code}, Response: {response.text}.")
            return
    except Exception as e:
        print(f"An error occurred: {e}.")

def main():
    # Exit out if the required client_id or client_secret is not set
    for var in ['API_KEY', 'API_SECRET', 'OUTPUT_DIR']:
        if os.environ.get(var) == None:
            print("Required environment variable: {} not set".format(var))
            exit()

    # Get an API token
    api = API(token_url, client_id, client_secret)

    try:
        for endpoint in reporting_to_from_limit_endpoints:
            json_data = get_reporting(api, endpoint, to_from_limit_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_limit_offset_endpoints:
            json_data = get_reporting(api, endpoint, to_from_limit_offset_required_parameters, get_csv_filepath)

        for endpoint in reporting_limit_offset_endpoints:
            json_data = get_reporting(api, endpoint, limit_offset_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_endpoints:
            json_data = get_reporting(api, endpoint, to_from_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_privateresourceId_endpoints:
            json_data = get_reporting(api, endpoint, to_from_privateresourceId_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_privateresourceIds_endpoints:
            json_data = get_reporting(api, endpoint, to_from_privateresourceIds_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_groupids_endpoints:
            json_data = get_reporting(api, endpoint, to_from_groupids_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_ruleids_endpoints:
            json_data = get_reporting(api, endpoint, to_from_ruleids_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_agentids_endpoints:
            json_data = get_reporting(api, endpoint, to_from_agentids_required_parameters, get_csv_filepath)

        for endpoint in reporting_to_from_cputhreshold_endpoints:
            json_data = get_reporting(api, endpoint, to_from_cputhreshold_required_parameters, get_csv_filepath)

    except Exception as e:
        print(e)

# main
if __name__ == "__main__":
    main()