SAP Analytics Cloud (SAC) is widely used by organizations to provide interactive storytelling and track the business KPIs with advanced visualizations. However, companies often need to obtain KPI information in ways other than the SAC user interface, including in custom web apps. Although SAC does not support the direct embedding of KPI tiles into external apps, it does provide REST APIs that allow programmatic access to widget-level data from SAC stories. These APIs can be used to collect and display KPI tile data, including number (value), number state (status), title, and subtitle, in a bespoke user interface.
In this blog, I walk through a detailed, end-to-end implementation that illustrates how to fetch KPI tile data from a SAP Analytics Cloud story using the widgetquery/getWidgetData REST API. The approach uses Python for backend processing and Flask as a lightweight web framework to securely call SAC APIs and output KPI values on a web page.
We may test API access and retrieve KPI tile data using a straightforward Python script before developing the Flask application. This program shows you how to:
import requests
import webbrowser
import urllib.parse
# ---------------- CONFIG ----------------
TENANT_URL = "https://<your-tenant>.hanacloudservices.cloud.sap"
CLIENT_ID = "<YOUR_CLIENT_ID>"
CLIENT_SECRET = "<YOUR_CLIENT_SECRET>"
AUTHORIZATION_ENDPOINT = "https://<your-tenant>.hana.ondemand.com/oauth/authorize"
TOKEN_ENDPOINT = "https://<your-tenant>.hana.ondemand.com/oauth/token"
REDIRECT_URI = "https://your-app-domain.com/oauth/callback" # used only to capture code manually
STORY_ID = "<your-storyid>"
WIDGET_IDS = [
"Chart_1", "Chart_2", "Chart_3",
"Chart_4", "Chart_5", "Chart_6", "Chart_8"
]
# ---------------- STEP 1: LOGIN ----------------
params = {
"response_type": "code",
"client_id": CLIENT_ID,
"redirect_uri": REDIRECT_URI
}
auth_url = AUTHORIZATION_ENDPOINT + "?" + urllib.parse.urlencode(params)
print("\n Opening browser for SAC login...")
webbrowser.open(auth_url)
code = input("\n Paste the authorization code here: ").strip()
# ---------------- STEP 2: TOKEN ----------------
payload = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET
}
token_resp = requests.post(
TOKEN_ENDPOINT,
data=payload,
headers={"Content-Type": "application/x-www-form-urlencoded"}
)
token_resp.raise_for_status()
access_token = token_resp.json()["access_token"]
print("\n Access token received")
# ---------------- STEP 3: FETCH KPI ----------------
headers = {
"Authorization": f"Bearer {access_token}",
"Accept": "application/json"
}
print("\n KPI VALUES\n" + "-" * 40)
for widget_id in WIDGET_IDS:
url = f"{TENANT_URL}/widgetquery/getWidgetData"
params = {
"storyId": STORY_ID,
"widgetId": widget_id,
"type": "kpiTile"
}
r = requests.get(url, headers=headers, params=params)
if r.ok:
data = r.json()
number = data.get("number", "N/A")
title = data.get("title", widget_id)
print(f"{title}: {number}")
else:
print(f"{widget_id}: Error")
print("\n Done")How this operate
The full Flask application that is used to retrieve KPI tile data and authenticate with SAP Analytics Cloud is shown below. This code manages widget data fetching, token retrieval, login, and creates an eye-catching KPI dashboard in the browser.
HTML_TEMPLATE = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SAC KPI Dashboard</title>
<style>
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: #f4f6f8;
margin: 0; padding: 0;
}
h1 { text-align: center; margin-top: 20px; color: #333; }
.container {
display: flex;
flex-wrap: wrap;
justify-content: center;
margin: 40px auto;
max-width: 1200px;
gap: 20px;
}
.card {
color: #fff;
width: 250px;
height: 150px;
border-radius: 16px;
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
transition: transform 0.3s, box-shadow 0.3s;
cursor: pointer;
text-align: center;
padding: 10px;
}
.card:hover {
transform: translateY(-10px);
box-shadow: 0 20px 30px rgba(0,0,0,0.3);
}
.number {
font-weight: bold;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 90%;
}
.title {
font-size: 1em;
margin-top: 10px;
color: #e0e0e0;
}
/* Rainbow colors for cards */
.rainbow-0 { background: linear-gradient(135deg, #ff6b6b, #f06595); }
.rainbow-1 { background: linear-gradient(135deg, #feca57, #ff9f43); }
.rainbow-2 { background: linear-gradient(135deg, #1dd1a1, #10ac84); }
.rainbow-3 { background: linear-gradient(135deg, #54a0ff, #2e86de); }
.rainbow-4 { background: linear-gradient(135deg, #5f27cd, #341f97); }
.rainbow-5 { background: linear-gradient(135deg, #ee5253, #c0392b); }
.rainbow-6 { background: linear-gradient(135deg, #48dbfb, #00d2d3); }
</style>
<script>
// Adjust font size based on length
function adjustFontSize() {
const numbers = document.querySelectorAll('.number');
numbers.forEach(num => {
const length = num.innerText.length;
if(length <= 5) num.style.fontSize = '2.5em';
else if(length <= 8) num.style.fontSize = '2em';
else num.style.fontSize = '1.5em';
});
}
window.onload = adjustFontSize;
</script>
</head>
<body>
<h1>SAP Analytics Cloud - RESTAPI Fetched Sales KPI Dashboard</h1>
<div class="container">
{% for kpi in kpis %}
<div class="card rainbow-{{ loop.index0 % 7 }}">
<div class="number">{{ kpi.number }}</div>
<div class="title">{{ kpi.title }}</div>
</div>
{% endfor %}
</div>
</body>
</html>
"""from flask import Flask, render_template_string
import requests
import urllib.parse
import webbrowser
# ---------------- CONFIG ----------------
TENANT_URL = "https://yourtenant.hanacloudservices.cloud.sap"
CLIENT_ID = "<YOUR_CLIENT_ID>"
CLIENT_SECRET = "<YOUR_CLIENT_SECRET>"
AUTHORIZATION_ENDPOINT = "https://yourtenant.hana.ondemand.com/oauth/authorize"
TOKEN_ENDPOINT = "https://yourtenant.hana.ondemand.com/oauth/token"
REDIRECT_URI = "https://your-app-domain.com/oauth/callback"
STORY_ID = "<your_storyid>"
WIDGET_IDS = [
"Chart_1", "Chart_2", "Chart_3",
"Chart_4", "Chart_5", "Chart_6", "Chart_8"
]
# ---------------- FLASK APP ----------------
app = Flask(__name__)
def get_access_token():
# Step 1: login manually
params = {"response_type": "code", "client_id": CLIENT_ID, "redirect_uri": REDIRECT_URI}
auth_url = AUTHORIZATION_ENDPOINT + "?" + urllib.parse.urlencode(params)
print("\nOpen this URL in browser to login to SAC:")
print(auth_url)
webbrowser.open(auth_url)
code = input("\nPaste the authorization code here: ").strip()
# Step 2: get token
payload = {
"grant_type": "authorization_code",
"code": code,
"redirect_uri": REDIRECT_URI,
"client_id": CLIENT_ID,
"client_secret": CLIENT_SECRET
}
r = requests.post(TOKEN_ENDPOINT, data=payload, headers={"Content-Type": "application/x-www-form-urlencoded"})
r.raise_for_status()
return r.json()["access_token"]
def fetch_kpis(token):
headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"}
kpis = []
for widget_id in WIDGET_IDS:
url = f"{TENANT_URL}/widgetquery/getWidgetData"
params = {"storyId": STORY_ID, "widgetId": widget_id, "type": "kpiTile"}
r = requests.get(url, headers=headers, params=params)
if r.ok:
data = r.json()
kpis.append({
"title": data.get("title", widget_id),
"number": data.get("number", "N/A")
})
else:
kpis.append({"title": widget_id, "number": "Error"})
return kpis
# HTML Code will be written Here
@app.route("/")
def dashboard():
token = get_access_token()
kpis = fetch_kpis(token)
return render_template_string(HTML_TEMPLATE, kpis=kpis)
if __name__ == "__main__":
app.run(debug=True,port=8000)An explanation of the code
The KPI data is retrieved and shown in a personalized web dashboard after the program has launched and the user has successfully logged in using SAP Analytics Cloud OAuth.
The original KPI tiles as they appear in the SAP Analytics Cloud narrative are depicted in the image below. Business users in SAC are in charge of configuring and maintaining these KPIs.
The widgetquery/getWidgetData REST API is used to retrieve the same KPI values, which are then shown in a specially created web application. Custom Web Dashboard Screenshot
The KPI numbers in the SAC story and the web dashboard are same.
Every KPI tile shows:
The website's dashboard uses the following to improve visualization:
This proves to the secure consumption and reuse of SAC KPI data outside of the SAC user interface without requiring the duplication of business logic.
In this blog post, we shown a simple yet effective technique for extracting SAP REST APIs are used to tile Analytics Cloud KPI data and display it on a special webpage.
SAC KPIs can be consumed using custom dashboards outside of the SAC UI.
Python provides an adaptable and lightweight backend for API integration. REST APIs enable KPI tracking in real-time, while front-end. Style increases visibility.
Setting up the environment and security is essential for a safe execution.
SAC insights can be incorporated into executive dashboards, intranet portals, or external web apps, businesses are able to give users a consolidated view of key performance indicators without having to Launch SAC directly.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 27 | |
| 24 | |
| 20 | |
| 20 | |
| 14 | |
| 13 | |
| 13 | |
| 12 | |
| 12 | |
| 11 |