---
title: Analytics
description: Query Cloudflare Realtime TURN usage metrics and traffic data via the GraphQL API.
image: https://developers.cloudflare.com/dev-products-preview.png
---

> Documentation Index  
> Fetch the complete documentation index at: https://developers.cloudflare.com/realtime/llms.txt  
> Use this file to discover all available pages before exploring further.

[Skip to content](#%5Ftop) 

# Analytics

Cloudflare Realtime TURN service counts ingress and egress usage in bytes. You can access this real-time and historical data using the TURN analytics API. You can see TURN usage data in a time series or aggregate that shows traffic in bytes over time.

Cloudflare TURN analytics is available over the GraphQL API only.

API token permissions

You will need the "Account Analytics" permission on your API token to make queries to the Realtime GraphQL API.

Note

See [GraphQL API](https://developers.cloudflare.com/analytics/graphql-api/) for more information on how to set up your GraphQL client. The examples below use the same GraphQL endpoint at `https://api.cloudflare.com/client/v4/graphql`.

## Available metrics and dimensions

TURN analytics provides rich data that you can query and aggregate in various ways.

### Metrics

You can query the following metrics:

* **egressBytes**: Total bytes sent from TURN servers to clients
* **ingressBytes**: Total bytes received by TURN servers from clients
* **concurrentConnections**: Average number of concurrent connections

These metrics support aggregations using `sum` and `avg` functions.

### Dimensions

You can break down your data by the following dimensions:

* **Time aggregations**: `datetime`, `datetimeMinute`, `datetimeFiveMinutes`, `datetimeFifteenMinutes`, `datetimeHour`
* **Geographic**: `datacenterCity`, `datacenterCountry`, `datacenterRegion` (Cloudflare data center location)
* **Identity**: `keyId`, `customIdentifier`, `username`

### Filters

You can filter the data in TURN analytics on:

* Datetime range
* TURN Key ID
* TURN Username
* Custom identifier

Note

[Custom identifiers](https://developers.cloudflare.com/realtime/turn/replacing-existing/#tag-users-with-custom-identifiers) are useful for accounting usage for different users in your system.

## GraphQL clients

GraphQL is a self-documenting protocol. You can use any GraphQL client to explore the schema and available fields. Popular options include:

* **[Altair ↗](https://altairgraphql.dev/)**: A feature-rich GraphQL client with schema documentation explorer
* **[GraphiQL ↗](https://github.com/graphql/graphiql)**: The original GraphQL IDE
* **[Postman ↗](https://www.postman.com/)**: Supports GraphQL queries with schema introspection

To explore the full schema, configure your client to connect to `https://api.cloudflare.com/client/v4/graphql` with your API credentials. Refer to [Explore the GraphQL schema](https://developers.cloudflare.com/analytics/graphql-api/getting-started/explore-graphql-schema/) for detailed instructions.

## Useful TURN analytics queries

Below are some example queries for common usecases. You can modify them to adapt your use case and get different views to the analytics data.

### Concurrent connections with data usage over time

This comprehensive query shows how to retrieve multiple metrics simultaneously, including concurrent connections, egress, and ingress bytes in 5-minute intervals. This is useful for building dashboards and monitoring real-time usage.

```

query concurrentConnections {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        limit: 10000

        filter: { date_geq: $dateFrom, date_leq: $dateTo }

      ) {

        dimensions {

          datetimeFiveMinutes

        }

        avg {

          concurrentConnectionsFiveMinutes

        }

        sum {

          egressBytes

          ingressBytes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 816

              },

              "dimensions": {

                "datetimeFiveMinutes": "2025-12-02T03:45:00Z"

              },

              "sum": {

                "egressBytes": 207314144,

                "ingressBytes": 8534200

              }

            },

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 1945

              },

              "dimensions": {

                "datetimeFiveMinutes": "2025-12-02T16:00:00Z"

              },

              "sum": {

                "egressBytes": 462909020,

                "ingressBytes": 128434592

              }

            },


          ]

        }

      ]

    }

  ]

}


```

### Top TURN keys by egress

```

query egressByTurnKey{

  viewer {

    usage: accounts(filter: { accountTag: $accountId }) {

        callsTurnUsageAdaptiveGroups(

          filter: {

          date_geq: $dateFrom,

          date_leq: $dateTo

        }

          limit: 2

          orderBy: [sum_egressBytes_DESC]

        ) {

          dimensions {

            keyId

          }

          sum {

            egressBytes

          }

        }

      }

    },

    "errors": null

  }


```

Example response:

```

{

  "data": {

    "viewer": {

      "usage": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "dimensions": {

                "keyId": "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

              },

              "sum": {

                "egressBytes": 160040068147

              }

            }

          ]

        }

      ]

    }

  },

  "errors": null

}


```

### Top TURN custom identifiers

```

query topTurnCustomIdentifiers {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        filter: { date_geq: $dateFrom, date_leq: $dateTo }

        limit: 1

        orderBy: [sum_egressBytes_DESC]

      ) {

        dimensions {

          customIdentifier

        }

        sum {

          egressBytes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "dimensions": {

                "customIdentifier": "some identifier"

              },

              "sum": {

                "egressBytes": 160040068147

              }

            }

          ]

        }

      ]

    }

  },

  "errors": null

}


```

### Usage for a specific custom identifier

```

query {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        filter: {

          date_geq: $dateFrom

          date_leq: $dateTo

          customIdentifier: "tango"

        }

        limit: 100

        orderBy: []

      ) {

        dimensions {

          keyId

          customIdentifier

        }

        sum {

          egressBytes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "usage": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "dimensions": {

                "customIdentifier": "tango",

                "keyId": "74007022d80d7ebac4815fb776b9d3ed"

              },

              "sum": {

                "egressBytes": 162641324

              }

            }

          ]

        }

      ]

    }

  },

  "errors": null

}


```

### Usage as a timeseries (for graphs)

```

query {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        filter: { date_geq: $dateFrom, date_leq: $dateTo }

        limit: 100

        orderBy: [datetimeMinute_ASC]

      ) {

        dimensions {

          datetimeMinute

        }

        sum {

          egressBytes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "dimensions": {

                "datetimeMinute": "2025-12-01T00:00:00Z"

              },

              "sum": {

                "egressBytes": 159512

              }

            },

            {

              "dimensions": {

                "datetimeMinute": "2025-12-01T00:01:00Z"

              },

              "sum": {

                "egressBytes": 133818

              }

            },

            ... (more data here)

           ]

        }

      ]

    }

  },

  "errors": null

}


```

### Usage breakdown by geographic location

You can break down usage data by Cloudflare data center location to understand where your TURN traffic is being served. This is useful for optimizing regional capacity and understanding geographic distribution of your users.

```

query {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        limit: 100

        filter: { date_geq: $dateFrom, date_leq: $dateTo }

        orderBy: [sum_egressBytes_DESC]

      ) {

        dimensions {

          datacenterCity

          datacenterCode

          datacenterRegion

          datacenterCountry

        }

        sum {

          egressBytes

          ingressBytes

        }

        avg {

          concurrentConnectionsFiveMinutes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 3135

              },

              "dimensions": {

                "datacenterCity": "Columbus",

                "datacenterCode": "CMH",

                "datacenterCountry": "US",

                "datacenterRegion": "ENAM"

              },

              "sum": {

                "egressBytes": 47720931316,

                "ingressBytes": 19351966366

              }

            },

            ...

          ]

        }

      ]

    }

  },

  "errors": null

}


```

### Filter by specific key or identifier

You can filter data to analyze a specific TURN key or custom identifier. This is useful for debugging specific connections or analyzing usage patterns for particular clients.

```

query {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        limit: 1000

        filter: {

          keyId: "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

          date_geq: $dateFrom

          date_leq: $dateTo

        }

        orderBy: [datetimeFiveMinutes_ASC]

      ) {

        dimensions {

          datetimeFiveMinutes

          keyId

        }

        sum {

          egressBytes

          ingressBytes

        }

        avg {

          concurrentConnectionsFiveMinutes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 130

              },

              "dimensions": {

                "datetimeFiveMinutes": "2025-12-01T00:00:00Z",

                "keyId": "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

              },

              "sum": {

                "egressBytes": 609156,

                "ingressBytes": 464326

              }

            },

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 118

              },

              "dimensions": {

                "datetimeFiveMinutes": "2025-12-01T00:05:00Z",

                "keyId": "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

              },

              "sum": {

                "egressBytes": 534948,

                "ingressBytes": 401286

              }

            },

            ...

          ]

        }

      ]

    }

  },

  "errors": null

}


```

### Time aggregation options

You can choose different time aggregation intervals depending on your analysis needs:

* **`datetimeMinute`**: 1-minute intervals (most granular)
* **`datetimeFiveMinutes`**: 5-minute intervals (recommended for dashboards)
* **`datetimeFifteenMinutes`**: 15-minute intervals
* **`datetimeHour`**: Hourly intervals (best for long-term trends)

Example query with hourly aggregation:

```

query {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        limit: 1000

        filter: {

          keyId: "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

          date_geq: $dateFrom

          date_leq: $dateTo

        }

        orderBy: [datetimeFiveMinutes_ASC]

      ) {

        dimensions {

          datetimeFiveMinutes

          keyId

        }

        sum {

          egressBytes

          ingressBytes

        }

        avg {

          concurrentConnectionsFiveMinutes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 130

              },

              "dimensions": {

                "datetimeFiveMinutes": "2025-12-01T00:00:00Z",

                "keyId": "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

              },

              "sum": {

                "egressBytes": 609156,

                "ingressBytes": 464326

              }

            },

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 118

              },

              "dimensions": {

                "datetimeFiveMinutes": "2025-12-01T00:05:00Z",

                "keyId": "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

              },

              "sum": {

                "egressBytes": 534948,

                "ingressBytes": 401286

              }

            },

            ...

          ]

        }

      ]

    }

  },

  "errors": null

}


```

## Advanced use cases

### Combining multiple dimensions

You can combine multiple dimensions in a single query to get more detailed breakdowns. For example, to see usage by both time and location:

```

query {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        limit: 10000

        filter: { date_geq: $dateFrom, date_leq: $dateTo }

        orderBy: [datetimeHour_ASC, sum_egressBytes_DESC]

      ) {

        dimensions {

          datetimeHour

          datacenterCity

          datacenterCountry

        }

        sum {

          egressBytes

          ingressBytes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "dimensions": {

                "datacenterCity": "Chennai",

                "datacenterCountry": "IN",

                "datetimeHour": "2025-12-01T00:00:00Z"

              },

              "sum": {

                "egressBytes": 3416216,

                "ingressBytes": 498927214

              }

            },

            {

              "dimensions": {

                "datacenterCity": "Mumbai",

                "datacenterCountry": "IN",

                "datetimeHour": "2025-12-01T00:00:00Z"

              },

              "sum": {

                "egressBytes": 1267076,

                "ingressBytes": 1140140

              }

            },

            ...

          ]

        }

      ]

    }

  },

  "errors": null

}


```

### Identifying top consumers

To find which keys or custom identifiers are using the most bandwidth:

```

query {

  viewer {

    accounts(filter: { accountTag: $accountId }) {

      callsTurnUsageAdaptiveGroups(

        limit: 10

        filter: { date_geq: $dateFrom, date_leq: $dateTo }

        orderBy: [sum_egressBytes_DESC, sum_ingressBytes_DESC]

      ) {

        dimensions {

          keyId

          customIdentifier

        }

        sum {

          egressBytes

          ingressBytes

        }

        avg {

          concurrentConnectionsFiveMinutes

        }

      }

    }

  }

}


```

Example response:

```

{

  "data": {

    "viewer": {

      "accounts": [

        {

          "callsTurnUsageAdaptiveGroups": [

            {

              "avg": {

                "concurrentConnectionsFiveMinutes": 837305

              },

              "dimensions": {

                "customIdentifier": "",

                "keyId": "82a58d0aeabfa8f4a4e0c4a9efc9cda5"

              },

              "sum": {

                "egressBytes": 160040068147,

                "ingressBytes": 154955460564

              }

            }

          ]

        }

      ]

    }

  },

  "errors": null

}


```

## Schema exploration

The GraphQL Analytics API is self-documenting. You can use introspection to discover all available fields, filters, and capabilities for `callsTurnUsageAdaptiveGroups`. Using a GraphQL client like Altair or GraphiQL, you can browse the schema interactively to find additional dimensions and metrics that may be useful for your specific use case.

For more information on GraphQL introspection and schema exploration, refer to:

* [Explore the GraphQL schema](https://developers.cloudflare.com/analytics/graphql-api/getting-started/explore-graphql-schema/)
* [GraphQL introspection](https://developers.cloudflare.com/analytics/graphql-api/features/discovery/introspection/)

```json
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"item":{"@id":"/directory/","name":"Directory"}},{"@type":"ListItem","position":2,"item":{"@id":"/realtime/","name":"Realtime"}},{"@type":"ListItem","position":3,"item":{"@id":"/realtime/turn/","name":"TURN Service"}},{"@type":"ListItem","position":4,"item":{"@id":"/realtime/turn/analytics/","name":"Analytics"}}]}
```
