# GraphQL Analytics API Reference

## Query Root

The schema has a single entry point: `Query.viewer`. Mutations are not supported.

```graphql
{
  cost       # uint64 -- query cost (returned in response)
  viewer {
    budget   # uint64 -- remaining budget
    zones(filter: { zoneTag: "..." }) { ... }
    accounts(filter: { accountTag: "..." }) { ... }
  }
}
```

## Aggregation Fields

Aggregated dataset nodes (`*Groups`) return these field categories. Not every node has all — use introspection to check.

### count

Total events in the group. Available on `*Groups` nodes but **not** on raw `*Adaptive` nodes (e.g., `workersInvocationsAdaptive` — use `sum { requests }` instead).

### sum

Cumulative metrics. Fields vary by dataset:

```graphql
# HTTP requests
sum { edgeResponseBytes edgeRequestBytes visits edgeTimeToFirstByteMs originResponseDurationMs }

# Workers invocations
sum { requests errors subrequests cpuTimeUs wallTime duration responseBodySize clientDisconnects requestDuration }
```

### quantiles

Percentile distributions (on datasets like `workersInvocationsAdaptive`). Available percentiles: P25, P50, P75, P90, P95, P99, P999 for `cpuTime`, `wallTime`, `requestDuration`, `duration`, `responseBodySize`.

```graphql
quantiles { cpuTimeP50 cpuTimeP99 wallTimeP50 wallTimeP99 }
```

### ratio, avg, uniq, confidence

```graphql
ratio { status4xx status5xx }      # float64 (0 to 1) -- HTTP datasets only
avg { sampleInterval }              # useful for understanding sampling resolution
uniq { uniques }                    # unique IP count -- rollup datasets (*1hGroups, *1dGroups) only
confidence(level: 0.95) {           # Adaptive datasets only; works on count and sum fields
  count { estimate lower upper sampleSize }
}
```

## Dimensions

Dimensions are fields you can group by via the `dimensions` sub-selection.

### Time Dimensions

| Dimension                | Granularity     |
| ------------------------ | --------------- |
| `date`                   | Day             |
| `datetime`               | Exact timestamp |
| `datetimeMinute`         | 1 minute        |
| `datetimeFiveMinutes`    | 5 minutes       |
| `datetimeFifteenMinutes` | 15 minutes      |
| `datetimeHour`           | 1 hour          |

Workers datasets also support `datetimeSixHours`.

### HTTP Request Dimensions (httpRequestsAdaptiveGroups)

83 dimensions available. Key ones:

| Dimension                            | Description                             |
| ------------------------------------ | --------------------------------------- |
| `clientCountryName`                  | Country of origin                       |
| `clientRequestHTTPHost`              | Requested hostname                      |
| `clientRequestHTTPMethodName`        | HTTP method                             |
| `clientRequestPath`                  | URI path                                |
| `edgeResponseStatus`                 | Edge HTTP status code                   |
| `cacheStatus`                        | Cache status (hit, miss, dynamic, etc.) |
| `coloCode`                           | Cloudflare datacenter IATA code         |
| `clientIP` / `clientAsn`             | Client IP address / ASN                 |
| `botScore` / `botManagementDecision` | Bot management score (0-99) / verdict   |
| `wafAttackScore` / `securityAction`  | WAF score / firewall action taken       |
| `ja3Hash` / `ja4`                    | TLS fingerprints                        |
| `sampleInterval`                     | ABR sample interval                     |

### Workers Dimensions (workersInvocationsAdaptive)

`scriptName`, `scriptTag`, `scriptVersion`, `environmentName`, `status`, `usageModel`, `coloCode`, `dispatchNamespaceName`, `isDispatcher`

### Firewall Dimensions (firewallEventsAdaptive)

`action`, `source`, `ruleId`, `clientCountryName`, `clientIP`, `clientAsn`, `userAgent`

## Filtering

### Scope Filters

```graphql
zones(filter: { zoneTag: "ZONE_ID" })           # up to 10 zones
zones(filter: { zoneTag_in: ["Z1", "Z2"] })
accounts(filter: { accountTag: "ACCOUNT_ID" })   # exactly 1 account
```

### Dataset Filters

**Always include a time range filter.** Multiple filters at the same level are implicitly AND-ed.

```graphql
httpRequestsAdaptiveGroups(
  filter: { datetime_gt: "2025-01-01T00:00:00Z", datetime_lt: "2025-01-02T00:00:00Z", clientCountryName: "US" }
  limit: 1000
)
```

### Filter Operators

| Operator                       | Meaning               | Example                              |
| ------------------------------ | --------------------- | ------------------------------------ |
| (none)                         | equals                | `clientCountryName: "US"`            |
| `_gt` / `_lt`                  | greater / less than   | `datetime_gt: "..."`                 |
| `_geq` / `_leq`                | greater/less or equal | `datetime_geq: "..."`                |
| `_neq`                         | not equal             | `cacheStatus_neq: "hit"`             |
| `_in` / `_notin`               | in / not in list      | `clientCountryName_in: ["US", "GB"]` |
| `_like` / `_notlike`           | SQL LIKE with `%`     | `clientRequestPath_like: "/api/%"`   |
| `_has` / `_hasall` / `_hasany` | array contains        | `botDetectionIds_has: "abc"`         |

> `_notin` and `_notlike` are in the schema but not in official docs. Confirmed via introspection.

### Boolean Operators (AND / OR)

```graphql
# Explicit AND
filter: { AND: [{ datetime_gt: "..." }, { datetime_lt: "..." }, { clientCountryName: "US" }] }

# Explicit OR
filter: { datetime_gt: "...", OR: [{ edgeResponseStatus: 403 }, { edgeResponseStatus: 429 }] }
```

## Pagination & Sorting

No cursor-based pagination. Use `limit`, `orderBy`, and filter-based offsets:

```graphql
# First page
httpRequestsAdaptiveGroups(filter: { datetime_gt: "..." }, limit: 100, orderBy: [datetime_ASC])

# Next page: filter by last seen value from previous page
httpRequestsAdaptiveGroups(filter: { datetime_gt: "2025-01-01T01:35:00Z" }, limit: 100, orderBy: [datetime_ASC])
```

Sort with `orderBy: [field_ASC]` or `[field_DESC]`. Multiple sort fields supported.

## Settings Node

Query per-node limits and availability:

```graphql
viewer { zones(filter: { zoneTag: "..." }) { settings {
  httpRequestsAdaptiveGroups { enabled maxDuration maxNumberOfFields maxPageSize notOlderThan }
} } }
```

## See Also

- [README.md](README.md) - Overview, decision tree, dataset index
- [configuration.md](configuration.md) - Authentication, client setup, introspection queries
- [patterns.md](patterns.md) - Common query patterns (time-series, top-N, per-product)
- [gotchas.md](gotchas.md) - Rate limits, sampling, troubleshooting
