How to Poll Real-Time Queue Statistics with the Genesys Cloud Statistics API
What You Will Build
- A Python script that retrieves real-time queue metrics, including waiting calls, available agents, and handle time, for a specified queue.
- This implementation uses the Genesys Cloud CX Statistics API (
/api/v2/analytics/stats/queues) and the officialgenesyscloudPython SDK. - The tutorial covers Python 3.9+ with the
genesyscloudlibrary.
Prerequisites
- OAuth Client Type: A Genesys Cloud OAuth Client configured with the
client_credentialsgrant type. - Required Scopes: The client must have the
analytics:stats:readscope assigned. - SDK Version:
genesyscloudPython SDK v2.0 or later. - Runtime: Python 3.9 or higher.
- Dependencies:
pip install genesyscloud
Authentication Setup
The Genesys Cloud Python SDK handles the OAuth client_credentials flow automatically when you initialize the PlatformClient with your client credentials. You do not need to manually manage token refresh cycles; the SDK caches the token and requests a new one when the current one expires.
Set the following environment variables in your shell before running the script:
GENESYS_CLOUD_REGION: Your Genesys Cloud region (e.g.,us-east-1,eu-west-1).GENESYS_CLOUD_CLIENT_ID: Your OAuth Client ID.GENESYS_CLOUD_CLIENT_SECRET: Your OAuth Client Secret.
import os
from purecloudplatformclientv2 import (
PlatformClient,
AnalyticsApi,
QueueStatsRequest,
QueueStatsQueryType
)
def get_platform_client() -> PlatformClient:
"""
Initializes and returns an authenticated PlatformClient.
"""
client = PlatformClient()
# Configure the region
client.set_environment(os.getenv('GENESYS_CLOUD_REGION', 'us-east-1'))
# Set client credentials
client.client_id = os.getenv('GENESYS_CLOUD_CLIENT_ID')
client.client_secret = os.getenv('GENESYS_CLOUD_CLIENT_SECRET')
return client
Implementation
Step 1: Define the Queue Statistics Request
To fetch real-time data, you must construct a QueueStatsRequest object. This object tells the API exactly which queues you want, what time frame you are looking at, and which specific metrics (aggregates) you need.
For real-time observation, you set the intervalType to realtime. This is distinct from historical queries, which use hourly, daily, or weekly.
Critical Parameter: The queues filter. You must provide the specific Queue ID(s). You cannot fetch stats for “all queues” in a single real-time call efficiently without knowing the IDs. If you do not know the ID, you must first query the /api/v2/routing/queues endpoint. For this tutorial, we assume you have the Queue ID.
def build_queue_stats_request(queue_id: str) -> QueueStatsRequest:
"""
Constructs the request body for fetching real-time queue statistics.
Args:
queue_id: The UUID of the target queue.
Returns:
A configured QueueStatsRequest object.
"""
# Define the aggregates (metrics) you want to retrieve.
# These map directly to the columns you see in the Genesys Cloud Admin UI.
aggregates = [
"queue/waiting/count", # Number of interactions waiting in the queue
"queue/agentsAvailable/count", # Number of agents currently available
"queue/agentsBusy/count", # Number of agents currently on a call/chat
"queue/avgHandleTime", # Average handle time for completed interactions
"queue/avgWaitTime" # Average wait time for completed interactions
]
# Create the request object
request = QueueStatsRequest(
interval_type="realtime", # Crucial for real-time data
queues=[queue_id], # List of queue IDs to filter
aggregates=aggregates # List of metric keys to return
)
return request
Step 2: Execute the API Call
The AnalyticsApi class provides the post_analytics_stats_queues method. This is a POST endpoint because the request body contains complex filtering criteria (aggregates, queues, interval types).
Note on Rate Limiting: The Statistics API is subject to rate limits (typically 10 requests per second for this endpoint). If you are polling frequently (e.g., every 5 seconds), ensure you are not exceeding this limit across your entire application. The SDK does not automatically retry 429 errors for all endpoints, so explicit error handling is recommended.
def fetch_realtime_stats(client: PlatformClient, queue_id: str) -> dict:
"""
Fetches real-time statistics for a specific queue.
Args:
client: An authenticated PlatformClient instance.
queue_id: The UUID of the target queue.
Returns:
A dictionary containing the parsed statistics.
"""
analytics_api = AnalyticsApi(client)
request = build_queue_stats_request(queue_id)
try:
# Execute the API call
# The response object contains a 'divisions' key, which is a list of division-level stats.
# Within divisions, we look for 'queues'.
response = analytics_api.post_analytics_stats_queues(body=request)
return parse_response(response)
except Exception as e:
# Handle specific HTTP errors
if hasattr(e, 'status') and e.status == 429:
print(f"Rate limit exceeded. Please wait before retrying. Error: {e.message}")
raise
elif hasattr(e, 'status') and e.status == 401:
print("Authentication failed. Check your Client ID and Secret.")
raise
elif hasattr(e, 'status') and e.status == 403:
print("Forbidden. Ensure the OAuth client has the 'analytics:stats:read' scope.")
raise
else:
print(f"Unexpected error: {e}")
raise
Step 3: Processing Results
The Genesys Cloud Statistics API response structure is nested. It does not return a flat list of metrics. Instead, it returns a structure organized by divisions, and within those divisions, by queues.
You must traverse this hierarchy to extract the values. The metrics dictionary inside the queue object contains the actual numeric values for each aggregate you requested.
def parse_response(response) -> dict:
"""
Parses the nested API response to extract clean metric values.
Args:
response: The raw response object from post_analytics_stats_queues.
Returns:
A flat dictionary of metric keys to values.
"""
result = {
"waiting_count": 0,
"agents_available": 0,
"agents_busy": 0,
"avg_handle_time": 0,
"avg_wait_time": 0
}
# The response is structured as:
# response.divisions -> list of DivisionStats
# division.queues -> list of QueueStats
# queue.metrics -> dict of aggregate keys to values
if not response.divisions:
return result
for division in response.divisions:
if not division.queues:
continue
for queue_stats in division.queues:
if not queue_stats.metrics:
continue
# Map the API aggregate keys to our clean result keys
metrics = queue_stats.metrics
if "queue/waiting/count" in metrics:
result["waiting_count"] = metrics["queue/waiting/count"]
if "queue/agentsAvailable/count" in metrics:
result["agents_available"] = metrics["queue/agentsAvailable/count"]
if "queue/agentsBusy/count" in metrics:
result["agents_busy"] = metrics["queue/agentsBusy/count"]
if "queue/avgHandleTime" in metrics:
# avgHandleTime is in milliseconds
result["avg_handle_time"] = metrics["queue/avgHandleTime"]
if "queue/avgWaitTime" in metrics:
# avgWaitTime is in milliseconds
result["avg_wait_time"] = metrics["queue/avgWaitTime"]
# Break after the first queue if we are only targeting one specific ID
break
return result
Complete Working Example
The following script combines all steps into a runnable module. It polls the queue every 10 seconds and prints the status.
import os
import time
import logging
from purecloudplatformclientv2 import (
PlatformClient,
AnalyticsApi,
QueueStatsRequest
)
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def get_platform_client() -> PlatformClient:
client = PlatformClient()
client.set_environment(os.getenv('GENESYS_CLOUD_REGION', 'us-east-1'))
client.client_id = os.getenv('GENESYS_CLOUD_CLIENT_ID')
client.client_secret = os.getenv('GENESYS_CLOUD_CLIENT_SECRET')
return client
def build_queue_stats_request(queue_id: str) -> QueueStatsRequest:
aggregates = [
"queue/waiting/count",
"queue/agentsAvailable/count",
"queue/agentsBusy/count",
"queue/avgHandleTime",
"queue/avgWaitTime"
]
return QueueStatsRequest(
interval_type="realtime",
queues=[queue_id],
aggregates=aggregates
)
def parse_response(response) -> dict:
result = {
"waiting_count": 0,
"agents_available": 0,
"agents_busy": 0,
"avg_handle_time": 0,
"avg_wait_time": 0
}
if not response.divisions:
return result
for division in response.divisions:
if not division.queues:
continue
for queue_stats in division.queues:
if not queue_stats.metrics:
continue
metrics = queue_stats.metrics
if "queue/waiting/count" in metrics:
result["waiting_count"] = metrics["queue/waiting/count"]
if "queue/agentsAvailable/count" in metrics:
result["agents_available"] = metrics["queue/agentsAvailable/count"]
if "queue/agentsBusy/count" in metrics:
result["agents_busy"] = metrics["queue/agentsBusy/count"]
if "queue/avgHandleTime" in metrics:
result["avg_handle_time"] = metrics["queue/avgHandleTime"]
if "queue/avgWaitTime" in metrics:
result["avg_wait_time"] = metrics["queue/avgWaitTime"]
break
return result
def main():
# Configuration
QUEUE_ID = os.getenv('GENESYS_CLOUD_QUEUE_ID') # Set this env var
if not QUEUE_ID:
raise ValueError("GENESYS_CLOUD_QUEUE_ID environment variable not set.")
POLL_INTERVAL_SECONDS = 10
logger.info(f"Starting real-time queue observer for Queue: {QUEUE_ID}")
try:
client = get_platform_client()
analytics_api = AnalyticsApi(client)
while True:
try:
request = build_queue_stats_request(QUEUE_ID)
response = analytics_api.post_analytics_stats_queues(body=request)
stats = parse_response(response)
logger.info(
f"[Real-Time] Waiting: {stats['waiting_count']} | "
f"Available: {stats['agents_available']} | "
f"Busy: {stats['agents_busy']} | "
f"Avg Handle: {stats['avg_handle_time']}ms | "
f"Avg Wait: {stats['avg_wait_time']}ms"
)
except Exception as e:
logger.error(f"Error fetching stats: {e}")
time.sleep(POLL_INTERVAL_SECONDS)
except KeyboardInterrupt:
logger.info("Observer stopped.")
except Exception as e:
logger.error(f"Fatal error: {e}")
if __name__ == "__main__":
main()
Common Errors & Debugging
Error: 403 Forbidden
- Cause: The OAuth Client ID used does not have the
analytics:stats:readscope assigned. - Fix: Go to the Genesys Cloud Admin console, navigate to Platform > OAuth Clients, edit your client, and add the
analytics:stats:readscope. Save and restart your application.
Error: 404 Not Found (Empty Response)
- Cause: The Queue ID provided does not exist, or the queue is not associated with the division implicitly handled by the API filter.
- Fix: Verify the Queue ID by calling
GET /api/v2/routing/queues/{queueId}. Ensure the ID is a valid UUID.
Error: 429 Too Many Requests
- Cause: You are polling the Statistics API faster than the allowed rate limit.
- Fix: Increase the
POLL_INTERVAL_SECONDSin your loop. For real-time dashboards, consider using the Real-Time API (WebSocket) instead of polling the Statistics REST API if you require sub-second updates. The Statistics API is designed for near-real-time snapshots (typically updated every 1-5 seconds server-side).
Error: Metrics Return 0 or Null
- Cause: You requested a metric that is not applicable to the queue type or the time frame.
- Fix: Ensure you are requesting valid aggregates for
realtimeintervals. For example,queue/avgHandleTimemay return 0 if no interactions have completed recently. This is expected behavior.