Troubleshoot Real-Time Metrics
Find why a Real-Time Metrics chart is empty or low, why totals differ from Billing, and what the GraphQL API returns when it refuses a query.
This page lists the symptoms that Real-Time Metrics shows on a dashboard in Azion Console or in a GraphQL API response, each with its cause and its fix. The chart symptoms come first: low or missing points, empty and failed charts, the variation tag, totals that differ from Billing, the tooltip, the legend, copied queries, and the query input. The errors the GraphQL API returns close the page.
The newest points of a chart read lower than the rest
The last points of a line fall below the traffic you expect, then rise when you refresh the dashboard a few minutes later.
The Console does not plot the last bucket when the range ends at the current minute. The buckets before it may still be aggregating, for up to 10 minutes, so they can read low, as Aggregation and delay explains.
- End the range 10 minutes back: in the Absolute tab of the time range picker, set End date to a time slot at least 10 minutes in the past, then select Apply.
- Refresh after the delay: select Refresh once the newest minutes have finished aggregating.
- In a GraphQL query: set the
endoftsRangeat least 10 minutes before the query runs. The same query sent twice within those 10 minutes returns different values for its newest buckets, for the same reason.
Every point of a range that ended 10 minutes or more in the past is final, and returns the same value on each refresh.
A chart shows No data available
A chart card shows No data available in place of the chart, on one chart or on every chart of a product tab.
The dataset holds no metrics for the selected range and filters. Three cases cause it: the product that records the metrics is not active in your account, no traffic reached that product in the range, or an applied filter matches no traffic.
- Activate the product behind the chart: Real-Time Metrics reads only what these products record.
| Tab or chart | Requirement |
|---|---|
| Edge Cache chart, Build › Applications › Data Transferred | Cache active in your account |
| Build › Tiered Cache | Tiered Cache active in your account |
| Build › Functions | Functions active in your account |
| Build › Image Processor | Image Processor active in your account |
| Secure › Edge DNS | Edge DNS active in your account |
| Secure › Bot Manager | A subscription to Bot Manager, through Technical Support |
| Observe › Data Stream | Data Stream active, with at least one stream configured |
- Widen the time range: the initial range, Last 5 minutes, is empty when no request arrived in those minutes. Select a preset such as Last 24 hours.
- Remove a filter: select the remove icon on each applied-filter chip until the chart plots.
- Read an empty array as no data: through the API, a dataset with no metrics for the range returns
200and an empty array, not an error. AtieredCacheMetricsquery on an account with no Tiered Cache traffic returns:
Once the product records traffic in the range, the chart plots it, and a bucket with no events inside the range plots as zero.
A query for an older range returns an empty array
A GraphQL query for an old range returns 200 and an empty array, while the same query for a recent range returns rows.
Real-Time Metrics keeps each dataset for a fixed period, and past that period it returns no rows and no error. The period differs by dataset, so a breakdown dataset can return nothing for a range that another dataset still answers.
A query for a range in 2023 returns:
- Start the range inside the retention period of the dataset, which Data retention lists.
- Expect partial ranges to return what is kept: a range that starts before the retention period still returns the rows inside it, with no error.
- Store what you need to keep longer: query a period once it is complete and save the result, as Best practices for Real-Time Metrics describes.
Inside the retention period, the query returns the rows that hold data.
A chart shows The chart can’t be plotted
A chart card shows The chart can't be plotted. There was an issue loading the data. in place of its aggregation tag row.
Each chart sends its own query to the GraphQL API, and this chart’s query returned an error instead of data. The other charts of the dashboard can still plot.
- Send the query again: select Refresh.
- Narrow the range or add a filter: the API refuses a query past its request rate or the rows it reads, as Real-Time Metrics limits shows.
- Read the error yourself: in the chart’s More options menu, select Copy query, then run the query in GraphiQL Playground. The response carries the message the chart does not show.
After the fix, the chart plots its points, or the response names one of the errors under The GraphQL API refuses a query.
The variation tag reads Can’t compare
The variation tag of a chart reads Can’t compare, in a warning color with a triangle icon, instead of a percentage.
The tag compares the selected range with the window of the same length immediately before it. It reads Can’t compare when the change is between –0.01% and +0.01%, when either window has no value, or when the earlier window is 0.
- Read a change within ±0.01% as no change: the totals of the two windows differ by less than 0.01%.
- Choose a range whose earlier window had traffic: for example, if an application started serving traffic 30 minutes ago, Last 1 hour compares with an hour that held no requests.
- Compare complete windows: end the range at least 10 minutes in the past, so neither window holds buckets that are still aggregating.
When both windows hold a value and the change exceeds 0.01%, the tag shows the change as a percentage with two decimals, as Variation tag describes.
Real-Time Metrics totals differ from Billing
The total of a dashboard or a query for a period differs from the usage that Azion Billing reports for the same period.
Real-Time Metrics counts each event at most once, and Billing counts each event exactly once, so Real-Time Metrics can miss an event that Billing counts. On average, the two differ by less than 1%, as Counting and Billing explains.
- Use the Billing figure for charges: when the two differ, Billing is the reference, as Real-Time Info and Precise Billing describes.
- Use Real-Time Metrics for operations: read the dashboards to see a traffic change within minutes, not to settle a charge.
- Compare complete periods: end the range at least 10 minutes in the past, so no bucket of the total is still aggregating.
A gap of about 1% between the two is the expected difference, not a fault in either one.
A chart shows no tooltip
A chart plots, but shows no values when you hover over a series.
Azion Console shows the tooltip of a chart only in a browser window wider than 540 px. At 540 px and below, no chart shows a tooltip.
- Widen the browser window past 540 px.
- Read the totals in the legend: each entry shows the series name and its total over the range.
- Export the points: in the chart’s More options menu, select Export CSV to download the points as plotted.
In a window wider than 540 px, the tooltip lists the name and value of each series at the point under the cursor.
A chart legend stops at 16 series
A chart that splits its data into many series, such as one per domain, draws 16 of them, and its legend lists 16 entries.
A chart plots at most 16 series. Any series after the 16th is not added to the chart or to its legend.
- Filter to the series you need: add a filter on Domain or Workload, whichever label your account shows, with the In operator and the values you compare.
- Query every series through the API: select Copy query in the chart’s More options menu, and run the query with a
limithigh enough for every row, up to 10,000. The copied query keeps the chart’s ownlimit.
With the filter applied, the chart draws each series the filter keeps, up to 16, and the API returns one row for each series.
A copied query does not run in GraphiQL
A query pasted from Copy query into GraphiQL Playground does not run as pasted.
Copy query copies a text block, not a request: the line # QUERY, the query, the line # VARIABLES, and the variables as a JSON object. The query reads its filter values from those variables, so the JSON belongs in the variables pane, not in the query editor.
To run the copied query in GraphiQL Playground:
The object starts after the # VARIABLES line.
The response holds a data object named after the dataset, with the rows behind the chart. For the clipboard format, refer to Copy query, and for the playground, to GraphiQL Playground.
The query input refuses a filter
A message appears under the Azion Query Language input in the filter row, and Refresh stays disabled.
The expression breaks a syntax rule of the input, or names a field that the dataset of the current dashboard does not have. The fields depend on the dashboard, so an expression that works on one dashboard can fail on another.
- Space the operator: write
status = 200, notstatus=200. - Quote names of more than one word: write
"Upstream Status". - Close lists in parentheses: write
domain in (domain1, domain2), with no comma after the last value. - Give between two different values: write
status between (200, 300). - Pick fields from the suggestions:
Ctrl+Space, orCmd+Space, lists only the fields of the current dashboard.
When the expression is valid, the message clears and Enter applies it to the dashboard. Each message, verbatim, is listed in Validation messages.
The GraphQL API refuses a query
The GraphQL API answers at https://api.azion.com/v4/metrics/graphql. When it refuses a query, it returns a JSON body whose detail field holds the message. Each entry quotes the body the API returns. For every status code and message of the API, refer to GraphQL API error responses.
A query is refused with Authentication credentials were not provided
The API answers 401 with this body:
The request carries no Authorization header, and every query to the API needs a personal token.
- Send a personal token in the
Authorization: Token [TOKEN VALUE]header. To create one, refer to How to manage a personal token. Test it with a minimal query:
- Replace an invalid or expired token: the API answers
401with other messages, listed in GraphQL API error responses.
With a valid token, the API answers 200:
A query is refused because it has no time range
The API answers 400 with this body:
Every query must set a time range in its filter, and this one sets none.
- Add
tsRangeto the filter: for example,filter: { tsRange: { begin: "2026-01-01T12:00:00", end: "2026-01-02T12:00:00" } }. - Or set
tsGtandtsLtfor the start and the end of the range.
With a time range, the query returns 200 and the rows of that range.
A query is refused with You have exceeded the limit amount allowed for selected fields
The API answers 400 with this body:
The query selects more fields than one query accepts. The ts field counts toward the limit, and an aggregate output such as sum does not, as Real-Time Metrics limits shows.
- Drop the fields you do not read, including
tswhen you do not group by time. - Split the selection into two queries over the same range and filter.
Within the limit, the query returns 200 with every selected field.
A query is refused with The value for the query limit is invalid
The API answers 400 with this body:
The limit argument is above 10,000 or below 0.
- Set
limitbetween 0 and 10,000. - For more rows, shorten the range or page through the rows with
offset, as GraphQL features describes. - Do not drop
limitto avoid the error: a query without it is not refused, but it returns 10 rows.
With a valid limit, the query returns up to that many rows.
A query is refused with Cannot query field
The API answers 400 when a dataset or a field name does not exist. For a dataset, the message suggests the closest names:
The query names a dataset or a field that the API does not have, such as imageProcessedMetrics for the imagesProcessedMetrics dataset. For a field, the message names the type it was looked up in, such as Cannot query field "wafThreatFamilies" on type "HttpMetricsAggregatedFieldsLogType".
- Take the dataset name the message suggests, such as
imagesProcessedMetrics. - Check the field in its dataset: Real-Time Metrics GraphQL fields lists the fields of each dataset.
With names the API knows, the query returns 200.
A query is refused with Argument has invalid value
The API answers 400 when groupBy or aggregate names a field it does not accept on that dataset:
groupBy accepts only the dimensions of its own dataset, and remoteAddress is a dimension of httpBreakdownMetrics, not of httpMetrics. A computed field needs no aggregate, so sum: uniqueSessions on connectedUsersMetrics returns a message that starts with Argument "aggregate" has invalid value {sum: uniqueSessions}.
- Query the dataset that has the dimension: group by
remoteAddressonhttpBreakdownMetrics, as Find the top sources of WAF threats does. - Select a computed field directly: remove
aggregate, and list the field, such asuniqueSessions, among the selected fields.
With fields the dataset accepts, the query returns 200.
A query is refused with You have reached the request rate limit
The API answers 429 with the message You have reached the request rate limit!.
More requests reached the API in one minute than it accepts, as Real-Time Metrics limits shows.
- Wait, then send the request again.
- Send fewer requests: query a complete period once and keep the result, instead of querying the same period again.
- Select several fields in one query instead of one query per field.
Below the rate limit, each request returns its data again.
A call to the legacy API host answers 403 Forbidden
A query sent to https://api.azionapi.net/metrics/graphql answers 403 with an HTML page titled Azion - Default error page that reads Forbidden, not with JSON.
api.azionapi.net is the legacy host of the API. Real-Time Metrics queries go to the v4 endpoint.
- Send the query to
https://api.azion.com/v4/metrics/graphql, with theAuthorization: Token [TOKEN VALUE]header. - Update a Grafana data source that uses the legacy URL, as Import the Data Transferred dashboard shows.
On the v4 endpoint with a valid token, the query returns 200 and a JSON body.