""" API Usage Dashboard Template A feature analytics dashboard demonstrating: - Segmented control for category selection - Multiselect for endpoint filtering - Starter kits / presets for quick selection - Time series visualization with normalization - Metric cards with 28-day deltas - Rolling average options This template uses synthetic data. Replace generate_api_data() with your actual data source (e.g., Snowflake queries, APIs, etc.) """ from datetime import date, timedelta import numpy as np import pandas as pd import streamlit as st import altair as alt st.set_page_config( page_title="API Usage Dashboard", page_icon=":material/api:", layout="wide", ) # ============================================================================= # Synthetic Data Generation (Replace with your data source) # ============================================================================= # API categories and their endpoints API_CATEGORIES = { "Users": ["/users", "/users/{id}", "/users/me", "/users/search", "/users/bulk", "/users/export"], "Orders": ["/orders", "/orders/{id}", "/orders/create", "/orders/cancel", "/orders/refund", "/orders/status"], "Products": ["/products", "/products/{id}", "/products/search", "/products/categories", "/products/inventory"], "Analytics": ["/analytics/events", "/analytics/metrics", "/analytics/reports", "/analytics/dashboards"], } # Starter kits - predefined endpoint selections STARTER_KITS = { "None": [], "Core CRUD": ["/users", "/users/{id}", "/orders", "/orders/{id}"], "Search": ["/users/search", "/products/search", "/products/categories"], "Analytics": ["/analytics/events", "/analytics/metrics", "/analytics/reports"], "High Volume": ["/users", "/products", "/orders", "/analytics/events"], } ROLLING_OPTIONS = {"Raw": 1, "7-day average": 7, "28-day average": 28} def generate_api_data( endpoints: list[str], start_date: date, end_date: date, ) -> pd.DataFrame: """Generate synthetic API usage data. Replace this function with your actual data source. """ np.random.seed(42) dates = pd.date_range(start=start_date, end=end_date, freq="D") records = [] for endpoint in endpoints: # Each endpoint has different base traffic and growth base = np.random.randint(1000, 50000) growth = np.random.uniform(0.0005, 0.003) for i, dt in enumerate(dates): # Base trend with growth trend = base * (1 + growth) ** i # Weekly seasonality (lower on weekends) if dt.dayofweek >= 5: trend *= 0.4 # Random noise value = trend * np.random.uniform(0.85, 1.15) records.append({ "date": dt, "endpoint": endpoint, "request_count": int(value), }) df = pd.DataFrame(records) return df @st.cache_data(ttl=3600) def load_api_data() -> pd.DataFrame: """Load all API usage data.""" end_date = date.today() - timedelta(days=1) start_date = end_date - timedelta(days=365) all_endpoints = [] for endpoints in API_CATEGORIES.values(): all_endpoints.extend(endpoints) return generate_api_data(all_endpoints, start_date, end_date) def apply_rolling_average(df: pd.DataFrame, window: int) -> pd.DataFrame: """Apply rolling average to request data.""" if window == 1: return df result = df.copy() result["request_count"] = ( result.groupby("endpoint")["request_count"] .transform(lambda x: x.rolling(window, min_periods=1).mean()) ) return result def normalize_data(df: pd.DataFrame) -> pd.DataFrame: """Normalize request counts to percentages (share of total per day).""" result = df.copy() daily_totals = result.groupby("date")["request_count"].transform("sum") result["request_count"] = result["request_count"] / daily_totals return result def calculate_delta(df: pd.DataFrame, endpoint: str) -> tuple[float, float | None]: """Calculate 28-day delta for an endpoint.""" endpoint_data = df[df["endpoint"] == endpoint].sort_values("date") if len(endpoint_data) < 2: return endpoint_data["request_count"].iloc[-1], None latest = endpoint_data["request_count"].iloc[-1] if len(endpoint_data) > 28: previous = endpoint_data["request_count"].iloc[-29] else: previous = endpoint_data["request_count"].iloc[0] delta = latest - previous return latest, delta # ============================================================================= # Page Layout # ============================================================================= # Load data raw_data = load_api_data() # Header st.markdown("# API Usage :material/api:") st.caption("Select an API category to explore endpoint usage over time.") # Category selection (not centered) category = st.segmented_control( "Select category", options=[ ":material/person: Users", ":material/shopping_cart: Orders", ":material/inventory_2: Products", ":material/analytics: Analytics", ], default=":material/person: Users", label_visibility="collapsed", ) if not category: st.warning("Please select a category above.", icon=":material/warning:") st.stop() # Map display name to category key category_map = { ":material/person: Users": "Users", ":material/shopping_cart: Orders": "Orders", ":material/inventory_2: Products": "Products", ":material/analytics: Analytics": "Analytics", } selected_category = category_map[category] st.subheader(f"{category} endpoints", divider="gray") # Layout: filters on left, chart on right filter_col, chart_col = st.columns([1, 2]) with filter_col: # Metric selection with st.expander("Metric", expanded=True, icon=":material/analytics:"): measure = st.selectbox( "Choose a measure", ["Request count", "Unique callers", "Error rate"], index=0, label_visibility="collapsed", disabled=True, # Only one option in this template help="In production, connect to different metrics tables", ) rolling_label = st.segmented_control( "Time aggregation", list(ROLLING_OPTIONS.keys()), default="7-day average", label_visibility="collapsed", ) if rolling_label is None: st.caption("Please select a time aggregation.") st.stop() rolling_window = ROLLING_OPTIONS[rolling_label] normalize = st.toggle( "Normalize", value=False, help="Normalize to show percentage share of total requests", ) # Starter kits with st.expander("Starter kits", expanded=True, icon=":material/auto_awesome:"): starter_kit = st.pills( "Quick select", options=list(STARTER_KITS.keys()), default="None", label_visibility="collapsed", ) # Endpoint selection available_endpoints = API_CATEGORIES[selected_category] # Determine default selection based on starter kit if starter_kit and starter_kit != "None": default_endpoints = [e for e in STARTER_KITS[starter_kit] if e in available_endpoints] else: default_endpoints = available_endpoints[:4] # First 4 endpoints with st.expander("Endpoints", expanded=True, icon=":material/checklist:"): selected_endpoints = st.multiselect( "Select endpoints", options=available_endpoints, default=default_endpoints, label_visibility="collapsed", ) # Filter and process data if not selected_endpoints: with chart_col: st.info("Select at least one endpoint to view usage data.", icon=":material/info:") st.stop() filtered_data = raw_data[raw_data["endpoint"].isin(selected_endpoints)].copy() filtered_data = apply_rolling_average(filtered_data, rolling_window) if normalize: filtered_data = normalize_data(filtered_data) with chart_col: # Latest metrics with st.expander("Latest numbers", expanded=True, icon=":material/numbers:"): metrics_row = st.container(horizontal=True) for endpoint in selected_endpoints: latest, delta = calculate_delta(filtered_data, endpoint) if normalize: value_str = f"{latest:.2%}" delta_str = f"{delta:+.2%}" if delta is not None else None else: value_str = f"{latest:,.0f}" delta_str = f"{delta:+,.0f}" if delta is not None else None metrics_row.metric( label=endpoint, value=value_str, delta=delta_str, border=True, ) # Time series chart with st.expander("Time series", expanded=True, icon=":material/show_chart:"): y_format = ".1%" if normalize else ",.0f" y_title = "Share of requests" if normalize else "Request count" chart = ( alt.Chart(filtered_data) .mark_line() .encode( x=alt.X("date:T", title="Date"), y=alt.Y("request_count:Q", title=y_title, axis=alt.Axis(format=y_format)), color=alt.Color("endpoint:N", title="Endpoint", legend=alt.Legend(orient="bottom")), tooltip=[ alt.Tooltip("date:T", title="Date", format="%Y-%m-%d"), alt.Tooltip("endpoint:N", title="Endpoint"), alt.Tooltip("request_count:Q", title="Requests", format=y_format), ], ) .properties(height=450) .interactive() ) st.altair_chart(chart) # Raw data section with st.expander("Raw data", expanded=False, icon=":material/table:"): display_df = filtered_data.copy() if normalize: display_df["request_count"] = display_df["request_count"].apply(lambda x: f"{x:.2%}") st.dataframe(display_df, hide_index=True)