# Real user monitoring

> Core Web Vitals measured in your visitors' browsers, LCP, INP, CLS, FCP and TTFB, by page, country, device and more.

Real user monitoring (RUM) measures Core Web Vitals in the visitors' browsers. It is off by default; enable it in the zone's [edge settings](https://developers.openwaf.dev/api/edge-settings/#update-zone-edge) (`rum`). All endpoints share the same filters and use the 75th percentile (p75).

| Operation | Endpoint |
|---|---|
| [Get RUM summary](https://developers.openwaf.dev/api/rum/#get-rum-summary) | `GET /v1/orgs/{org}/zones/{zone}/rum/summary` |
| [Get RUM time series](https://developers.openwaf.dev/api/rum/#get-rum-timeseries) | `GET /v1/orgs/{org}/zones/{zone}/rum/timeseries` |
| [Get RUM breakdown](https://developers.openwaf.dev/api/rum/#get-rum-breakdown) | `GET /v1/orgs/{org}/zones/{zone}/rum/breakdown` |
| [Get LCP parts](https://developers.openwaf.dev/api/rum/#get-rum-lcp-parts) | `GET /v1/orgs/{org}/zones/{zone}/rum/lcp-parts` |
| [Get RUM offenders](https://developers.openwaf.dev/api/rum/#get-rum-offenders) | `GET /v1/orgs/{org}/zones/{zone}/rum/offenders` |
| [Get RUM report](https://developers.openwaf.dev/api/rum/#get-rum-report) | `GET /v1/orgs/{org}/zones/{zone}/rum/report` |

## Get RUM summary

```http
GET /v1/orgs/{org}/zones/{zone}/rum/summary
```

Returns the p75 of LCP, INP, CLS, FCP and TTFB with their rating, against the previous period of the same length, and what the edge did with the measurement script (`injection`). Percentiles are per page view; visits are estimated from the sample rate.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `since` | query | string (date-time) | No | RFC 3339; until minus 7 days by default. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `path` | query | string | No | Only that exact path. At most 512 characters. Pattern `^/`. |
| `country` | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted). Pattern `^[A-Za-z]{2}$`. |
| `node` | query | string | No | Only the views measured through that edge node. At most 128 characters. |
| `device` | query | string | No | Only that device type. One of `mobile`, `tablet`, `desktop`. |
| `connection` | query | string | No | Only that effective connection type. One of `4g`, `3g`, `2g`, `slow-2g`. |
| `nav_type` | query | string | No | Only that navigation type. One of `navigate`, `reload`, `back-forward`, `back-forward-cache`, `prerender`, `restore`. |

#### Response

- `200`: Returns [RumSummary](https://developers.openwaf.dev/api/schemas/#rum-summary).

| Field | Type | Description |
|---|---|---|
| `since` | string (date-time) |  |
| `until` | string (date-time) |  |
| `prev_since` | string (date-time) |  |
| `prev_until` | string (date-time) |  |
| `visits` | number | Estimated page views: Σ 1/sample_rate, rounded. |
| `prev_visits` | number |  |
| `metrics` | object |  |
| `metrics.lcp` | [RumSummaryAgg](https://developers.openwaf.dev/api/schemas/#rum-summary-agg) |  |
| `metrics.inp` | [RumSummaryAgg](https://developers.openwaf.dev/api/schemas/#rum-summary-agg) |  |
| `metrics.cls` | [RumSummaryAgg](https://developers.openwaf.dev/api/schemas/#rum-summary-agg) |  |
| `metrics.fcp` | [RumSummaryAgg](https://developers.openwaf.dev/api/schemas/#rum-summary-agg) |  |
| `metrics.ttfb` | [RumSummaryAgg](https://developers.openwaf.dev/api/schemas/#rum-summary-agg) |  |
| `injection` | object |  |
| `injection.injected` | integer |  |
| `injection.csp_nonce` | integer |  |
| `injection.csp_blocked` | integer |  |
| `injection.no_head` | integer |  |
| `injection.sampled_out` | integer |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rum/summary" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getRumSummary({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_rum_summary(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "since": "2026-10-27T09:30:00Z",
  "until": "2026-10-27T09:30:00Z",
  "prev_since": "2026-10-27T09:30:00Z",
  "prev_until": "2026-10-27T09:30:00Z",
  "visits": 128.0,
  "prev_visits": 128.0,
  "metrics": {
    "lcp": {
      "p50": 0.5,
      "p75": 0.5,
      "p90": 0.5,
      "count": 128,
      "good": 0,
      "needs_improvement": 0,
      "poor": 0,
      "prev_p75": 0.5,
      "prev_count": 128
    },
    "inp": {
      "p50": 0.5,
      "p75": 0.5,
      "p90": 0.5,
      "count": 128,
      "good": 0,
      "needs_improvement": 0,
      "poor": 0,
      "prev_p75": 0.5,
      "prev_count": 128
    },
    "cls": {
      "p50": 0.5,
      "p75": 0.5,
      "p90": 0.5,
      "count": 128,
      "good": 0,
      "needs_improvement": 0,
      "poor": 0,
      "prev_p75": 0.5,
      "prev_count": 128
    },
    "fcp": {
      "p50": 0.5,
      "p75": 0.5,
      "p90": 0.5,
      "count": 128,
      "good": 0,
      "needs_improvement": 0,
      "poor": 0,
      "prev_p75": 0.5,
      "prev_count": 128
    },
    "ttfb": {
      "p50": 0.5,
      "p75": 0.5,
      "p90": 0.5,
      "count": 128,
      "good": 0,
      "needs_improvement": 0,
      "poor": 0,
      "prev_p75": 0.5,
      "prev_count": 128
    }
  },
  "injection": {
    "injected": 0,
    "csp_nonce": 0,
    "csp_blocked": 0,
    "no_head": 0,
    "sampled_out": 0
  }
}
```

## Get RUM time series

```http
GET /v1/orgs/{org}/zones/{zone}/rum/timeseries
```

Returns the p75 of each metric and the visits per interval, including empty intervals (with `null` and `0`).

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `since` | query | string (date-time) | No | RFC 3339; until minus 7 days by default. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `path` | query | string | No | Only that exact path. At most 512 characters. Pattern `^/`. |
| `country` | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted). Pattern `^[A-Za-z]{2}$`. |
| `node` | query | string | No | Only the views measured through that edge node. At most 128 characters. |
| `device` | query | string | No | Only that device type. One of `mobile`, `tablet`, `desktop`. |
| `connection` | query | string | No | Only that effective connection type. One of `4g`, `3g`, `2g`, `slow-2g`. |
| `nav_type` | query | string | No | Only that navigation type. One of `navigate`, `reload`, `back-forward`, `back-forward-cache`, `prerender`, `restore`. |
| `step` | query | integer | No | Seconds per point, a multiple of 60, at most 1000 points. By default the smallest of 300, 900, 1800, 3600, 10800, 21600, 43200 and 86400 that gives at most 60 points. Buckets aligned to the Unix epoch (UTC). Minimum 60. |

#### Response

- `200`: Returns [RumTimeseries](https://developers.openwaf.dev/api/schemas/#rum-timeseries).

| Field | Type | Description |
|---|---|---|
| `step` | integer |  |
| `points` | array of object |  |
| `points[].ts` | string (date-time) | Time of the request. |
| `points[].visits` | number |  |
| `points[].lcp` | number or null | p75. |
| `points[].inp` | number or null |  |
| `points[].cls` | number or null |  |
| `points[].fcp` | number or null |  |
| `points[].ttfb` | number or null |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rum/timeseries" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getRumTimeseries({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_rum_timeseries(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "step": 0,
  "points": [
    {
      "ts": "2026-10-27T09:30:00Z",
      "visits": 128.0,
      "lcp": 0.5,
      "inp": 0.5,
      "cls": 0.5,
      "fcp": 0.5,
      "ttfb": 0.5
    }
  ]
}
```

## Get RUM breakdown

```http
GET /v1/orgs/{org}/zones/{zone}/rum/breakdown
```

Returns the metrics per value of one dimension (`by`), sorted by visits (ties by key).

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `since` | query | string (date-time) | No | RFC 3339; until minus 7 days by default. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `path` | query | string | No | Only that exact path. At most 512 characters. Pattern `^/`. |
| `country` | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted). Pattern `^[A-Za-z]{2}$`. |
| `node` | query | string | No | Only the views measured through that edge node. At most 128 characters. |
| `device` | query | string | No | Only that device type. One of `mobile`, `tablet`, `desktop`. |
| `connection` | query | string | No | Only that effective connection type. One of `4g`, `3g`, `2g`, `slow-2g`. |
| `nav_type` | query | string | No | Only that navigation type. One of `navigate`, `reload`, `back-forward`, `back-forward-cache`, `prerender`, `restore`. |
| `by` | query | string | No | Dimension to group by. One of `path`, `country`, `node`, `device`, `connection`, `nav_type`, `host`, `viewport`. Default `path`. |
| `limit` | query | integer | No | Maximum number of items. Default `10`. From 1 to 100. |

#### Response

- `200`: Returns [RumBreakdown](https://developers.openwaf.dev/api/schemas/#rum-breakdown).

| Field | Type | Description |
|---|---|---|
| `by` | string |  |
| `rows` | array of object |  |
| `rows[].key` | string | Value of the dimension ("" = unknown). |
| `rows[].visits` | number |  |
| `rows[].metrics` | [RumMetrics](https://developers.openwaf.dev/api/schemas/#rum-metrics) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rum/breakdown?by=country" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getRumBreakdown({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  by: "country"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_rum_breakdown(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", by="country")
```



```json
{
  "by": "path",
  "rows": [
    {
      "key": "/pricing",
      "visits": 128.0,
      "metrics": {
        "lcp": {},
        "inp": {},
        "cls": {},
        "fcp": {},
        "ttfb": {}
      }
    }
  ]
}
```

## Get LCP parts

```http
GET /v1/orgs/{org}/zones/{zone}/rum/lcp-parts
```

Splits LCP into its parts (TTFB, resource load delay, resource load time and render delay) for the slow views (LCP at or above p75) and for all views. `by` adds `rows` for the 10 devices or paths with most views.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `since` | query | string (date-time) | No | RFC 3339; until minus 7 days by default. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `path` | query | string | No | Only that exact path. At most 512 characters. Pattern `^/`. |
| `country` | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted). Pattern `^[A-Za-z]{2}$`. |
| `node` | query | string | No | Only the views measured through that edge node. At most 128 characters. |
| `device` | query | string | No | Only that device type. One of `mobile`, `tablet`, `desktop`. |
| `connection` | query | string | No | Only that effective connection type. One of `4g`, `3g`, `2g`, `slow-2g`. |
| `nav_type` | query | string | No | Only that navigation type. One of `navigate`, `reload`, `back-forward`, `back-forward-cache`, `prerender`, `restore`. |
| `by` | query | string | No | Adds `rows` (the 10 with most views). One of `device`, `path`. |

#### Response

- `200`: Returns [RumLcpParts](https://developers.openwaf.dev/api/schemas/#rum-lcp-parts).

| Field | Type | Description |
|---|---|---|
| `lcp_p75` | number or null |  |
| `count` | integer |  |
| `parts` | [RumLcpPartsValues](https://developers.openwaf.dev/api/schemas/#rum-lcp-parts-values) | Views with LCP ≥ p75. |
| `all` | [RumLcpPartsValues](https://developers.openwaf.dev/api/schemas/#rum-lcp-parts-values) |  |
| `rows` | array of object |  |
| `rows[].key` | string |  |
| `rows[].lcp_p75` | number or null |  |
| `rows[].count` | integer |  |
| `rows[].parts` | [RumLcpPartsValues](https://developers.openwaf.dev/api/schemas/#rum-lcp-parts-values) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rum/lcp-parts" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getRumLcpParts({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_rum_lcp_parts(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "lcp_p75": 0.5,
  "count": 128,
  "parts": {
    "ttfb": 0.5,
    "load_delay": 0.5,
    "load_duration": 0.5,
    "render_delay": 0.5
  },
  "all": {
    "ttfb": 0.5,
    "load_delay": 0.5,
    "load_duration": 0.5,
    "render_delay": 0.5
  },
  "rows": [
    {
      "key": "/pricing",
      "lcp_p75": 0.5,
      "count": 128,
      "parts": {
        "ttfb": 0.5,
        "load_delay": 0.5,
        "load_duration": 0.5,
        "render_delay": 0.5
      }
    }
  ]
}
```

## Get RUM offenders

```http
GET /v1/orgs/{org}/zones/{zone}/rum/offenders
```

Ranks the elements, resources and scripts with most poor views of their metric (then most views), without the empty key. The keys (selectors, URLs) come from the visitors' pages.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `since` | query | string (date-time) | No | RFC 3339; until minus 7 days by default. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `path` | query | string | No | Only that exact path. At most 512 characters. Pattern `^/`. |
| `country` | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted). Pattern `^[A-Za-z]{2}$`. |
| `node` | query | string | No | Only the views measured through that edge node. At most 128 characters. |
| `device` | query | string | No | Only that device type. One of `mobile`, `tablet`, `desktop`. |
| `connection` | query | string | No | Only that effective connection type. One of `4g`, `3g`, `2g`, `slow-2g`. |
| `nav_type` | query | string | No | Only that navigation type. One of `navigate`, `reload`, `back-forward`, `back-forward-cache`, `prerender`, `restore`. |
| `kind` | query | string | Yes | What to rank. One of `lcp_element`, `lcp_url`, `inp_target`, `inp_script`, `cls_target`. |
| `limit` | query | integer | No | Maximum number of items. Default `10`. From 1 to 100. |

#### Response

- `200`: Returns an object with the fields below.

| Field | Type | Description |
|---|---|---|
| `kind` | string |  |
| `rows` | array of [RumOffender](https://developers.openwaf.dev/api/schemas/#rum-offender) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rum/offenders?kind=lcp_element" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getRumOffenders({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d",
  kind: "lcp_element"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_rum_offenders(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d", kind="lcp_element")
```



```json
{
  "kind": "example",
  "rows": [
    {
      "key": "/pricing",
      "count": 128,
      "p75": 0.5,
      "poor": 0,
      "top_path": "/docs/"
    }
  ]
}
```

## Get RUM report

```http
GET /v1/orgs/{org}/zones/{zone}/rum/report
```

Returns in one call everything the dashboard's real user monitoring page shows, with the zone's RUM settings, the summary, a time series with automatic step, breakdowns by path (25 rows), country, node, device and connection (10 rows), LCP parts by device and the top offenders. `breakdowns.device` ignores the `device` filter.

**Required role:** `readonly` or higher.

#### Parameters

| Name | In | Type | Required | Description |
|---|---|---|---|---|
| `org` | path | string (uuid) | Yes | Organization id. Every route checks that it is the token's organization (otherwise `404`). |
| `zone` | path | string (uuid) | Yes | Zone id (see [List zones](https://developers.openwaf.dev/api/zones/#list-zones)). |
| `since` | query | string (date-time) | No | RFC 3339; until minus 7 days by default. |
| `until` | query | string (date-time) | No | RFC 3339, exclusive; now by default. At most 31 days of range. |
| `host` | query | string | No | Only that name. At most 253 characters. |
| `path` | query | string | No | Only that exact path. At most 512 characters. Pattern `^/`. |
| `country` | query | string | No | ISO 3166-1 alpha-2 (lowercase accepted). Pattern `^[A-Za-z]{2}$`. |
| `node` | query | string | No | Only the views measured through that edge node. At most 128 characters. |
| `device` | query | string | No | Only that device type. One of `mobile`, `tablet`, `desktop`. |
| `connection` | query | string | No | Only that effective connection type. One of `4g`, `3g`, `2g`, `slow-2g`. |
| `nav_type` | query | string | No | Only that navigation type. One of `navigate`, `reload`, `back-forward`, `back-forward-cache`, `prerender`, `restore`. |

#### Response

- `200`: Returns [RumReport](https://developers.openwaf.dev/api/schemas/#rum-report).

| Field | Type | Description |
|---|---|---|
| `summary` | [RumSummary](https://developers.openwaf.dev/api/schemas/#rum-summary) |  |
| `timeseries` | [RumTimeseries](https://developers.openwaf.dev/api/schemas/#rum-timeseries) |  |
| `breakdowns` | object |  |
| `breakdowns.path` | [RumBreakdown](https://developers.openwaf.dev/api/schemas/#rum-breakdown) |  |
| `breakdowns.country` | [RumBreakdown](https://developers.openwaf.dev/api/schemas/#rum-breakdown) |  |
| `breakdowns.node` | [RumBreakdown](https://developers.openwaf.dev/api/schemas/#rum-breakdown) |  |
| `breakdowns.device` | [RumBreakdown](https://developers.openwaf.dev/api/schemas/#rum-breakdown) |  |
| `breakdowns.connection` | [RumBreakdown](https://developers.openwaf.dev/api/schemas/#rum-breakdown) |  |
| `lcp_parts` | [RumLcpParts](https://developers.openwaf.dev/api/schemas/#rum-lcp-parts) |  |
| `offenders` | object |  |
| `offenders.lcp_element` | array of [RumOffender](https://developers.openwaf.dev/api/schemas/#rum-offender) |  |
| `offenders.inp_target` | array of [RumOffender](https://developers.openwaf.dev/api/schemas/#rum-offender) |  |
| `offenders.inp_script` | array of [RumOffender](https://developers.openwaf.dev/api/schemas/#rum-offender) |  |
| `offenders.cls_target` | array of [RumOffender](https://developers.openwaf.dev/api/schemas/#rum-offender) |  |
| `settings` | [ZoneEdgeRUM](https://developers.openwaf.dev/api/schemas/#zone-edge-rum) |  |

#### Errors

| Status | Description |
|---|---|
| `400` | Malformed request (`bad_request`). |
| `404` | Not found, or not visible to the token (`not_found`). |
| `503` | Not available right now (`unavailable`); retry later. |

#### Example

**curl**


```sh
curl "https://dash.openwaf.dev/v1/orgs/$OPENWAF_ORG/zones/6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d/rum/report" \
  -H "Authorization: Bearer $OPENWAF_TOKEN"
```

**TypeScript**


```ts
import { OpenWAF } from "./openwaf";

const owf = new OpenWAF({ baseUrl: "https://dash.openwaf.dev" }); // token from OPENWAF_TOKEN
const result = await owf.getRumReport({
  zone: "6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d"
});
```

**Python**


```py
from openwaf import OpenWAF

owf = OpenWAF(base_url="https://dash.openwaf.dev")  # token from OPENWAF_TOKEN
result = owf.get_rum_report(zone="6f1d2c3b-8a4e-4b7d-9c21-5e3f7a8b9c0d")
```



```json
{
  "summary": {
    "since": "2026-10-27T09:30:00Z",
    "until": "2026-10-27T09:30:00Z",
    "prev_since": "2026-10-27T09:30:00Z",
    "prev_until": "2026-10-27T09:30:00Z",
    "visits": 128.0,
    "prev_visits": 128.0,
    "metrics": {
      "lcp": {
        "p50": 0.5,
        "p75": 0.5,
        "p90": 0.5,
        "count": 128,
        "good": 0,
        "needs_improvement": 0,
        "poor": 0,
        "prev_p75": 0.5,
        "prev_count": 128
      },
      "inp": {
        "p50": 0.5,
        "p75": 0.5,
        "p90": 0.5,
        "count": 128,
        "good": 0,
        "needs_improvement": 0,
        "poor": 0,
        "prev_p75": 0.5,
        "prev_count": 128
      },
      "cls": {
        "p50": 0.5,
        "p75": 0.5,
        "p90": 0.5,
        "count": 128,
        "good": 0,
        "needs_improvement": 0,
        "poor": 0,
        "prev_p75": 0.5,
        "prev_count": 128
      },
      "fcp": {
        "p50": 0.5,
        "p75": 0.5,
        "p90": 0.5,
        "count": 128,
        "good": 0,
        "needs_improvement": 0,
        "poor": 0,
        "prev_p75": 0.5,
        "prev_count": 128
      },
      "ttfb": {
        "p50": 0.5,
        "p75": 0.5,
        "p90": 0.5,
        "count": 128,
        "good": 0,
        "needs_improvement": 0,
        "poor": 0,
        "prev_p75": 0.5,
        "prev_count": 128
      }
    },
    "injection": {
      "injected": 0,
      "csp_nonce": 0,
      "csp_blocked": 0,
      "no_head": 0,
      "sampled_out": 0
    }
  },
  "timeseries": {
    "step": 0,
    "points": [
      {
        "ts": "2026-10-27T09:30:00Z",
        "visits": 128.0,
        "lcp": 0.5,
        "inp": 0.5,
        "cls": 0.5,
        "fcp": 0.5,
        "ttfb": 0.5
      }
    ]
  },
  "breakdowns": {
    "path": {
      "by": "path",
      "rows": [
        {}
      ]
    },
    "country": {
      "by": "path",
      "rows": [
        {}
      ]
    },
    "node": {
      "by": "path",
      "rows": [
        {}
      ]
    },
    "device": {
      "by": "path",
      "rows": [
        {}
      ]
    },
    "connection": {
      "by": "path",
      "rows": [
        {}
      ]
    }
  },
  "lcp_parts": {
    "lcp_p75": 0.5,
    "count": 128,
    "parts": {
      "ttfb": 0.5,
      "load_delay": 0.5,
      "load_duration": 0.5,
      "render_delay": 0.5
    },
    "all": {
      "ttfb": 0.5,
      "load_delay": 0.5,
      "load_duration": 0.5,
      "render_delay": 0.5
    },
    "rows": [
      {
        "key": "/pricing",
        "lcp_p75": 0.5,
        "count": 128,
        "parts": {}
      }
    ]
  },
  "offenders": {
    "lcp_element": [
      {
        "key": "/pricing",
        "count": 128,
        "p75": 0.5,
        "poor": 0,
        "top_path": "/docs/"
      }
    ],
    "inp_target": [
      {
        "key": "/pricing",
        "count": 128,
        "p75": 0.5,
        "poor": 0,
        "top_path": "/docs/"
      }
    ],
    "inp_script": [
      {
        "key": "/pricing",
        "count": 128,
        "p75": 0.5,
        "poor": 0,
        "top_path": "/docs/"
      }
    ],
    "cls_target": [
      {
        "key": "/pricing",
        "count": 128,
        "p75": 0.5,
        "poor": 0,
        "top_path": "/docs/"
      }
    ]
  },
  "settings": {
    "enabled": false,
    "sample_rate": 1,
    "exclude_paths": []
  }
}
```
