Learn how to use the Chrome UX Report API to get access to real-user experience data across millions of websites.


![Rick Viscomi](https://web.dev/images/authors/rviscomi.jpg) Rick Viscomi [GitHub](https://github.com/rviscomi) [Bluesky](https://bsky.app/profile/rviscomi.dev) [Homepage](https://developer.chrome.com/rviscomi.dev/) ![Shane Exterkamp](https://web.dev/images/authors/exterkamp.jpg) Shane Exterkamp [X](https://twitter.com/shane_exterkamp) [GitHub](https://github.com/exterkamp)

<br />

The [Chrome UX Report](https://developer.chrome.com/docs/crux) (CrUX) dataset represents how real-world Chrome users experience popular destinations on the web. Since 2017, when the queryable dataset was first released on [BigQuery](https://developer.chrome.com/chrome-ux-report-bigquery), field data from CrUX has been integrated into developer tools like [PageSpeed Insights](https://developer.chrome.com/chrome-ux-report-pagespeed-insights), and Search Console's [Core Web Vitals report](https://support.google.com/webmasters/answer/9205520), enabling developers to measure and monitor real-user experiences. The piece that has been missing all this time has been a tool that provides free and RESTful access to CrUX data programmatically. To help bridge that gap, we're excited to announce the release of the all new [Chrome UX Report API](https://developer.chrome.com/docs/crux/api)!

This API has been built with the goal of providing developers with quick and comprehensive access to CrUX data. The CrUX API only reports [*field*](https://web.dev/articles/how-to-measure-speed#lab_data_vs_field_data) user experience data, unlike the existing [PageSpeed Insights API](https://developers.google.com/speed/docs/insights/v5/get-started), which also reports *lab* data from the Lighthouse performance audits. The CrUX API is streamlined and can quickly serve user experience data, making it ideally suited for real-time auditing applications.

To ensure that developers have access to all of the metrics that matter the most---the [Core Web Vitals](https://web.dev/articles/vitals#core_web_vitals)---the CrUX API audits and monitors [Largest Contentful Paint](https://web.dev/articles/lcp) (LCP), [Interaction to Next Paint](https://web.dev/articles/inp) (INP), and [Cumulative Layout Shift](https://web.dev/articles/cls) (CLS) at both the origin and URL level.

So let's dive in and see how to use it!

## Try the API on this page

[Try it!](https://developer.chrome.com/docs/crux/guides/crux-api#try-it)

## Query origin data

Origins in the CrUX dataset encompass all underlying page-level experiences. The following example demonstrates how to query the CrUX API for an origin's user experience data using curl on the command line.

    API_KEY="[YOUR_API_KEY]"
    curl "https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=$API_KEY" \
      --header 'Content-Type: application/json' \
      --data '{"origin": "https://web.dev"}'

> [!NOTE]
> **Note:** All API requests must provide a value for the `key` parameter---`[YOUR_API_KEY]` in the previous example is left as a placeholder. Get your own private CrUX API key as described in [CrUX API documentation](https://developer.chrome.com/docs/crux/api#crux_api_key).

The `curl` command is made up of three parts:

1. The URL endpoint of the API, including the caller's private API key.
2. The `Content-Type: application/json` header, indicating that the request body contains JSON.
3. The JSON-encoded [request body](https://developer.chrome.com/docs/crux/api#request_body), specifying the `https://web.dev` origin.

To do the same thing in JavaScript, use the `CrUXApiUtil` utility,
which makes the API call and returns the decoded response (see also [our Github variant](https://github.com/GoogleChrome/CrUX/blob/main/js/crux-api-util.js)
for more features including history and batch support).

    const CrUXApiUtil = {};
    // Get your CrUX API key at https://goo.gle/crux-api-key.
    CrUXApiUtil.API_KEY = '[YOUR_API_KEY]';
    CrUXApiUtil.API_ENDPOINT = `https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=${CrUXApiUtil.API_KEY}`;
    CrUXApiUtil.query = function (requestBody) {
      if (CrUXApiUtil.API_KEY == '[YOUR_API_KEY]') {
        throw 'Replace "YOUR_API_KEY" with your private CrUX API key. Get a key at https://goo.gle/crux-api-key.';
      }
      return fetch(CrUXApiUtil.API_ENDPOINT, {
        method: 'POST',
        body: JSON.stringify(requestBody)
      }).then(response => response.json()).then(response => {
        if (response.error) {
          return Promise.reject(response);
        }
        return response;
      });
    };

Replace `[YOUR_API_KEY]` with your [key](https://goo.gle/crux-api-key). Next, call the `CrUXApiUtil.query` function and pass in the [request body](https://developer.chrome.com/docs/crux/api#request_body) object.

    CrUXApiUtil.query({
      origin: 'https://web.dev'
    }).then(response => {
      console.log(response);
    }).catch(response => {
      console.error(response);
    });

If data exists for this origin, the API response is a JSON-encoded object containing [metrics](https://developer.chrome.com/docs/crux/api#metric) representing the distribution of user experiences. The distribution metrics are histogram bins and percentiles.

    {
      "record": {
        "key": {
          "origin": "https://web.dev"
        },
        "metrics": {
          "largest_contentful_paint": {
            "histogram": [
              {
                "start": 0,
                "end": 2500,
                "density": 0.7925068547983514
              },
              {
                "start": 2500,
                "end": 4000,
                "density": 0.1317422195536863
              },
              {
                "start": 4000,
                "density": 0.07575092564795324
              }
            ],
            "percentiles": {
              "p75": 2216
            }
          },
          // ...
        }
      }
    }

The `start` and `end` properties of the `histogram` object represent the range of values users experience for the given metric. The `density` property represents the proportion of user experiences within that range. In this example, 79% of LCP user experiences across all web.dev pages are under 2,500 milliseconds, which is the "[good](https://web.dev/articles/lcp#what_is_lcp)" LCP threshold. The `percentiles.p75` value means that 75% of user experiences in this distribution are less than 2,216 milliseconds. Learn more about the response structure in the [response body](https://developer.chrome.com/docs/crux/api#response_body) documentation.

### Errors

When the CrUX API doesn't have any data for a given origin, it responds with a JSON-encoded error message:

    {
      "error": {
        "code": 404,
        "message": "chrome ux report data not found",
        "status": "NOT_FOUND"
      }
    }

To debug this error, first check that the requested origin is publicly navigable. You can test this by entering the origin into your browser's address bar and comparing it against the final URL after any redirects. Common problems include unnecessarily adding or omitting the subdomain and using the wrong HTTP protocol.
Error

```json
{"origin": "http://www.web.dev"}
```
This origin incorrectly includes the `http://` protocol and `www.` subdomain.
Success

```json
{"origin": "https://web.dev"}
```
This origin is publicly navigable.

If the requested origin *is* the navigable version, this error may also occur if the origin has an insufficient number of samples. All origins and URLs included in the dataset must have a sufficient number of samples to anonymize individual users. Additionally, origins and URLs must be [publicly indexable](https://developers.google.com/search/docs/advanced/crawling/block-indexing). Refer to the [CrUX methodology](https://developer.chrome.com/docs/crux/methodology#origin-eligibility) to learn more about how websites are included in the dataset.

## Query URL data

You've seen how to query the CrUX API for the overall user experience on an origin. To restrict the results to a particular page, use the `url` request parameter.

    API_KEY="[YOUR_API_KEY]"
    curl "https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=$API_KEY" \
      --header 'Content-Type: application/json' \
      --data '{"url": "https://web.dev/fast/"}'

This curl command is similar to the origin example, except that the request body uses the `url` parameter to specify the page to look up.

To query URL data from the CrUX API in JavaScript, call the [`CrUXApiUtil.query`](https://developer.chrome.com/docs/crux/guides/crux-api#crux-api-util) function using the `url` parameter in the request body.

    CrUXApiUtil.query({
      url: 'https://web.dev/fast/'
    }).then(response => {
      console.log(response);
    }).catch(response => {
      console.error(response);
    });

If data for this URL exists in the CrUX dataset, the API will return a JSON-encoded response. For example

    {
      "record": {
        "key": {
          "url": "https://web.dev/fast/"
        },
        "metrics": {
          "largest_contentful_paint": {
            "histogram": [
              {
                "start": 0,
                "end": 2500,
                "density": 0.8477304539092148
              },
              {
                "start": 2500,
                "end": 4000,
                "density": 0.08988202359528057
              },
              {
                "start": 4000,
                "density": 0.062387522495501155
              }
            ],
            "percentiles": {
              "p75": 1947
            }
          },
          // ...
        }
      }
    }

True to form, the results show that `https://web.dev/fast/` has 85% "good" LCP experiences and a 75th percentile of 1,947 milliseconds, which is slightly better than the origin-wide distribution.

### URL normalization

The CrUX API may normalize requested URLs to better match the list of known URLs. For example, querying for the URL `https://web.dev/fast/#measure-performance-in-the-field` will result in data for `https://web.dev/fast/` due to normalization. When this happens, a `urlNormalizationDetails` object will be included in the response.

    {
      "record": {
        "key": {
          "url": "https://web.dev/fast/"
        },
        "metrics": { ... }
      },
      "urlNormalizationDetails": {
        "normalizedUrl": "https://web.dev/fast/",
        "originalUrl": "https://web.dev/fast/#measure-performance-in-the-field"
      }
    }

Learn more about [URL normalization](https://developer.chrome.com/docs/crux/api#api-response-urlnormalization) in the CrUX documentation.

## Query by form factor

> [!IMPORTANT]
> **Key term:** A form factor is the type of device on which a user visits a website. Common device types include desktop, phone, and tablet.

User experiences can vary significantly depending on website optimizations, network conditions, and users' devices. To better understand these differences, drill down into origin and URL performance using the [`formFactor`](https://developer.chrome.com/docs/crux/api#form_factor) dimension of the CrUX API.

The API supports three explicit form factor values: `DESKTOP`, `PHONE`, and `TABLET`. In addition to the origin or URL, specify one of these values in the [request body](https://developer.chrome.com/docs/crux/api#request_body) to restrict results to only those user experiences. This example demonstrates how to query the API by form factor using curl.

    API_KEY="[YOUR_API_KEY]"
    curl "https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=$API_KEY" \
      --header 'Content-Type: application/json' \
      --data '{"url": "https://web.dev/fast/", "formFactor": "PHONE"}'

To query the CrUX API for form factor-specific data using JavaScript, call the [`CrUXApiUtil.query`](https://developer.chrome.com/docs/crux/guides/crux-api#crux-api-util) function using the `url` and `formFactor` parameters in the request body.

    CrUXApiUtil.query({
      url: 'https://web.dev/fast/',
      formFactor: 'PHONE'
    }).then(response => {
      console.log(response);
    }).catch(response => {
      console.error(response);
    });

Omitting the `formFactor` parameter is equivalent to requesting data for all form factors combined.

    {
      "record": {
        "key": {
          "url": "https://web.dev/fast/",
          "formFactor": "PHONE"
        },
        "metrics": {
          "largest_contentful_paint": {
            "histogram": [
              {
                "start": 0,
                "end": 2500,
                "density": 0.778631284916204
              },
              {
                "start": 2500,
                "end": 4000,
                "density": 0.13943202979515887
              },
              {
                "start": 4000,
                "density": 0.08193668528864119
              }
            ],
            "percentiles": {
              "p75": 2366
            }
          },
        // ...
        }
      }
    }

The `key` field of the response will echo back the `formFactor` request configuration to confirm that only phone experiences are included.

> [!CAUTION]
> **Caution:** The more fine-grained the request is, for example a specific combination of URL and form factor, the fewer user experiences it will include. This may lead to more frequent "not found" errors, especially when querying less popular URLs or the less popular tablet device type.

Recall from the previous section that 85% of user experiences on this page had "good" LCP. Compare that to phone-specific experiences, of which only 78% are considered "good". The 75th percentile is also slower among phone experiences, up from 1,947 milliseconds to 2,366 milliseconds. Segmenting by form factor has the potential to highlight more extreme disparities in user experiences.

## Assess Core Web Vitals performance

The [Core Web Vitals](https://web.dev/articles/vitals#core_web_vitals) program defines targets that help determine whether a user experience or a distribution of experiences can be considered "good". In the following example, we use the CrUX API and the [`CrUXApiUtil.query`](https://developer.chrome.com/docs/crux/guides/crux-api#crux-api-util) function to assess whether a web page's distribution of Core Web Vitals metrics (LCP, INP, CLS) are "good".

    CrUXApiUtil.query({
      url: 'https://web.dev/fast/'
    }).then(response => {
      assessCoreWebVitals(response);
    }).catch(response => {
      console.error(response);
    });

    function assessCoreWebVitals(response) {
      // See https://web.dev/articles/vitals/#core-web-vitals.
      const CORE_WEB_VITALS = [
        'largest_contentful_paint',
        'interaction_to_next_paint',
        'cumulative_layout_shift'
      ];
      CORE_WEB_VITALS.forEach(metric => {
        const data = response.record.metrics[metric];
        if (!data) {
          console.log('No data for', metric);
          return;
        }
        const p75 = data.percentiles.p75;
        const threshold = data.histogram[0].end;
        // A Core Web Vitals metric passes the assessment if
        // its 75th percentile is under the "good" threshold.
        const passes = p75 < threshold;
        console.log(`The 75th percentile (${p75}) of ${metric} ` +
            `${passes ? 'passes' : 'does not pass'} ` +
            `the Core Web Vitals "good" threshold (${threshold}).`)
      });
    }

> [!IMPORTANT]
> **Key point:** The API may only be called with one origin or URL at a time. To assess multiple websites or pages, make separate calls to the API.

The results show that this page passes the Core Web Vitals assessments for all three metrics.

    The 75th percentile (1973) of largest_contentful_paint passes the Core Web Vitals "good" threshold (2500).
    The 75th percentile (20) of interaction_to_next_paint passes the Core Web Vitals "good" threshold (200).
    The 75th percentile (0.05) of cumulative_layout_shift passes the Core Web Vitals "good" threshold (0.10).

Combined with an automated way to monitor API results, data from CrUX can be used to ensure that real-user experiences **get fast** and **stay fast** . For more information about Core Web Vitals and how to measure them, check out [Web Vitals](https://web.dev/articles/vitals) and [Tools to measure Core Web Vitals](https://web.dev/articles/vitals-tools).

## What's next?

The features included in the initial version of the CrUX API only scratch the surface of the kinds of insights that are possible with CrUX. Users of the [CrUX dataset on BigQuery](https://developer.chrome.com/chrome-ux-report-bigquery) may be familiar with some of the more advanced features including:

- Additional metrics
  - `first_paint`
  - `dom_content_loaded`
  - `onload`
  - `time_to_first_byte`
  - `notification_permissions`
- Additional dimensions
  - `month`
  - `country`
- Additional granularity
  - detailed histograms
  - more percentiles

Check out the official [CrUX API docs](https://developer.chrome.com/docs/crux/api) to [acquire your API key](https://goo.gle/crux-api-key) and explore more example applications. We hope you'll give it a try and we'd love to hear any questions or feedback you may have, so reach out to us on the [CrUX discussion forum](https://groups.google.com/a/chromium.org/forum/#!forum/chrome-ux-report). And to stay up to date on everything we have planned for the CrUX API, subscribe to the [CrUX announcement forum](https://groups.google.com/a/chromium.org/forum/#!forum/chrome-ux-report-announce) or follow us on Twitter at [@ChromeUXReport](https://twitter.com/ChromeUXReport).