API activity
API activity shows call counts by response class, the busiest discovered endpoints, and endpoint inventory.
Count calls by response class
Section titled “Count calls by response class”API discovery must be enabled (XCSH_API_DISCOVERY_ENABLED=true) for these queries. Summary queries select XCSH_NAMESPACE, XCSH_VIRTUAL_HOST, and the fixed two-hour XCSH_START_TIME–XCSH_END_TIME window in apiep_summary_filter; the end time is exclusive. Counts cover API endpoints and can differ from HTTP access logs.
-
Build a fixed-window summary request.
Terminal window test "$XCSH_API_DISCOVERY_ENABLED" = trueXCSH_REQUEST="$(jq -nc --arg ns "<XCSH_NAMESPACE>" --arg vh "<XCSH_VIRTUAL_HOST>" \--arg start "$XCSH_START_TIME" --arg end "$XCSH_END_TIME" '{namespace: $ns, name: $vh,apiep_summary_filter: {start_time: $start, end_time: $end}}')" -
Query the returned API-call counters.
Terminal window XCSH_API_CALLS_JSON="$(curl --silent --show-error --fail-with-body --max-time 60 \--request POST \--header "Authorization: APIToken <XCSH_API_TOKEN>" \--header 'Content-Type: application/json' \--data-binary "$XCSH_REQUEST" \"<XCSH_API_URL>/api/ml/data/namespaces/<XCSH_NAMESPACE>/virtual_hosts/<XCSH_VIRTUAL_HOST>/api_endpoints/summary/calls_by_response_code")"jq -er '{total_calls,request_count_per_rsp_code: (.request_count_per_rsp_code | sort_by(.rsp_code_class))}' <<<"$XCSH_API_CALLS_JSON"
Measured call summary
Section titled “Measured call summary”Captured 2026-10-06T03:13:52Z; HTTP 200.
{ "total_calls": "378", "request_count_per_rsp_code": [ { "rsp_code_class": "HTTP_RESPONSE_CODE_CLASS_2XX", "count": 378 } ]}The response fields are total_calls and request_count_per_rsp_code, whose entries contain rsp_code_class and count. total_calls is a JSON string while class counts are numbers. These are request counts, not sampled rates. A missing class is not a measured zero. Keep the API population and window with the output when comparing it to logs.
The status routes from the bounded request sequence appeared in access logs but not in this discovered-endpoint population. Compare each count within its query scope.
Find the busiest endpoints
Section titled “Find the busiest endpoints”Use the same namespace, virtual host, discovery requirement, and fixed two-hour summary window; the end time is exclusive.
-
Build the top-five request-percentage selector and query the endpoint summary.
Terminal window XCSH_REQUEST="$(jq -nc --arg ns "<XCSH_NAMESPACE>" --arg vh "<XCSH_VIRTUAL_HOST>" \--arg start "$XCSH_START_TIME" --arg end "$XCSH_END_TIME" '{namespace: $ns, name: $vh,apiep_summary_filter: {start_time: $start, end_time: $end},topk: 5, top_by_metric: "ACTIVITY_METRIC_TYPE_REQ_PERCENTAGE"}')"XCSH_API_TOP_JSON="$(curl --silent --show-error --fail-with-body --max-time 60 \--request POST \--header "Authorization: APIToken <XCSH_API_TOKEN>" \--header 'Content-Type: application/json' \--data-binary "$XCSH_REQUEST" \"<XCSH_API_URL>/api/ml/data/namespaces/<XCSH_NAMESPACE>/virtual_hosts/<XCSH_VIRTUAL_HOST>/api_endpoints/summary/top_active")"jq -er '{top_apieps: [.top_apieps[]? | {apiep_url, method, top_by_metric_value}]}' <<<"$XCSH_API_TOP_JSON"
Measured endpoint activity
Section titled “Measured endpoint activity”Captured 2026-10-06T03:13:53Z; HTTP 200.
{ "top_apieps": [ { "apiep_url": "<XCSH_API_ENDPOINT_1>", "method": "GET", "top_by_metric_value": 29 }, { "apiep_url": "<XCSH_API_ENDPOINT_2>", "method": "GET", "top_by_metric_value": 34 }, { "apiep_url": "<XCSH_API_ENDPOINT_3>", "method": "GET", "top_by_metric_value": 35 } ]}top_by_metric_value is the integer percentage of requests to that endpoint relative to all API endpoints for this
selector. It is not a call count. The default selector ranks endpoints by security-event percentage; set the
request-percentage selector explicitly. Do not assume backend array order is a descending ranking, and do not expect
rounded percentages or a top-five subset to sum to 100.
Read endpoint statistics
Section titled “Read endpoint statistics”This discovery snapshot uses the namespace and telemetry virtual host without a time-window selector.
-
Query the telemetry virtual host and summarize category counts.
Terminal window test "$XCSH_API_DISCOVERY_ENABLED" = truetest -n "<XCSH_VIRTUAL_HOST>"curl --silent --show-error --fail-with-body \--header "Authorization: APIToken <XCSH_API_TOKEN>" \--header 'Accept: application/json' \"<XCSH_API_URL>/api/ml/data/namespaces/<XCSH_NAMESPACE>/virtual_hosts/<XCSH_VIRTUAL_HOST>/api_endpoints/stats" |jq -er '{total_endpoints, inventory, discovered, shadow, pii_detected}'
Observed endpoint statistics
Section titled “Observed endpoint statistics”Captured 2026-10-06 02:14:32 UTC; HTTP 200. These are category counts, not a sum of disjoint sets.
{ "total_endpoints": 3, "inventory": 0, "discovered": 3, "shadow": 0, "pii_detected": 2}Expect total_endpoints, inventory, discovered, shadow, and pii_detected. Categories can overlap, so their sum need not equal the total.
Read endpoint inventory
Section titled “Read endpoint inventory”Use the exact XCSH_APP_TYPE label from the load balancer configuration. If it is empty, wait for configuration to settle and repeat identifier discovery. This inventory snapshot uses the namespace and app type without a time-window selector.
-
Query the app type and summarize its inventory.
Terminal window test "$XCSH_API_DISCOVERY_ENABLED" = truetest -n "<XCSH_APP_TYPE>"curl --silent --show-error --fail-with-body \--header "Authorization: APIToken <XCSH_API_TOKEN>" \--header 'Accept: application/json' \"<XCSH_API_URL>/api/ml/data/namespaces/<XCSH_NAMESPACE>/app_types/<XCSH_APP_TYPE>/api_endpoints" |jq -er '{last_update,count: (.apiep_list | length),categories: ([.apiep_list[]?.category] | group_by(.) |map({category: .[0], count: length}))}'
Observed endpoint inventory
Section titled “Observed endpoint inventory”Captured 2026-10-06 02:14:32 UTC; HTTP 200.
{ "last_update": null, "count": 3, "categories": [ { "category": [], "count": 3 } ]}The summary contains last_update, count, and category counts. A new API route can take time to appear.
The measured inventory contains three entries. Its last_update is null, so it does not provide a refresh time.