Skip to content

A tiny (500 bytes) CrUX API wrapper that supports record & history API, handles errors, and provides types.

License

Notifications You must be signed in to change notification settings

treosh/crux-api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

56 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

crux-api

A tiny CrUX API wrapper that supports record & history API, handles errors, and provides types.

Motivation: CrUX API is a fantastic tool to get RUM data without installing any script. While using the API in Treo, we discovered a few complex cases like API errors, rate limits, not found entries, URLs normalization, and TypeScript notations. We decided to build the crux-api library to make working with the CrUX API easier.

Features:

  • A tiny (500 bytes) wrapper for Chrome UX Report API;
  • Supports both the record & history API;
  • TypeScript notations for options and responses;
  • Isomorphic: works in a browser and node.js;
  • Returns null for the 404 (CrUX data not found) response;
  • Automatic retry when hits the API rate limits: 429 (Quota exceeded);
  • URL normalization helper to check the CrUX API index;

Usage

Install:

npm install crux-api

Fetch URL-level data for a various form factors and connections:

import { createQueryRecord } from 'crux-api'
const queryRecord = createQueryRecord({ key: CRUX_API_KEY })

// fetch all dimensions
const jsonRecord = await queryRecord({ url: 'https://github.com/marketplace?type=actions', formFactor: 'DESKTOP' })

The jsonRecord is null or an object with queryRecord response body, ex:

{
  "record": {
    "key": {
      "formFactor": "DESKTOP",
      "url": "https://github.com/marketplace"
    },
    "metrics": {
      "first_contentful_paint": {
        "histogram": [
          { "start": 0, "end": 1000, "density": 0.454180602006688 },
          { "start": 1000, "end": 3000, "density": 0.52575250836120291 },
          { "start": 3000, "density": 0.020066889632107024 }
        ],
        "percentiles": {
          "p75": 1365
        }
      },
      "cumulative_layout_shift": { ... },
      "first_input_delay": { ... },
      "largest_contentful_paint": { ... },
      "interaction_to_next_paint": { ... },
      "experimental_time_to_first_byte": { ... },
    }
  },
  "urlNormalizationDetails": {
    "originalUrl": "https://github.com/marketplace?type=actions",
    "normalizedUrl": "https://github.com/marketplace"
  }
}

Fetch historic data for a URL:

import { createQueryRecord } from 'crux-api'
const queryRecord = createQueryHistoryRecord({ key: CRUX_API_KEY })

const jsonHistory = await queryRecord({ url: 'https://www.github.com/' }) // fetch ALL_FORM_FACTORS

The jsonHistory is null or an object with queryRecord response body, ex:

{
  "record": {
    "key": {
      "url": "https://github.com/"
    },
    "metrics": {
      "cumulative_layout_shift": {
        "histogramTimeseries": [
          {
            "start": "0.00",
            "end": "0.10",
            "densities": [0.716522216796875, 0.672149658203125, ...]
          },
          {
            "start": "0.10",
            "end": "0.25",
            "densities": [0.244537353515625, 0.246917724609375, ...]
          },
          {
            "start": "0.25",
            "densities": [0.0389404296875, 0.0809326171875, ...]
          }
        ],
        "percentilesTimeseries": {
          "p75s": ["0.10", "0.12", ...]
        }
      },
      "experimental_time_to_first_byte": { ... },
      "first_contentful_paint": { ... },
      "first_input_delay": { ... },
      "interaction_to_next_paint": { ... },
      "largest_contentful_paint": { ... }
    }
  }
}

API

createQueryRecord(createOptions)

Returns a queryRecord function.

  • createOptions.key (required) - CrUX API key, use https://goo.gle/crux-api-key to generate a new key;
  • createOptions.fetch (optional, default: window.fetch) - pass a WHATWG fetch implementation for a non-browser environment;

queryRecord(queryOptions)

Fetches CrUX API using queryRecord options:

  • queryOptions.url or queryOptions.origin (required) – the main identifier for a record lookup;
  • queryOptions.formFactor (optional, defaults to all form factors) - the form factor dimension: PHONE | DESKTOP | TABLET;
  • queryOptions.effectiveConnectionType (optional, defaults to all connections) - the effective network class: 4G | 3G | 2G | slow-2G | offline.

Returns a Promise with a raw queryRecord response or null when the data is not found.

import { createQueryRecord } from 'crux-api'

const queryRecord = createQueryRecord({ key: process.env.CRUX_API_KEY })
const res = await queryRecord({
  url: 'https://github.com/marketplace?type=actions',
  formFactor: 'DESKTOP',
  effectiveConnectionType: '4G',
})

// res -> URL-level data for https://github.com/marketplace

createQueryHistoryRecord(historyOptions)

Returns a function that fetches [CrUX History API using queryHistoryRecord options:

import { createQueryHistoryRecord } from 'crux-api'

const queryHistory = createQueryHistoryRecord({ key: process.env.CRUX_API_KEY })
const res = await queryHistory({
  url: 'https://www.github.com/',
})

normalizeUrl(url)

Normalize a URL to match the CrUX API internal index. It is a URL's origin + pathname (source).

import { normalizeUrl } from 'crux-api'

console.log(normalizeUrl('https://github.com/marketplace?type=actions')) // https://github.com/marketplace (removes search params)
console.log(normalizeUrl('https://github.com')) // https://github.com/ (adds "/" to the end)

CrUX API Responses

The crux-api is designed to return data and automatically handles errors. It returns an object for 200 responses, retries after 429, and returns null for 404. For 400 and 5xx it throws a relevant error.

Below are all known responses of Chrome UX Report API for easier debugging and development (The API documentation is vague about errors, please, submit an issue, if you know other responses).

βœ… 200: URL-level CrUX History API data
curl -d url='https://github.com/' \
     -d formFactor=DESKTOP \
     'https://chromeuxreport.googleapis.com/v1/records:queryHistoryRecord?key=CRUX_API_KEY'
{
  "record": {
    "key": {
      "formFactor": "DESKTOP",
      "url": "https://github.com/"
    },
    "metrics": {
      "cumulative_layout_shift": {
        "histogramTimeseries": [
          {
            "start": "0.00",
            "end": "0.10",
            "densities": [
              0.726043701171875, 0.676971435546875, 0.638519287109375, 0.629058837890625, 0.622161865234375,
              0.656158447265625, 0.688262939453125, 0.73272705078125, 0.791351318359375, 0.845489501953125,
              0.897674560546875, 0.916046142578125, 0.91455078125, 0.91363525390625, 0.913543701171875,
              0.915008544921875, 0.9166259765625, 0.9210205078125, 0.929107666015625, 0.936553955078125,
              0.93804931640625, 0.925628662109375, 0.913299560546875, 0.9024658203125, 0.903533935546875
            ]
          },
          {
            "start": "0.10",
            "end": "0.25",
            "densities": [
              0.2373046875, 0.240753173828125, 0.224029541015625, 0.190032958984375, 0.1575927734375, 0.123931884765625,
              0.10748291015625, 0.098846435546875, 0.087677001953125, 0.07757568359375, 0.069122314453125,
              0.065460205078125, 0.064697265625, 0.06402587890625, 0.06298828125, 0.061614990234375, 0.0601806640625,
              0.05560302734375, 0.048614501953125, 0.041778564453125, 0.041229248046875, 0.05340576171875,
              0.0660400390625, 0.07568359375, 0.07342529296875
            ]
          },
          {
            "start": "0.25",
            "densities": [
              0.036651611328125, 0.082275390625, 0.137451171875, 0.180908203125, 0.220245361328125, 0.21990966796875,
              0.204254150390625, 0.168426513671875, 0.1209716796875, 0.076934814453125, 0.033203125, 0.01849365234375,
              0.020751953125, 0.0223388671875, 0.023468017578125, 0.02337646484375, 0.023193359375, 0.02337646484375,
              0.02227783203125, 0.02166748046875, 0.020721435546875, 0.020965576171875, 0.020660400390625,
              0.0218505859375, 0.023040771484375
            ]
          }
        ],
        "percentilesTimeseries": {
          "p75s": [
            "0.10",
            "0.11",
            "0.12",
            "0.14",
            "0.18",
            "0.17",
            "0.14",
            "0.10",
            "0.08",
            "0.07",
            "0.06",
            "0.06",
            "0.06",
            "0.06",
            "0.06",
            "0.06",
            "0.06",
            "0.06",
            "0.05",
            "0.05",
            "0.04",
            "0.05",
            "0.05",
            "0.05",
            "0.05"
          ]
        }
      },
      "experimental_time_to_first_byte": {
        "histogramTimeseries": [
          {
            "start": 0,
            "end": 800,
            "densities": [
              0.779144287109375, 0.780303955078125, 0.777435302734375, 0.77899169921875, 0.780487060546875,
              0.779144287109375, 0.77862548828125, 0.776763916015625, 0.774810791015625, 0.76995849609375,
              0.765777587890625, 0.75335693359375, 0.741180419921875, 0.73614501953125, 0.732025146484375,
              0.7298583984375, 0.7265625, 0.72662353515625, 0.724853515625, 0.72650146484375, 0.7236328125,
              0.718994140625, 0.717437744140625, 0.71881103515625, 0.721832275390625
            ]
          },
          {
            "start": 800,
            "end": 1800,
            "densities": [
              0.1845703125, 0.184783935546875, 0.187591552734375, 0.186492919921875, 0.186920166015625,
              0.189056396484375, 0.190155029296875, 0.193328857421875, 0.1947021484375, 0.19903564453125,
              0.202911376953125, 0.21380615234375, 0.22515869140625, 0.22967529296875, 0.232208251953125,
              0.23468017578125, 0.23553466796875, 0.236907958984375, 0.237640380859375, 0.2371826171875,
              0.23992919921875, 0.24432373046875, 0.245819091796875, 0.24505615234375, 0.242095947265625
            ]
          },
          {
            "start": 1800,
            "densities": [
              0.036285400390625, 0.034912109375, 0.03497314453125, 0.034515380859375, 0.0325927734375, 0.03179931640625,
              0.031219482421875, 0.0299072265625, 0.030487060546875, 0.031005859375, 0.03131103515625, 0.0328369140625,
              0.033660888671875, 0.0341796875, 0.0357666015625, 0.03546142578125, 0.03790283203125, 0.036468505859375,
              0.037506103515625, 0.03631591796875, 0.03643798828125, 0.03668212890625, 0.0367431640625, 0.0361328125,
              0.03607177734375
            ]
          }
        ],
        "percentilesTimeseries": {
          "p75s": [
            748, 749, 752, 752, 751, 752, 755, 757, 761, 768, 778, 796, 814, 821, 826, 832, 832, 834, 836, 836, 838,
            846, 847, 846, 841
          ]
        }
      },
      "first_contentful_paint": {
        "histogramTimeseries": [
          {
            "start": 0,
            "end": 1800,
            "densities": [
              0.88543701171875, 0.88568115234375, 0.886962890625, 0.889556884765625, 0.8934326171875, 0.89837646484375,
              0.90142822265625, 0.903594970703125, 0.900787353515625, 0.899627685546875, 0.894317626953125,
              0.8936767578125, 0.89227294921875, 0.89227294921875, 0.8905029296875, 0.891815185546875, 0.88812255859375,
              0.88897705078125, 0.888946533203125, 0.888458251953125, 0.889923095703125, 0.88800048828125,
              0.88433837890625, 0.88311767578125, 0.880706787109375
            ]
          },
          {
            "start": 1800,
            "end": 3000,
            "densities": [
              0.07373046875, 0.07440185546875, 0.072662353515625, 0.0693359375, 0.0662841796875, 0.063446044921875,
              0.061248779296875, 0.06024169921875, 0.061920166015625, 0.06378173828125, 0.06536865234375,
              0.066314697265625, 0.0675048828125, 0.066741943359375, 0.06787109375, 0.0675048828125, 0.0699462890625,
              0.06915283203125, 0.068267822265625, 0.06878662109375, 0.068267822265625, 0.070343017578125,
              0.072509765625, 0.072418212890625, 0.073883056640625
            ]
          },
          {
            "start": 3000,
            "densities": [
              0.04083251953125, 0.0399169921875, 0.040374755859375, 0.041107177734375, 0.040283203125,
              0.038177490234375, 0.037322998046875, 0.036163330078125, 0.03729248046875, 0.036590576171875,
              0.040313720703125, 0.040008544921875, 0.04022216796875, 0.040985107421875, 0.0416259765625,
              0.040679931640625, 0.04193115234375, 0.0418701171875, 0.04278564453125, 0.042755126953125,
              0.04180908203125, 0.041656494140625, 0.04315185546875, 0.044464111328125, 0.04541015625
            ]
          }
        ],
        "percentilesTimeseries": {
          "p75s": [
            1221, 1216, 1219, 1205, 1186, 1173, 1158, 1157, 1165, 1175, 1190, 1204, 1214, 1217, 1222, 1227, 1231, 1237,
            1237, 1237, 1236, 1250, 1263, 1272, 1276
          ]
        }
      },
      "first_input_delay": {
        "histogramTimeseries": [
          {
            "start": 0,
            "end": 100,
            "densities": [
              0.970855712890625, 0.971649169921875, 0.97064208984375, 0.969085693359375, 0.96905517578125,
              0.968017578125, 0.969146728515625, 0.96868896484375, 0.9720458984375, 0.971710205078125, 0.9720458984375,
              0.97308349609375, 0.974609375, 0.9749755859375, 0.974090576171875, 0.97412109375, 0.9752197265625,
              0.97528076171875, 0.9747314453125, 0.973236083984375, 0.973846435546875, 0.97296142578125,
              0.974884033203125, 0.97357177734375, 0.975128173828125
            ]
          },
          {
            "start": 100,
            "end": 300,
            "densities": [
              0.01763916015625, 0.01641845703125, 0.017730712890625, 0.0181884765625, 0.018218994140625,
              0.019622802734375, 0.01849365234375, 0.019256591796875, 0.0172119140625, 0.017608642578125,
              0.017059326171875, 0.016876220703125, 0.015350341796875, 0.014984130859375, 0.0159912109375,
              0.016021728515625, 0.014495849609375, 0.0145263671875, 0.014739990234375, 0.0159912109375,
              0.01568603515625, 0.016204833984375, 0.014739990234375, 0.01617431640625, 0.01416015625
            ]
          },
          {
            "start": 300,
            "densities": [
              0.011505126953125, 0.011932373046875, 0.011627197265625, 0.012725830078125, 0.012725830078125,
              0.012359619140625, 0.012359619140625, 0.012054443359375, 0.0107421875, 0.01068115234375,
              0.010894775390625, 0.010040283203125, 0.010040283203125, 0.010040283203125, 0.009918212890625,
              0.009857177734375, 0.010284423828125, 0.01019287109375, 0.010528564453125, 0.010772705078125,
              0.010467529296875, 0.010833740234375, 0.0103759765625, 0.01025390625, 0.010711669921875
            ]
          }
        ],
        "percentilesTimeseries": {
          "p75s": [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]
        }
      },
      "interaction_to_next_paint": {
        "histogramTimeseries": [
          {
            "start": 0,
            "end": 200,
            "densities": [
              0.9437255859375, 0.944244384765625, 0.94207763671875, 0.938720703125, 0.936737060546875,
              0.936126708984375, 0.93768310546875, 0.93988037109375, 0.94134521484375, 0.9393310546875, 0.9384765625,
              0.939208984375, 0.937744140625, 0.9365234375, 0.937286376953125, 0.937896728515625, 0.937591552734375,
              0.93682861328125, 0.9373779296875, 0.93505859375, 0.9368896484375, 0.9373779296875, 0.937255859375,
              0.938629150390625, 0.940155029296875
            ]
          },
          {
            "start": 200,
            "end": 500,
            "densities": [
              0.03204345703125, 0.032135009765625, 0.034271240234375, 0.0360107421875, 0.0382080078125, 0.0384521484375,
              0.038360595703125, 0.037994384765625, 0.036376953125, 0.037841796875, 0.038848876953125, 0.03875732421875,
              0.039947509765625, 0.04107666015625, 0.040435791015625, 0.039337158203125, 0.039947509765625,
              0.040313720703125, 0.04034423828125, 0.04168701171875, 0.0406494140625, 0.03924560546875,
              0.039581298828125, 0.039459228515625, 0.03814697265625
            ]
          },
          {
            "start": 500,
            "densities": [
              0.02423095703125, 0.02362060546875, 0.023651123046875, 0.0252685546875, 0.025054931640625,
              0.025421142578125, 0.023956298828125, 0.022125244140625, 0.02227783203125, 0.0228271484375,
              0.022674560546875, 0.02203369140625, 0.022308349609375, 0.02239990234375, 0.02227783203125,
              0.02276611328125, 0.0224609375, 0.022857666015625, 0.02227783203125, 0.02325439453125, 0.0224609375,
              0.02337646484375, 0.023162841796875, 0.02191162109375, 0.021697998046875
            ]
          }
        ],
        "percentilesTimeseries": {
          "p75s": [61, 62, 63, 64, 65, 66, 66, 66, 66, 67, 68, 70, 72, 73, 73, 72, 72, 73, 72, 71, 70, 70, 69, 69, 69]
        }
      },
      "largest_contentful_paint": {
        "histogramTimeseries": [
          {
            "start": 0,
            "end": 2500,
            "densities": [
              0.86895751953125, 0.850921630859375, 0.834320068359375, 0.818145751953125, 0.802490234375,
              0.796417236328125, 0.79620361328125, 0.809417724609375, 0.830078125, 0.850067138671875, 0.87017822265625,
              0.87786865234375, 0.87628173828125, 0.875823974609375, 0.875335693359375, 0.867889404296875,
              0.869781494140625, 0.87115478515625, 0.869476318359375, 0.874481201171875, 0.877227783203125,
              0.884185791015625, 0.890472412109375, 0.895751953125, 0.896820068359375
            ]
          },
          {
            "start": 2500,
            "end": 4000,
            "densities": [
              0.08221435546875, 0.09649658203125, 0.10797119140625, 0.11907958984375, 0.130035400390625,
              0.1356201171875, 0.135101318359375, 0.12530517578125, 0.110565185546875, 0.095550537109375,
              0.07904052734375, 0.072845458984375, 0.074127197265625, 0.0743408203125, 0.07452392578125,
              0.080596923828125, 0.07769775390625, 0.078460693359375, 0.0782470703125, 0.074798583984375,
              0.07415771484375, 0.0692138671875, 0.06475830078125, 0.0615234375, 0.0595703125
            ]
          },
          {
            "start": 4000,
            "densities": [
              0.048828125, 0.052581787109375, 0.057708740234375, 0.062774658203125, 0.067474365234375,
              0.067962646484375, 0.068695068359375, 0.065277099609375, 0.059356689453125, 0.05438232421875, 0.05078125,
              0.049285888671875, 0.049591064453125, 0.049835205078125, 0.050140380859375, 0.051513671875,
              0.052520751953125, 0.050384521484375, 0.052276611328125, 0.05072021484375, 0.048614501953125,
              0.046600341796875, 0.044769287109375, 0.042724609375, 0.043609619140625
            ]
          }
        ],
        "percentilesTimeseries": {
          "p75s": [
            1815, 1953, 2056, 2148, 2236, 2271, 2273, 2200, 2068, 1918, 1757, 1695, 1715, 1719, 1726, 1772, 1760, 1760,
            1762, 1726, 1701, 1657, 1618, 1579, 1556
          ]
        }
      }
    },
    "collectionPeriods": [
      {
        "firstDate": {
          "year": 2023,
          "month": 3,
          "day": 26
        },
        "lastDate": {
          "year": 2023,
          "month": 4,
          "day": 22
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 4,
          "day": 2
        },
        "lastDate": {
          "year": 2023,
          "month": 4,
          "day": 29
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 4,
          "day": 9
        },
        "lastDate": {
          "year": 2023,
          "month": 5,
          "day": 6
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 4,
          "day": 16
        },
        "lastDate": {
          "year": 2023,
          "month": 5,
          "day": 13
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 4,
          "day": 23
        },
        "lastDate": {
          "year": 2023,
          "month": 5,
          "day": 20
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 4,
          "day": 30
        },
        "lastDate": {
          "year": 2023,
          "month": 5,
          "day": 27
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 5,
          "day": 7
        },
        "lastDate": {
          "year": 2023,
          "month": 6,
          "day": 3
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 5,
          "day": 14
        },
        "lastDate": {
          "year": 2023,
          "month": 6,
          "day": 10
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 5,
          "day": 21
        },
        "lastDate": {
          "year": 2023,
          "month": 6,
          "day": 17
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 5,
          "day": 28
        },
        "lastDate": {
          "year": 2023,
          "month": 6,
          "day": 24
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 6,
          "day": 4
        },
        "lastDate": {
          "year": 2023,
          "month": 7,
          "day": 1
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 6,
          "day": 11
        },
        "lastDate": {
          "year": 2023,
          "month": 7,
          "day": 8
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 6,
          "day": 18
        },
        "lastDate": {
          "year": 2023,
          "month": 7,
          "day": 15
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 6,
          "day": 25
        },
        "lastDate": {
          "year": 2023,
          "month": 7,
          "day": 22
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 7,
          "day": 2
        },
        "lastDate": {
          "year": 2023,
          "month": 7,
          "day": 29
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 7,
          "day": 9
        },
        "lastDate": {
          "year": 2023,
          "month": 8,
          "day": 5
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 7,
          "day": 16
        },
        "lastDate": {
          "year": 2023,
          "month": 8,
          "day": 12
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 7,
          "day": 23
        },
        "lastDate": {
          "year": 2023,
          "month": 8,
          "day": 19
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 7,
          "day": 30
        },
        "lastDate": {
          "year": 2023,
          "month": 8,
          "day": 26
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 8,
          "day": 6
        },
        "lastDate": {
          "year": 2023,
          "month": 9,
          "day": 2
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 8,
          "day": 13
        },
        "lastDate": {
          "year": 2023,
          "month": 9,
          "day": 9
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 8,
          "day": 20
        },
        "lastDate": {
          "year": 2023,
          "month": 9,
          "day": 16
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 8,
          "day": 27
        },
        "lastDate": {
          "year": 2023,
          "month": 9,
          "day": 23
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 9,
          "day": 3
        },
        "lastDate": {
          "year": 2023,
          "month": 9,
          "day": 30
        }
      },
      {
        "firstDate": {
          "year": 2023,
          "month": 9,
          "day": 10
        },
        "lastDate": {
          "year": 2023,
          "month": 10,
          "day": 7
        }
      }
    ]
  }
}
βœ… 200: URL-level CrUX API data
curl -d url='https://github.com/marketplace?type=actions' \
     -d effectiveConnectionType=4G \
     -d formFactor=DESKTOP \
     'https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=CRUX_API_KEY'
{
  "record": {
    "key": {
      "formFactor": "DESKTOP",
      "effectiveConnectionType": "4G",
      "url": "https://github.com/marketplace"
    },
    "metrics": {
      "cumulative_layout_shift": {
        "histogram": [
          {
            "start": "0.00",
            "end": "0.10",
            "density": 0.9430604982206409
          },
          {
            "start": "0.10",
            "end": "0.25",
            "density": 0.020804817957842878
          },
          {
            "start": "0.25",
            "density": 0.03613468382151657
          }
        ],
        "percentiles": {
          "p75": "0.02"
        }
      },
      "experimental_time_to_first_byte": {
        "histogram": [
          {
            "start": 0,
            "end": 800,
            "density": 0.9393059587999442
          },
          {
            "start": 800,
            "end": 1800,
            "density": 0.05378128024332917
          },
          {
            "start": 1800,
            "density": 0.006912760956726073
          }
        ],
        "percentiles": {
          "p75": 550
        }
      },
      "first_contentful_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 1800,
            "density": 0.9731800766283532
          },
          {
            "start": 1800,
            "end": 3000,
            "density": 0.017651888341543534
          },
          {
            "start": 3000,
            "density": 0.009168035030104028
          }
        ],
        "percentiles": {
          "p75": 809
        }
      },
      "first_input_delay": {
        "histogram": [
          {
            "start": 0,
            "end": 100,
            "density": 0.9936164307521526
          },
          {
            "start": 100,
            "end": 300,
            "density": 0.004579517069109083
          },
          {
            "start": 300,
            "density": 0.0018040521787399426
          }
        ],
        "percentiles": {
          "p75": 3
        }
      },
      "interaction_to_next_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 200,
            "density": 0.9759848893685868
          },
          {
            "start": 200,
            "end": 500,
            "density": 0.014301133297355571
          },
          {
            "start": 500,
            "density": 0.009713977334052826
          }
        ],
        "percentiles": {
          "p75": 50
        }
      },
      "largest_contentful_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 2500,
            "density": 0.9798854493386074
          },
          {
            "start": 2500,
            "end": 4000,
            "density": 0.012886949406791222
          },
          {
            "start": 4000,
            "density": 0.007227601254602516
          }
        ],
        "percentiles": {
          "p75": 895
        }
      }
    },
    "collectionPeriod": {
      "firstDate": {
        "year": 2023,
        "month": 9,
        "day": 15
      },
      "lastDate": {
        "year": 2023,
        "month": 10,
        "day": 12
      }
    }
  },
  "urlNormalizationDetails": {
    "originalUrl": "https://github.com/marketplace?type=actions",
    "normalizedUrl": "https://github.com/marketplace"
  }
}
βœ… 200: Origin-level CrUX API data
curl -d origin='https://github.com' \
     -d formFactor=PHONE \
     'https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=CRUX_API_KEY'
{
  "record": {
    "key": {
      "formFactor": "PHONE",
      "origin": "https://github.com"
    },
    "metrics": {
      "first_input_delay": {
        "histogram": [
          {
            "start": 0,
            "end": 100,
            "density": 0.9498680738786286
          },
          {
            "start": 100,
            "end": 300,
            "density": 0.03430079155672826
          },
          {
            "start": 300,
            "density": 0.015831134564643756
          }
        ],
        "percentiles": {
          "p75": 17
        }
      },
      "interaction_to_next_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 200,
            "density": 0.6478405315614636
          },
          {
            "start": 200,
            "end": 500,
            "density": 0.24584717607973486
          },
          {
            "start": 500,
            "density": 0.10631229235880371
          }
        ],
        "percentiles": {
          "p75": 272
        }
      },
      "largest_contentful_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 2500,
            "density": 0.7900432900432863
          },
          {
            "start": 2500,
            "end": 4000,
            "density": 0.13528138528138467
          },
          {
            "start": 4000,
            "density": 0.0746753246753239
          }
        ],
        "percentiles": {
          "p75": 2312
        }
      },
      "cumulative_layout_shift": {
        "histogram": [
          {
            "start": "0.00",
            "end": "0.10",
            "density": 0.8527302813017104
          },
          {
            "start": "0.10",
            "end": "0.25",
            "density": 0.09817981246552682
          },
          {
            "start": "0.25",
            "density": 0.04908990623276335
          }
        ],
        "percentiles": {
          "p75": "0.02"
        }
      },
      "experimental_time_to_first_byte": {
        "histogram": [
          {
            "start": 0,
            "end": 800,
            "density": 0.6018518518518505
          },
          {
            "start": 800,
            "end": 1800,
            "density": 0.3312757201646084
          },
          {
            "start": 1800,
            "density": 0.0668724279835391
          }
        ],
        "percentiles": {
          "p75": 1039
        }
      },
      "first_contentful_paint": {
        "histogram": [
          {
            "start": 0,
            "end": 1800,
            "density": 0.7165570175438571
          },
          {
            "start": 1800,
            "end": 3000,
            "density": 0.1781798245614029
          },
          {
            "start": 3000,
            "density": 0.10526315789473623
          }
        ],
        "percentiles": {
          "p75": 1934
        }
      }
    },
    "collectionPeriod": {
      "firstDate": {
        "year": 2023,
        "month": 9,
        "day": 15
      },
      "lastDate": {
        "year": 2023,
        "month": 10,
        "day": 12
      }
    }
  }
}
πŸ›‘ 400 INVALID_ARGUMENT: API key not valid, please pass a valid API key
curl -d origin='https://github.com' \
     'https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=INVALID_KEY'
{
  "error": {
    "code": 400,
    "message": "API key not valid. Please pass a valid API key.",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.Help",
        "links": [
          {
            "description": "Google developers console",
            "url": "https://console.developers.google.com"
          }
        ]
      }
    ]
  }
}
πŸ›‘ 400 INVALID_ARGUMENT: Invalid value at 'form_factor'/'ect'
curl -d url='https://github.com/' \
     -d formFactor=mobile  \
     'https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=CRUX_API_KEY'
{
  "error": {
    "code": 400,
    "message": "Invalid value at 'form_factor' (type.googleapis.com/google.chrome.uxreport.v1.FormFactor), \"mobile\"",
    "status": "INVALID_ARGUMENT",
    "details": [
      {
        "@type": "type.googleapis.com/google.rpc.BadRequest",
        "fieldViolations": [
          {
            "field": "form_factor",
            "description": "Invalid value at 'form_factor' (type.googleapis.com/google.chrome.uxreport.v1.FormFactor), \"mobile\""
          }
        ]
      }
    ]
  }
}
πŸ›‘ 404 NOT_FOUND: chrome ux report data not found
curl -d url='https://github.com/search' \
     'https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=CRUX_API_KEY'
{
  "error": {
    "code": 404,
    "message": "chrome ux report data not found",
    "status": "NOT_FOUND"
  }
}
πŸ›‘ 429 RESOURCE_EXHAUSTED: Quota exceeded limit 'Queries per 100 seconds' of service
curl -d url='https://github.com/search' \
     'https://chromeuxreport.googleapis.com/v1/records:queryRecord?key=CRUX_API_KEY'
{
  "code": 429,
  "message": "Quota exceeded for quota group 'default' and limit 'Queries per 100 seconds' of service 'chromeuxreport.googleapis.com' for consumer 'project_number:00000000000000'.",
  "status": "RESOURCE_EXHAUSTED",
  "details": [
    {
      "@type": "type.googleapis.com/google.rpc.Help",
      "links": [
        {
          "description": "Google developer console API key",
          "url": "https://console.developers.google.com/project/00000000000000/apiui/credential"
        }
      ]
    }
  ]
}

Credits

Sponsored by Treo - Page speed monitoring made simple.

About

A tiny (500 bytes) CrUX API wrapper that supports record & history API, handles errors, and provides types.

Topics

Resources

License

Stars

Watchers

Forks