---
title: Sites API
description: Create and manage Real User Monitoring (RUM) sites to track web performance and errors via the Cronitor API.
---

The Sites API allows you to programmatically create and manage Real User Monitoring (RUM) sites. Sites collect web vitals, performance metrics, and JavaScript errors from your web applications. {% .lead %}

## Authentication

The Sites API requires API key authentication with site scopes:
- **`site:read`** - Required to list and retrieve sites
- **`site:write`** - Required to create, update, and delete sites

Create an API key with the appropriate scopes from the [API Settings](/app/settings/api) page. For more information about API authentication and scopes, see the [API documentation](/docs/api).

---

## Quick Start

### Create a Site
```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "name": "Main Website",
        "webvitals_enabled": true,
        "errors_enabled": true
    }' \
    https://cronitor.io/api/sites
```

### List All Sites
```bash
curl https://cronitor.io/api/sites -u API_KEY:
```

### Get Installation Snippet
```bash
curl "https://cronitor.io/api/sites/site_abc123?withSnippet=true" -u API_KEY:
```

---

## Sites

### List Sites

Retrieve all RUM sites for your organization.

#### Endpoint
```bash
GET https://cronitor.io/api/sites
```

#### Query Parameters
- **`page`** (integer) - Page number for pagination (default: 1)
- **`pageSize`** (integer) - Number of results per page (default: 50)

#### Example
```bash
curl https://cronitor.io/api/sites -u API_KEY:
```

#### Response
```json
{
  "page": 1,
  "page_size": 50,
  "total_count": 2,
  "data": [
    {
      "key": "site_abc123def456",
      "name": "Main Website",
      "client_key": "ck_abc123def456",
      "public_report_enabled": false,
      "public_report_key": "pr_abc123def456",
      "webvitals_enabled": true,
      "errors_enabled": true,
      "filter_localhost": true,
      "filter_bots": true,
      "filter_browser_extensions": true,
      "exclude_internal_referrers": false,
      "origin_allowlist": null,
      "ip_denylist": null,
      "country_code_denylist": null,
      "path_denylist": null,
      "sampling": 100,
      "ingestion_enabled": true,
      "created": "2023-06-15T10:00:00Z",
      "updated": "2023-06-15T10:00:00Z"
    }
  ]
}
```

---

### Create a Site

Create a new RUM site to start collecting web performance data.

#### Endpoint
```bash
POST https://cronitor.io/api/sites
```

#### Request Body
```json
{
  "name": "E-Commerce Store",
  "webvitals_enabled": true,
  "errors_enabled": true,
  "filter_localhost": true,
  "filter_bots": true,
  "sampling": 100
}
```

#### Parameters
- **`name`** (string, required) - Display name for the site
- **`webvitals_enabled`** (boolean, optional) - Enable Core Web Vitals collection (default: true)
- **`errors_enabled`** (boolean, optional) - Enable JavaScript error tracking (default: true)
- **`filter_localhost`** (boolean, optional) - Filter out localhost traffic (default: true)
- **`filter_bots`** (boolean, optional) - Filter out bot traffic (default: true)
- **`filter_browser_extensions`** (boolean, optional) - Filter out browser extension errors (default: true)
- **`exclude_internal_referrers`** (boolean, optional) - Exclude internal referrer tracking (default: false)
- **`public_report_enabled`** (boolean, optional) - Enable public performance report (default: false)
- **`sampling`** (integer, optional) - Percentage of traffic to sample, 0-100 (default: 100)
- **`origin_allowlist`** (array, optional) - List of allowed origins/hostnames
- **`ip_denylist`** (array, optional) - List of IP addresses or CIDR ranges to block
- **`country_code_denylist`** (array, optional) - List of country codes to block (ISO 3166-1 alpha-2)
- **`path_denylist`** (array, optional) - List of URL paths to exclude

#### Example
```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "name": "Marketing Site",
        "webvitals_enabled": true,
        "errors_enabled": true,
        "filter_localhost": true,
        "filter_bots": true,
        "sampling": 50,
        "origin_allowlist": ["example.com", "www.example.com"]
    }' \
    https://cronitor.io/api/sites
```

#### Response
```json
{
  "key": "site_xyz789abc123",
  "name": "Marketing Site",
  "client_key": "ck_xyz789abc123",
  "public_report_enabled": false,
  "public_report_key": "pr_xyz789abc123",
  "webvitals_enabled": true,
  "errors_enabled": true,
  "filter_localhost": true,
  "filter_bots": true,
  "filter_browser_extensions": true,
  "exclude_internal_referrers": false,
  "origin_allowlist": ["example.com", "www.example.com"],
  "ip_denylist": null,
  "country_code_denylist": null,
  "path_denylist": null,
  "sampling": 50,
  "ingestion_enabled": true,
  "created": "2023-12-01T10:00:00Z",
  "updated": "2023-12-01T10:00:00Z"
}
```

---

### Get a Site

Retrieve details for a specific site.

#### Endpoint
```bash
GET https://cronitor.io/api/sites/:key
```

#### Query Parameters
- **`withSnippet`** (boolean) - Include the JavaScript installation snippet

#### Example
```bash
curl "https://cronitor.io/api/sites/site_abc123?withSnippet=true" -u API_KEY:
```

#### Response
```json
{
  "key": "site_abc123def456",
  "name": "Main Website",
  "client_key": "ck_abc123def456",
  "public_report_enabled": false,
  "public_report_key": "pr_abc123def456",
  "webvitals_enabled": true,
  "errors_enabled": true,
  "filter_localhost": true,
  "filter_bots": true,
  "filter_browser_extensions": true,
  "exclude_internal_referrers": false,
  "origin_allowlist": null,
  "ip_denylist": null,
  "country_code_denylist": null,
  "path_denylist": null,
  "snippet": "<script async src=\"https://rum.cronitor.io/script.js\" data-client-key=\"ck_abc123def456\"></script>",
  "sampling": 100,
  "ingestion_enabled": true,
  "created": "2023-06-15T10:00:00Z",
  "updated": "2023-06-15T10:00:00Z"
}
```

---

### Update a Site

Update a site's configuration.

#### Endpoint
```bash
PUT https://cronitor.io/api/sites/:key
```

#### Request Body
```json
{
  "name": "Main Website (Updated)",
  "sampling": 75,
  "errors_enabled": true,
  "path_denylist": ["/admin/*", "/internal/*"]
}
```

#### Example
```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request PUT \
    --data '{
        "name": "Main Website",
        "sampling": 75,
        "ip_denylist": ["192.168.1.0/24", "10.0.0.0/8"]
    }' \
    https://cronitor.io/api/sites/site_abc123
```

#### Response
Returns the updated site object.

---

### Delete a Site

Delete a site and stop collecting data.

#### Endpoint
```bash
DELETE https://cronitor.io/api/sites/:key
```

#### Example
```bash
curl --request DELETE https://cronitor.io/api/sites/site_old123 -u API_KEY:
```

#### Response
Returns HTTP 204 No Content on success.

---

## Site Errors

### List Site Errors

Retrieve JavaScript errors collected from your sites.

#### Endpoint
```bash
GET https://cronitor.io/api/site_errors
```

#### Query Parameters
- **`site`** (string) - Filter by site key
- **`page`** (integer) - Page number for pagination
- **`pageSize`** (integer) - Number of results per page

#### Example
```bash
curl "https://cronitor.io/api/site_errors?site=site_abc123" -u API_KEY:
```

#### Response
```json
{
  "page": 1,
  "page_size": 50,
  "total_count": 15,
  "data": [
    {
      "key": "err_abc123def456",
      "message": "TypeError: Cannot read property 'length' of undefined",
      "source": "https://example.com/js/app.js",
      "line": 42,
      "column": 15,
      "count": 127,
      "first_seen": "2023-11-15T08:30:00Z",
      "last_seen": "2023-12-01T14:22:00Z",
      "status": "open"
    }
  ]
}
```

---

### Get a Site Error

Retrieve details for a specific error.

#### Endpoint
```bash
GET https://cronitor.io/api/site_errors/:key
```

#### Example
```bash
curl https://cronitor.io/api/site_errors/err_abc123 -u API_KEY:
```

---

## Site Analytics Query

Query analytics data from your RUM sites to build custom visualizations, reports, and integrations. This endpoint provides access to sessions, pageviews, performance metrics, web vitals, and more.

### Query Analytics Data

#### Endpoint
```bash
POST https://cronitor.io/api/sites/query
```

#### Request Body
```json
{
  "site": "site_abc123",
  "kind": "breakdown",
  "group_by": "country_code",
  "aggregate": ["session_count", "pageview_count"],
  "time": "7d",
  "timezone": "UTC",
  "filters": [],
  "page": 1,
  "page_size": 50
}
```

#### Core Parameters

{% attribute name="site" context="[string] **required**" /%}
The site key to query data from.

{% attribute name="kind" context="[string] **required**" /%}
The type of query to execute. Valid values:
- `aggregation` - Return aggregated metrics for the entire time range
- `breakdown` - Group aggregated metrics by a dimension
- `timeseries` - Return metrics over time with specified bucket granularity
- `search_options` - Search for available values within a dimension
- `error_groups` - Return grouped error patterns

{% attribute name="aggregate" context="[array of strings] **required**" /%}
List of metrics to compute (max 10). See [Aggregate Fields](#aggregate-fields) for valid values.

#### Time Parameters

{% attribute name="time" context="[string] default: \"24h\"" /%}
Predefined time range. Valid values: `1h`, `24h`, `today`, `3d`, `7d`, `14d`, `30d`, `180d`, `4w`, `mtd`, `3m`, `6m`, `12m`, `24m`, `ytd`, `custom`.

{% attribute name="start" context="[string] ISO 8601 datetime" /%}
Start of custom time range. Required when `time="custom"`.

{% attribute name="end" context="[string] ISO 8601 datetime" /%}
End of custom time range. Required when `time="custom"`. Must be after `start` and not in the future. Maximum range is 730 days.

{% attribute name="time_bucket" context="[string]" /%}
Bucket size for timeseries queries. Valid values: `minute`, `hour`, `day`, `week`, `month`. Must be compatible with the selected time range.

{% attribute name="timezone" context="[string] default: \"UTC\"" /%}
IANA timezone for query results (e.g., `America/New_York`, `Europe/London`).

#### Grouping and Filtering

{% attribute name="group_by" context="[string]" /%}
Dimension to group results by. Required for `breakdown` queries. See [Breakdown Fields](#breakdown-fields) for valid values.

{% attribute name="filters" context="[array of objects] max: 10" /%}
Array of filter objects to apply. Each filter has:
- `dimension` - Field to filter on (see [Breakdown Fields](#breakdown-fields))
- `operator` - One of: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `startsWith`, `endsWith`, `contains`
- `value` - Value to compare against

{% attribute name="filters_behavior" context="[string] default: \"and\"" /%}
How to combine multiple filters. Valid values: `and`, `or`.

#### Ordering and Pagination

{% attribute name="order_by" context="[array of strings] max: 2" /%}
Sort results by field. Prefix with `-` for descending order. Fields must be in `aggregate` or `group_by`.

{% attribute name="page" context="[integer] default: 1, max: 100" /%}
Page number for paginated results.

{% attribute name="page_size" context="[integer] default: 10, max: 250" /%}
Number of results per page.

#### Comparison

{% attribute name="compare" context="[string]" /%}
Compare current data with previous period. Valid value: `previous_time_range`. Returns additional `_previous_time_range_value` and `_previous_time_range_change_rate` fields.

#### Other Parameters

{% attribute name="environment" context="[string]" /%}
Filter by environment key.

{% attribute name="search" context="[string] max: 1024 chars" /%}
Search term for `search_options` queries.

---

### Aggregate Fields

Metrics available for the `aggregate` parameter:

**Session & Event Counts**
- `session_count` - Number of unique sessions
- `pageview_count` - Number of pageview events
- `event_count` - Total event count
- `error_count` - Number of error events

**Session Metrics** (available for `aggregation` and `timeseries` queries only)
- `session_duration_avg` - Average session duration (milliseconds)
- `bounce_rate` - Percentage of single-page sessions
- `events_per_session_avg` - Average events per session
- `errors_per_session_avg` - Average errors per session
- `session_error_rate` - Percentage of sessions with errors
- `conversion_rate` - Conversion rate (for filtered queries)

**Page Load Performance** (percentiles: p10, p25, p50, p75, p90, p95, p99)
- `page_load_total_p10`, `page_load_total_p25`, `page_load_total_p50`, `page_load_total_p75`, `page_load_total_p90`, `page_load_total_p95`, `page_load_total_p99` - Total page load time (ms)
- `page_load_dns_p50`, `page_load_dns_p95` - DNS lookup time
- `page_load_ssl_p50` - SSL/TLS negotiation time
- `page_load_connect_p50` - TCP connection time
- `page_load_ttfb_p50` - Time to first byte
- `page_load_download_p50` - Download time
- `page_load_dom_content_loaded_p50` - DOM content loaded time
- `page_load_render_p50` - Render time
- `page_load_network_p50` - Total network time
- `page_load_backend_p50` - Backend time
- `page_load_frontend_p50` - Frontend time

**Web Vitals** (p50, p95, p99 percentiles)
- `web_vital_lcp_p50`, `web_vital_lcp_p95`, `web_vital_lcp_p99` - Largest Contentful Paint (ms)
- `web_vital_fid_p50`, `web_vital_fid_p95`, `web_vital_fid_p99` - First Input Delay (ms)
- `web_vital_cls_p50`, `web_vital_cls_p95`, `web_vital_cls_p99` - Cumulative Layout Shift

**Error Details** (for error_groups queries)
- `error_type` - Error type/class
- `message` - Error message
- `filename` - Source filename
- `lineno` - Line number
- `colno` - Column number
- `first_seen` - First occurrence timestamp
- `last_seen` - Last occurrence timestamp

---

### Breakdown Fields

Dimensions available for `group_by` and filter `dimension` parameters:

**Geography**
- `country_code` - ISO 3166-1 alpha-2 country code
- `city_name` - City name

**Navigation**
- `path` - URL path
- `hostname` - Domain/hostname

**Device & Browser**
- `device_type` - Device classification (desktop, mobile, tablet)
- `browser` - Browser name
- `operating_system` - Operating system name

**Acquisition**
- `referrer_hostname` - Referrer domain
- `utm_source` - UTM source parameter
- `utm_campaign` - UTM campaign parameter
- `utm_medium` - UTM medium parameter
- `utm_content` - UTM content parameter
- `utm_term` - UTM term parameter

**Events**
- `event_name` - Custom event name
- `event_group` - Event group classification

**Network**
- `connection_type` - Network connection type

---

### Time Bucket Compatibility

Valid `time_bucket` values depend on the selected `time` range:

| Time Range | Valid Buckets |
|------------|---------------|
| `1h` | `minute` |
| `24h`, `today` | `hour` |
| `3d`, `7d`, `14d` | `hour`, `day` |
| `30d`, `4w`, `mtd` | `hour`, `day`, `week` |
| `180d`, `3m`, `6m`, `12m`, `24m`, `ytd` | `day`, `week`, `month` |
| `custom` | `hour`, `day`, `week`, `month` |

---

### Query Examples

#### Aggregation Query
Get total sessions and median page load for the last 7 days:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "aggregation",
        "aggregate": ["session_count", "page_load_total_p50", "bounce_rate"],
        "time": "7d",
        "timezone": "America/New_York"
    }' \
    https://cronitor.io/api/sites/query
```

**Response:**
```json
{
  "data": {
    "session_count": {"value": 15420},
    "page_load_total_p50": {"value": 2340},
    "bounce_rate": {"value": 0.42}
  }
}
```

#### Breakdown Query
Get sessions by country with filtering:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "breakdown",
        "group_by": "country_code",
        "aggregate": ["session_count", "pageview_count"],
        "time": "30d",
        "filters": [
            {"dimension": "device_type", "operator": "eq", "value": "mobile"}
        ],
        "order_by": ["-session_count"],
        "page": 1,
        "page_size": 10
    }' \
    https://cronitor.io/api/sites/query
```

**Response:**
```json
{
  "data": [
    {"country_code": "US", "session_count": 5230, "pageview_count": 12450},
    {"country_code": "GB", "session_count": 2100, "pageview_count": 4820},
    {"country_code": "DE", "session_count": 1450, "pageview_count": 3200}
  ],
  "page": 1,
  "page_size": 10,
  "page_count": 5,
  "total_count": 48
}
```

#### Timeseries Query
Get daily session counts for the last 30 days:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "timeseries",
        "aggregate": ["session_count", "pageview_count"],
        "time": "30d",
        "time_bucket": "day",
        "timezone": "UTC"
    }' \
    https://cronitor.io/api/sites/query
```

**Response:**
```json
{
  "data": [
    {"ts": "2025-12-01T00:00:00+00:00", "session_count": 520, "pageview_count": 1240},
    {"ts": "2025-12-02T00:00:00+00:00", "session_count": 485, "pageview_count": 1180},
    {"ts": "2025-12-03T00:00:00+00:00", "session_count": 612, "pageview_count": 1450}
  ],
  "time_bucket": "day"
}
```

#### Custom Date Range Query
Query a specific date range:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "timeseries",
        "aggregate": ["session_count"],
        "time": "custom",
        "start": "2025-01-01T00:00:00Z",
        "end": "2025-01-31T23:59:59Z",
        "time_bucket": "day"
    }' \
    https://cronitor.io/api/sites/query
```

#### Comparison Query
Compare current period with previous period:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "aggregation",
        "aggregate": ["session_count", "bounce_rate"],
        "time": "7d",
        "compare": "previous_time_range"
    }' \
    https://cronitor.io/api/sites/query
```

**Response:**
```json
{
  "data": {
    "session_count": {
      "value": 15420,
      "previous_time_range_value": 14200,
      "previous_time_range_change_rate": 0.086
    },
    "bounce_rate": {
      "value": 0.42,
      "previous_time_range_value": 0.45,
      "previous_time_range_change_rate": -0.067
    }
  }
}
```

#### Hourly Timeseries Query
Get hourly session data for granular traffic analysis:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "timeseries",
        "aggregate": ["session_count"],
        "time": "30d",
        "time_bucket": "hour",
        "timezone": "America/New_York"
    }' \
    https://cronitor.io/api/sites/query
```

#### Top Pages Query
Get the most visited pages:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "breakdown",
        "group_by": "path",
        "aggregate": ["pageview_count", "session_count"],
        "time": "7d",
        "order_by": ["-pageview_count"],
        "page_size": 20
    }' \
    https://cronitor.io/api/sites/query
```

#### Web Vitals by Device
Compare Core Web Vitals across device types:

```bash
curl --user API_KEY: \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
        "site": "site_abc123",
        "kind": "breakdown",
        "group_by": "device_type",
        "aggregate": ["web_vital_lcp_p50", "web_vital_fid_p50", "web_vital_cls_p50", "session_count"],
        "time": "7d"
    }' \
    https://cronitor.io/api/sites/query
```

---

## Site Attributes

{% attribute name="key" context="[string] read-only" /%}
Unique identifier for the site.

{% attribute name="name" context="[string] **required**" /%}
Display name for the site. Shown in the Cronitor dashboard.

{% attribute name="client_key" context="[string] read-only" /%}
Client-side key used in the JavaScript snippet. Safe to expose in browser code.

{% attribute name="public_report_enabled" context="[boolean] default: false" /%}
Whether to enable a public performance report for this site.

{% attribute name="public_report_key" context="[string] read-only" /%}
Key for accessing the public performance report.

{% attribute name="webvitals_enabled" context="[boolean] default: true" /%}
Enable collection of Core Web Vitals (LCP, FID, CLS, TTFB, INP).

{% attribute name="errors_enabled" context="[boolean] default: true" /%}
Enable JavaScript error tracking.

{% attribute name="filter_localhost" context="[boolean] default: true" /%}
Filter out traffic from localhost origins.

{% attribute name="filter_bots" context="[boolean] default: true" /%}
Filter out traffic from known bots and crawlers.

{% attribute name="filter_browser_extensions" context="[boolean] default: true" /%}
Filter out errors caused by browser extensions.

{% attribute name="exclude_internal_referrers" context="[boolean] default: false" /%}
Exclude internal navigation from referrer tracking.

{% attribute name="origin_allowlist" context="[array of strings]" /%}
List of allowed hostnames. If set, only traffic from these origins will be collected.

{% attribute name="ip_denylist" context="[array of strings]" /%}
List of IP addresses or CIDR ranges to block from collection.

{% attribute name="country_code_denylist" context="[array of strings]" /%}
List of ISO 3166-1 alpha-2 country codes to block.

{% attribute name="path_denylist" context="[array of strings]" /%}
List of URL paths to exclude from collection. Supports wildcards (e.g., `/admin/*`).

{% attribute name="snippet" context="[string] read-only" /%}
The JavaScript snippet to install on your site. Available when requesting with `withSnippet=true`.

{% attribute name="sampling" context="[integer] default: 100" /%}
Percentage of traffic to sample (0-100). Lower values reduce data volume for high-traffic sites.

{% attribute name="ingestion_enabled" context="[boolean] read-only" /%}
Whether data ingestion is currently enabled for this site.

---

## Installation

After creating a site, add the JavaScript snippet to your website:

```html
<script async src="https://rum.cronitor.io/script.js" data-client-key="YOUR_CLIENT_KEY"></script>
```

Place this snippet in the `<head>` of your HTML pages, or use the snippet returned by the API with `withSnippet=true`.

For more installation options, see the [RUM Quickstart Guide](/docs/rum-quickstart).

---

## Best Practices

- **Use origin allowlists**: Restrict data collection to your production domains
- **Enable filtering**: Use bot and localhost filtering to improve data quality
- **Adjust sampling**: For high-traffic sites, reduce sampling to control costs while maintaining statistical significance
- **Block internal IPs**: Add office/VPN IP ranges to the denylist to exclude internal traffic
- **Monitor errors proactively**: Enable error tracking and review new errors regularly
