# Troubleshoot the GraphQL API

A [GraphQL API](/en/documentation/devtools/graphql/) request can fail with a `401` or a `204`, a query can fail with a `400` that names the problem, and a query can return no rows. Authentication and endpoint errors come first, then refused queries, empty results, and the GraphiQL Playground. Every error the API returns is a JSON object with a single `detail` key.

---

## A request returns 401 with Authentication credentials were not provided

A request to a GraphQL API endpoint returns HTTP `401` with this body:

```json
{
  "detail": "Authentication credentials were not provided."
}
```

The request has no `Authorization` header, or it sends the token with the `Bearer` scheme. The API treats a `Bearer` header as an absent header.

- **Send the Token scheme**: add the header `Authorization: Token [TOKEN VALUE]` to every request, with the value of your personal token.
- **Check the header name**: the header is `Authorization`, and the scheme word `Token` comes before the value, separated by a space.

The query `{ __typename }` then returns HTTP `200` with `"__typename": "Query"`.

---

## A request returns 401 with Invalid Token

A request with an `Authorization: Token` header returns HTTP `401` with this body:

```json
{
  "detail": "Invalid Token"
}
```

The header uses the right scheme, but the API does not accept its value as a personal token.

- **Copy the whole token**: paste the full value of the personal token after the word `Token` and one space, with no quotes.
- **Create another personal token**: if you no longer have the value, create a personal token in Azion Console. For the steps, refer to [How to manage a personal token](/en/documentation/guides/platform/account-and-billing/personal-tokens/).

The request then returns HTTP `200` with a `data` object.

---

## A request returns 204 with an empty body

A `POST` request with a valid token returns HTTP `204` and no body, not an error.

The URL is not one of the GraphQL API endpoints. An unknown path under `https://api.azion.com/v4/` answers `204`, not `404`.

- **Use one of the five endpoints**: each endpoint serves one data family. Send the query to the endpoint that holds its dataset:

```text
https://api.azion.com/v4/metrics/graphql
https://api.azion.com/v4/events/graphql
https://api.azion.com/v4/billing/graphql
https://api.azion.com/v4/accounting/graphql
https://api.azion.com/v4/consumption/graphql
```

The request then returns HTTP `200` with a `data` object, or a `400` that names the problem in the query. For the datasets of each endpoint, refer to [Datasets and query arguments](/en/documentation/devtools/graphql/features/).

---

## A query fails with Cannot query field workloadEvents on type Query

A query on an events dataset, such as `workloadEvents`, returns HTTP `400` with this body:

```json
{
  "detail": "Cannot query field \"workloadEvents\" on type \"Query\". Did you mean \"workloadMetrics\" or \"workloadBreakdownMetrics\"?"
}
```

The query went to the metrics endpoint, which serves only Metrics datasets. The same message names a dataset whose name is misspelled, such as `workloadMetric`.

- **Send events queries to the events endpoint**: post `workloadEvents` and the other events datasets to `https://api.azion.com/v4/events/graphql`.
- **Check the dataset name**: the `Did you mean` list of the message names the datasets that endpoint serves.

This query, sent to the events endpoint, returns the latest requests of a 7-day window:

```graphql
query {
  workloadEvents(
    limit: 5
    filter: { tsRange: {begin: "2026-09-26T14:00:00", end: "2026-10-03T14:00:00"} }
    orderBy: [ts_DESC]
  ) {
    ts
    host
    status
    requestUri
  }
}
```

The response holds one object per request. It is cut after the third of 5 rows:

```json
{
  "data": {
    "workloadEvents": [
      {
        "ts": "2026-10-03T13:34:44Z",
        "host": "www.example.com",
        "status": 200,
        "requestUri": "/"
      },
      {
        "ts": "2026-10-03T13:16:33Z",
        "host": "www.example.com",
        "status": 200,
        "requestUri": "/"
      },
      {
        "ts": "2026-10-03T12:53:40Z",
        "host": "www.example.com",
        "status": 200,
        "requestUri": "/"
      },
      …
    ]
  }
}
```

The query returns HTTP `200` with the records of the window.

---

## A query fails with The query has reached a system limit

An events query returns HTTP `400` with this body:

```json
{
  "detail": "The query has reached a system limit. Please adjust your query and try again."
}
```

The query exceeds a bound of the events endpoint. Two causes return this message: 37 or more selected fields on `workloadEvents`, or a 7-day window on `functionConsoleEvents`.

- **Select at most 36 fields on workloadEvents**: remove fields until the query selects 36 or fewer. On a Metrics dataset, the limit is 37 fields. The 38th field returns `You have exceeded the limit amount allowed for selected fields (37 fields).`
- **Shorten the window on console events**: query `functionConsoleEvents` over a shorter window, such as one hour. The deprecated `cellsConsoleEvents` returns the same error; use `functionConsoleEvents`.

The query then returns HTTP `200`. For every bound a query runs within, refer to [GraphQL API limits](/en/documentation/devtools/graphql/limits/).

---

## A query fails with The query includes fields that require grouping

A Metrics query returns HTTP `400` with this body:

```json
{
  "detail": "The query includes fields that require grouping. Please ensure all non-aggregated fields are included in groupBy argument."
}
```

The query selects a measure, such as `requests`, without aggregating it. A measure goes in the `aggregate` argument, and the response returns its result in a field named after the function.

- **Aggregate the measure**: move the measure into `aggregate`, such as `aggregate: { sum: requests }`, and select `sum` in place of `requests`.
- **Group by the other fields**: list every selected field that is not an aggregate in `groupBy`.

This query sums the requests of a 7-day window per time bucket:

```graphql
query {
  workloadMetrics(limit: 10000, filter: { tsRange: {begin: "2026-09-26T14:00:00", end: "2026-10-03T14:00:00"} }, aggregate: { sum: requests }, groupBy: [ts]) {
    ts
    sum
  }
}
```

The response holds one row per bucket and is cut after the third row:

```json
{
  "data": {
    "workloadMetrics": [
      {
        "ts": "2026-10-02T20:00:00Z",
        "sum": 76
      },
      {
        "ts": "2026-10-03T14:00:00Z",
        "sum": 1
      },
      {
        "ts": "2026-10-02T03:00:00Z",
        "sum": 2
      },
      …
    ]
  }
}
```

The query returns HTTP `200` with one `sum` per group.

---

## A resample query fails with Query syntax error

A query with a `resample` argument returns HTTP `400` with this body:

```json
{
  "detail": "\n    Query syntax error. In resample queries, you must provide the timestamp (ts) field in\n    the group_by parameter and you must include the group_by fields in the selected fields to return data.\n    To query data without resampling, remove the resample parameter from the query.\n"
}
```

The `groupBy` argument of the query does not hold `ts`, which every resample query requires.

- **Group by ts**: add `ts` to `groupBy`, and select `ts` and every other `groupBy` field.
- **Resample a Metrics dataset**: `resample` exists only on Metrics datasets. On an events dataset, the API returns `Unknown argument "resample"`.

This query resamples the requests of a 7-day window to 10 points:

```graphql
query {
  workloadMetrics(
    limit: 10000
    resample: { function: mean, points: 10 }
    filter: { tsRange: {begin: "2026-09-26T14:00:00", end: "2026-10-03T14:00:00"} }
    aggregate: { sum: requests }
    groupBy: [ts]
    orderBy: [ts_ASC]
  ) {
    ts
    sum
  }
}
```

The response holds one row per point. It is cut after the third of 11 rows:

```json
{
  "data": {
    "workloadMetrics": [
      {
        "ts": "2026-09-26T16:00:00Z",
        "sum": 2.0
      },
      {
        "ts": "2026-09-27T08:00:00Z",
        "sum": 2.125
      },
      {
        "ts": "2026-09-28T00:00:00Z",
        "sum": 3.6875
      },
      …
    ]
  }
}
```

The query returns HTTP `200` with the resampled points. For the resample functions, refer to [Datasets and query arguments](/en/documentation/devtools/graphql/features/).

---

## A query fails with The start and end dates must have the same timezone

A query returns HTTP `400` with this body:

```json
{
  "detail": "The start and end dates must have the same timezone."
}
```

One bound of the time window carries a timezone and the other does not, such as `Z` on `begin` only.

- **Use the same offset on both bounds**: write both dates with the same offset, such as `-03:00`, or both without one. The API converts both to UTC.

This query reads one hour with the `-03:00` offset on both bounds:

```graphql
query {
  workloadMetrics(limit: 3, filter: { tsRange: { begin: "2026-10-03T10:00:00-03:00", end: "2026-10-03T11:00:00-03:00" } }, aggregate: { sum: requests }, groupBy: [ts], orderBy: [ts_ASC]) {
    ts
    sum
  }
}
```

The response returns the buckets in UTC:

```json
{
  "data": {
    "workloadMetrics": [
      {
        "ts": "2026-10-03T13:16:00Z",
        "sum": 1
      },
      {
        "ts": "2026-10-03T13:34:00Z",
        "sum": 1
      }
    ]
  }
}
```

The query returns HTTP `200` with timestamps in UTC.

---

## A query returns an empty array

A query returns HTTP `200`, but the dataset array holds no rows:

```json
{
  "data": {
    "workloadMetrics": []
  }
}
```

No row matches the window and the filters. The API returns no error when the window is reversed or holds no data.

- **Check the order of the bounds**: `begin` must come before `end`. A reversed window returns an empty array.
- **Move the window inside the retention**: events records are kept about 7 days, so a wider window returns nothing for the part outside it. `workloadBreakdownMetrics` keeps 90 days of data. For the retention of each dataset, refer to [How the GraphQL API works](/en/documentation/devtools/graphql/overview/).
- **Replace copied dates**: an example query with dates from an earlier year returns an empty array. Set the window to a period with traffic.
- **Check that the account has data**: a dataset with no traffic on the account returns an empty array.

The query returns rows once the window holds data that matches the filters.

---

## The GraphiQL Playground returns Authentication credentials were not provided

An endpoint URL opened in a browser shows HTTP `401` with this body, not the GraphiQL Playground:

```json
{"detail": "Authentication credentials were not provided."}
```

The browser request carries no session and no token.

- **Log in to Azion Console first**: log in to [Azion Console](https://console.azion.com/) in the same browser, then open the endpoint URL.
- **Open an endpoint URL**: the Playground runs on the five endpoint URLs, such as `https://api.azion.com/v4/metrics/graphql`. The API root, `https://api.azion.com/v4`, opens the REST API reference.
- **Send the token header**: a `GET` request with `Accept: text/html` and `Authorization: Token [TOKEN VALUE]` returns GraphiQL with HTTP `200`.

The endpoint URL then opens GraphiQL. For the Playground, refer to [GraphiQL Playground](/en/documentation/devtools/graphql/graphql-playground/).

---

## Related resources

- [Error responses](/en/documentation/devtools/graphql/error-responses.md): Every status code and message the API returns, with the cause of each one.
- [GraphQL API limits](/en/documentation/devtools/graphql/limits.md): The row, field, and time-window bounds that several of these errors enforce.
- [How the GraphQL API works](/en/documentation/devtools/graphql/overview.md): The endpoints, datasets, time windows, and retention that the fixes rely on.
- [GraphQL API quickstart](/en/documentation/devtools/graphql/first-steps.md): Create a personal token and run a first query from GraphiQL or the API.
