Lab Test API
The lab test API lets you create monitored pages, trigger tests, fetch results, and export monitoring data.
Creating a page
Pages live inside a project. Call createPage on the project (or debugbear.pages.create(projectId, …)) to add one. See the Projects API for managing projects.
const { DebugBear } = require("debugbear");
const debugbear = new DebugBear(process.env.DEBUGBEAR_API_KEY);
const project = await debugbear.projects.get(projectId);
const page = await project.createPage({
name: "Example",
url: "http://example.com/",
region: "germany",
testScheduleName: "Every 12 hours",
deviceName: "Desktop",
advancedSettings: ["Staging Basic Auth"],
tags: ["Tag 1"],
});
console.log(page);
The created page:
{
"id": "11111",
"name": "Example",
"url": "http://example.com/",
"region": "germany",
"testSchedules": [{ "id": "22222", "name": "Every 12 hours" }],
"device": {
"id": "33333",
"name": "Desktop",
"rtt": 40,
"bandwidthKbps": 8192,
"formFactor": "desktop",
"cpuThrottling": 1
},
"advancedSettings": [
{ "id": "18", "name": "Staging Basic Auth", "type": "basicAuth" }
],
"tags": ["Tag 1"]
}
Using cURL
curl https://www.debugbear.com/api/v1/projects/123456/pages \
-X POST \
-H "x-api-key: API_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "Example Homepage", "url": "https://example.com", "region": "uk", "tags": ["homepage"]}'
Updating a page
await debugbear.pages.update(PAGE_ID, {
tags: ["staging"],
advancedSettings: ["Staging Auth"],
});
Deleting a page
await project.deletePage(page.id);
Using cURL
curl https://www.debugbear.com/api/v1/pages/123456 \
-X DELETE \
-H "x-api-key: API_KEY"
Valid test server regions
See server locations for the supported region values.
Running a website test
You can either run a single test or trigger tests in bulk.
Create a script.js file like this:
const { DebugBear } = require("debugbear");
// TypeScript: import { DebugBear } from "debugbear"
const debugbear = new DebugBear(process.env.DEBUGBEAR_API_KEY);
const pageId = 185;
debugbear.pages.analyze(pageId).then((analysis) => {
analysis.waitForResult().then(() => {
console.log("Test complete, view results here: " + analysis.url);
});
});
Then run DEBUGBEAR_API_KEY=... node script.js.
Additional options
The Node module supports similar arguments to the CLI.
Building a particular commit
debugbear.pages.analyze(pageId, {
commitHash: "e2ba122",
buildTitle: "Add support for tags",
// infer additional details from the environment, for
// example the name of the current branch
inferBuildInfo: true,
});
Customizing the URL and HTTP headers
debugbear.pages.analyze(pageId, {
url: "http://staging.com",
customHeaders: {
"X-Feature-Flags": "tags",
},
});
Passing in custom cookies
debugbear.pages.analyze(pageId, {
cookies: [
{
name: "testcookie",
value: "testvalue",
domain: "www.example.com",
path: "/",
},
],
});
Testing HTML changes
Pass substituteDocument to serve different HTML for the main document, so you can measure a change before deploying it. Fetch the current HTML with getHtml, modify it, and send it back.
const { html } = await debugbear.analyses.getHtml(analysisId);
debugbear.pages.analyze(pageId, {
substituteDocument: {
body: html.replace("</head>", '<link rel="preload" href="/hero.jpg" as="image"></head>'),
},
});
To compare the change against the unmodified page, use the experiments API instead, which runs both sides for you.
Blocking network requests
Pass blockRequestsPatterns to stop matching requests from loading during the test. That's useful for measuring the impact of removing a script or a third party without changing your site. See Experiments for the equivalent feature in the UI.
debugbear.pages.analyze(pageId, {
blockRequestsPatterns: ["*/analytics.js", "*doubleclick.net*"],
});
Patterns are matched against the full request URL and support * as a wildcard. You can also pass a single comma-separated string instead of an array.
Using cURL
curl https://www.debugbear.com/api/v1/page/PAGE_ID/analyze \
-X POST \
-H "x-api-key: API_KEY" \
-H "Content-Type: application/json" \
-d '{ "blockRequestsPatterns": ["*/analytics.js", "*doubleclick.net*"] }'
Access build status and metrics
const analysis = await debugbear.pages.analyze(pageId, {
commitHash: "abc123",
});
const res = await analysis.waitForResult();
console.log(res);
This might log something like this:
{
"url": "https://www.debugbear.com/viewResult/787431",
"hasFinished": true,
"build": {
"status": "failure",
"oneLineSummary": "PF 100 ➔ 96, SEO 85 ➔ 80, Req# +1",
"metrics": {
"analysis.date": "2020-02-14T19:06:42.201Z",
"performance.speedIndex": 1087,
"performance.interactive": 895,
"performance.firstContentfulPaint": 845,
"performance.firstMeaningfulPaint": 1301,
"performance.score": 0.96,
"accessibility.score": 0.55,
"bestPractices.score": 0.79,
"seo.score": 0.8,
"pwa.score": 0.54,
"pageWeight.total": 1666205,
"pageWeight.document": 5745,
"pageWeight.stylesheet": 4562,
"pageWeight.image": 1571870,
"pageWeight.script": 65597,
"pageWeight.font": 18431,
"pageWeight.ajax": 0,
"pageWeight.media": 0,
"pageWeight.other": 0,
"pageWeight.redirect": 0,
"cpu.total": 42,
"cpu.scriptEvaluation": 8.4,
"console.errors": 0,
"console.warnings": 0,
"html.errors": 1,
"html.warnings": 1,
"mark.start": 120,
"mark.fully-rendered": 14125,
"measure.timeout": 14005,
"crux.granularity": "url",
"crux.fcp.p75": 906,
"crux.lcp.p75": 901,
"crux.cls.p75": 0,
"crux.fid.p75": 2
}
}
}
Status is neutral when no performance budget is set, failure when any budget is breached, or success otherwise.
To avoid polling for results from CI, see webhooks for push-based delivery.
Using cURL
You can trigger tests without using the Node module:
curl https://www.debugbear.com/api/v1/page/PAGE_ID/analyze \
-X POST \
-H "x-api-key: API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"http://example.com","buildTitle":"Site update"}'
Run tests in bulk
You can trigger multiple tests at once with the analyzeBulk function.
const bulkTests = await debugbear.pages.analyzeBulk([123, 124]);
const results = await bulkTests.waitForResult();
Currently, the bulk test API does not support passing additional options like a commitHash or custom headers. Contact support if you are interested in these features.
The result looks like this:
{
"hasFinished": true,
"results": [
{
"hasFinished": true,
"build": {
"status": "neutral",
"metrics": {
"analysis.date": "2024-05-26T07:56:39.634Z",
"performance.largestContentfulPaint": 1822,
"performance.totalBlockingTime": 190,
"...": "..."
},
"oneLineSummary": "No changes.",
"budgets": []
},
"analysis": {
"commitHash": null,
"commitBranch": null,
"buildTitle": null
},
"page": {
"id": "123",
"name": "MZ Finland",
"url": "https://www.example.com/",
"...": "..."
}
},
"..."
]
}
Experiments
An experiment measures whether an HTML change would make a page faster, without deploying it. DebugBear runs the page twice, once with its current HTML and once with yours, and compares the results. This is the same feature as experiments in the UI.
Describe the change and DebugBear applies it to the page's current HTML for you:
const experiment = await debugbear.experiments.run(pageId, {
headerHtml: '<link rel="preload" href="/hero.jpg" as="image">',
title: "Preload hero image",
runNTimes: 3,
});
console.log(experiment.url);
Use replacements to change markup that is already on the page. Every occurrence of target is replaced, and an empty value removes it:
const experiment = await debugbear.experiments.run(pageId, {
replacements: [
{
target: '<img src="/hero.jpg"',
value: '<img fetchpriority="high" src="/hero.jpg"',
},
],
title: "Prioritise the hero image",
});
Because the page's HTML never leaves the server, this works whatever the size of the document. You can still pass html to replace the whole document, but then you have to fetch it with analyses.getHtml and send it back, which is slower and can run into request size limits on large pages.
baselineHtml defaults to the page's current HTML, so you normally only describe the change. The baseline is also served with the same response headers and timing as the real page, so the HTML is the only difference between the two sides.
Options:
| Option | Description |
|---|---|
headerHtml | Markup to add at the start of the page's head, for example a preload or an inline style. |
replacements | An array of { target, value } pairs. Each replaces every occurrence of target in the HTML. Applied before headerHtml. |
html | A complete replacement document. Defaults to the page's current HTML, which headerHtml and replacements are then applied to. |
baselineHtml | HTML to compare against. Defaults to the page's current HTML. |
blockRequestsPatterns | URL patterns to block in the variant only, for testing the removal of a script or third party. |
runNTimes | How many times to run each side. Higher values reduce run-to-run noise. Defaults to 1. |
title | A name for the experiment. |
Getting experiment results
An experiment finishes when both of its tests have finished.
const result = await experiment.waitForResult();
// or poll it yourself:
const result = await debugbear.experiments.get(experiment.experimentId);
const metric = "performance.largestContentfulPaint";
console.log(result.baselineMetrics[metric], result.variantMetrics[metric]);
// 2100 1400
baselineMetrics and variantMetrics give the full metric set for each side, using the same metric names as the metrics export. Neither is present until hasFinished is true.
Compare the two to see the effect of your change, remembering that for timing metrics a lower value is faster. Treat small differences as run-to-run noise unless you raised runNTimes.
Using cURL
curl https://www.debugbear.com/api/v1/page/PAGE_ID/experiment \
-X POST \
-H "x-api-key: API_KEY" \
-H "Content-Type: application/json" \
-d '{ "html": "<html>…</html>", "title": "Preload hero image" }'
curl https://www.debugbear.com/api/v1/experiment/EXPERIMENT_ID \
-X GET \
-H "x-api-key: API_KEY"
Export lab test result metrics
The getMetrics function provides the same metrics as the on-page data export button, for example performance metrics, page weight, and Lighthouse scores.
let metrics = await debugbear.pages.getMetrics(pageId, {
from: new Date(2022, 4, 1),
to: new Date(2022, 5, 1),
});
console.log(metrics[0]["performance.score"]);
Using cURL
You can also load page metrics by using the HTTP API directly.
curl https://www.debugbear.com/api/v1/page/PAGE_ID/metrics \
-X GET \
-H "x-api-key: API_KEY" \
-G \
-d from=2022-02-01 \
-d to=2022-03-01
This will load data spanning from midnight on 1 February 2022 to midnight on 1 March 2022.
Get detailed analysis data
You can retrieve detailed information about a specific analysis using the analysis ID.
Get analysis info
The get function returns the basic info for an analysis (status, metrics, build, commit, etc.). Use it when you already have an analysis ID from an earlier run, a webhook payload, or a dashboard URL and don't want to trigger a new test.
const analysis = await debugbear.analyses.get(analysisId);
console.log(analysis);
Using cURL
curl https://www.debugbear.com/api/v1/analysis/ANALYSIS_ID \
-X GET \
-H "x-api-key: API_KEY"
Get network requests
The getRequests function returns a list of all network requests made during the test.
const requests = await debugbear.analyses.getRequests(analysisId);
console.log(requests);
Using cURL
curl https://www.debugbear.com/api/v1/analysis/ANALYSIS_ID/requests \
-X GET \
-H "x-api-key: API_KEY"
Get Lighthouse report
The getLighthouseReport function returns the complete Lighthouse report in JSON format for the tested page.
const lhr = await debugbear.analyses.getLighthouseReport(analysisId);
console.log(lhr);
Using cURL
curl https://www.debugbear.com/api/v1/analysis/ANALYSIS_ID/lhr \
-X GET \
-H "x-api-key: API_KEY"
Get page HTML
The getHtml function returns the HTML of the main document, as it was served by the page before any client-side rendering.
const { html } = await debugbear.analyses.getHtml(analysisId);
console.log(html);
The response is a JSON object with a single html property.
This returns a 404 error if HTML is not available for the test result. That's the case for older results, and for pages whose HTML document was larger than the maximum size DebugBear stores (2 MB by default).
Using cURL
curl https://www.debugbear.com/api/v1/analysis/ANALYSIS_ID/html \
-X GET \
-H "x-api-key: API_KEY"
Get recent results for pages in a project
Call projects.getPageMetrics to get the latest metrics for all your pages, similar to what you'd see on your project overview page on the DebugBear website.
const pageMetrics = await debugbear.projects.getPageMetrics(project.id);
pageMetrics.forEach((item) => {
console.log(`SEO score for ${item.page.name}: ${item.metrics["seo.score"]}`);
});
Getting older metrics
By default, the most recent results will be reported by the API. If you need to load results for an older build, you can pass in the before parameter, so results after that date will be ignored.
const pageMetrics = await debugbear.projects.getPageMetrics(project.id, {
before: new Date(2020, 8, 4),
});
Retrieving results from other environments
By default, only the test we consider to be primary will be returned by the API. These are tests that return accurate information about your page, so if you have run any experiments either via our UI or by including custom headers in an analysis, these results would be excluded as they may not be indicative of how your page typically executes.
To include non-primary results, you can set the environments option to "all" when using the per-page getMetrics method:
const metrics = await debugbear.pages.getMetrics(pageId, {
environments: "all",
});
Using cURL
curl https://www.debugbear.com/api/v1/projects/PROJECT_ID/pageMetrics \
-X GET \
-H "x-api-key: API_KEY" \
-G \
-d before=2020-02-01
Retrieving a project
Use projects.get to retrieve a project, including the list of pages within it.
const project = await debugbear.projects.get(project.id);
console.log(project.name, project.pages);
Using cURL
curl https://www.debugbear.com/api/v1/projects/PROJECT_ID \
-X GET \
-H "x-api-key: API_KEY"