Skip to main content

Overview

Results accumulates measurement data as the engine runs and exposes typed accessor methods for every metric. You can read from it at any time during a test run — methods return undefined (or an empty array) when the relevant measurement has not yet started.
import SpeedTest from '@cloudflare/speedtest';
import type { Results } from '@cloudflare/speedtest';

// Access during a run
const engine = new SpeedTest();
const bw = engine.results.getDownloadBandwidth(); // number | undefined

// Access on completion
engine.onFinish = (results: Results) => {
  console.log(results.getSummary());
};

Property

isFinished
boolean
required
true when every measurement in the configured sequence has completed and the results are final. Equivalent to SpeedTestEngine.isFinished. Use this to gate calls to getScores(), which requires all measurements to be done.
if (engine.results.isFinished) {
  console.log(engine.results.getScores());
}

Summary

getSummary()

getSummary(): {
  download?: number;
  upload?: number;
  latency?: number;
  jitter?: number;
  downLoadedLatency?: number;
  downLoadedJitter?: number;
  upLoadedLatency?: number;
  upLoadedJitter?: number;
  packetLoss?: number;
}
Returns a single object aggregating every available metric. Keys are only present when their corresponding measurement was included in the measurement sequence and produced a result. All values are in native units: bps for bandwidth, ms for latency and jitter, and a ratio between 0 and 1 for packet loss.
getSummary()
object
engine.onFinish = results => {
  const summary = results.getSummary();
  console.log(summary);
  // {
  //   download: 94500000,
  //   upload: 21300000,
  //   latency: 12,
  //   jitter: 1.4,
  //   downLoadedLatency: 38,
  //   downLoadedJitter: 4.2,
  //   upLoadedLatency: 29,
  //   upLoadedJitter: 3.1,
  //   packetLoss: 0
  // }
};

Bandwidth

All bandwidth values are in bits per second (bps). The single reduced value is the configured percentile of qualifying measurements (default: 90th percentile, set by bandwidthPercentile). Only measurements whose request duration exceeded bandwidthMinRequestDuration (default: 10 ms) are included.
To convert bps to Mbps, divide by 1e6. To convert to Kbps, divide by 1e3.
const mbps = (engine.results.getDownloadBandwidth() ?? 0) / 1e6;

getDownloadBandwidth()

getDownloadBandwidth(): number | undefined
Returns the computed download bandwidth. Returns undefined if no qualifying download measurement has completed.
engine.onResultsChange = () => {
  const bps = engine.results.getDownloadBandwidth();
  if (bps !== undefined) {
    console.log(`Download: ${(bps / 1e6).toFixed(1)} Mbps`);
  }
};

getDownloadBandwidthPoints()

getDownloadBandwidthPoints(): BandwidthPoint[]
Returns all individual download measurements as an array of BandwidthPoint objects. Useful for charting throughput over time.
BandwidthPoint
object
interface BandwidthPoint {
  bytes: number;
  bps: number;
  duration: number;
  ping: number;
  measTime: number;
  serverTime: number;
  transferSize: number;
}
const points = engine.results.getDownloadBandwidthPoints();

points.forEach(({ bytes, bps, duration }) => {
  console.log(
    `${bytes / 1e6} MB — ${(bps / 1e6).toFixed(2)} Mbps in ${duration.toFixed(0)} ms`
  );
});

getUploadBandwidth()

getUploadBandwidth(): number | undefined
Returns the computed upload bandwidth in bps. Same calculation and semantics as getDownloadBandwidth().

getUploadBandwidthPoints()

getUploadBandwidthPoints(): BandwidthPoint[]
Returns all individual upload measurements. Same BandwidthPoint shape as getDownloadBandwidthPoints().

Latency

All latency and jitter values are in milliseconds. The single reduced value is the configured percentile of all samples (default: median, set by latencyPercentile).

getUnloadedLatency()

getUnloadedLatency(): number | undefined
Returns the round-trip latency at idle. Measured by dedicated latency-type requests to the download API with bytes=0. Returns undefined if no latency measurement has started.

getUnloadedJitter()

getUnloadedJitter(): number | undefined
Returns the jitter at idle, calculated as the average absolute difference between consecutive latency samples. Requires at least two latency measurements; returns undefined otherwise.

getUnloadedLatencyPoints()

getUnloadedLatencyPoints(): number[]
Returns all raw latency values in measurement order. Each element is a single ping value in ms.
const latency = engine.results.getUnloadedLatency();   // number | undefined
const jitter  = engine.results.getUnloadedJitter();    // number | undefined
const points  = engine.results.getUnloadedLatencyPoints(); // number[]

getDownLoadedLatency()

getDownLoadedLatency(): number | undefined
Returns the latency observed while the connection is saturated with download traffic. Only includes samples from requests that lasted longer than loadedRequestMinDuration (default: 250 ms). Requires measureDownloadLoadedLatency: true (default).

getDownLoadedJitter()

getDownLoadedJitter(): number | undefined
Returns jitter during download load, in ms.

getDownLoadedLatencyPoints()

getDownLoadedLatencyPoints(): number[]
Returns all raw loaded-latency samples from the download phase. Capped at loadedLatencyMaxPoints (default: 20); when more samples are available, the most recent ones are kept as they are considered most accurate.
const dlLatency = engine.results.getDownLoadedLatency();
const dlJitter  = engine.results.getDownLoadedJitter();
const dlPoints  = engine.results.getDownLoadedLatencyPoints();

getUpLoadedLatency()

getUpLoadedLatency(): number | undefined
Returns the latency observed while the connection is saturated with upload traffic. Requires measureUploadLoadedLatency: true (default).

getUpLoadedJitter()

getUpLoadedJitter(): number | undefined
Returns jitter during upload load, in ms.

getUpLoadedLatencyPoints()

getUpLoadedLatencyPoints(): number[]
Returns all raw loaded-latency samples from the upload phase.
const ulLatency = engine.results.getUpLoadedLatency();
const ulJitter  = engine.results.getUpLoadedJitter();
const ulPoints  = engine.results.getUpLoadedLatencyPoints();
Loaded latency is typically higher than unloaded latency. The difference between the two (loaded latency increase) feeds directly into AIM score calculations — a large increase under load indicates bufferbloat.

Packet loss

Packet loss requires a WebRTC TURN server. You must provide your own TURN credentials — the public Cloudflare TURN server is deprecated. See Packet loss for setup instructions.

getPacketLoss()

getPacketLoss(): number | undefined
Returns the packet loss ratio as a number between 0 and 1. Multiply by 100 for a percentage. Returns undefined if no packetLoss measurement was configured or has not yet started.
const loss = engine.results.getPacketLoss();

if (loss !== undefined) {
  console.log(`Packet loss: ${(loss * 100).toFixed(2)}%`);
}

getPacketLossDetails()

getPacketLossDetails():
  | { packetLoss: number; totalMessages: number; numMessagesSent: number; lostMessages: number[] }
  | { error: string }
  | undefined
Returns the full packet loss result object, or an error object if the TURN connection failed, or undefined if the measurement has not started.
getPacketLossDetails()
object | undefined
const details = engine.results.getPacketLossDetails();

if (details && 'error' in details) {
  console.error('Packet loss measurement failed:', details.error);
} else if (details) {
  const { packetLoss, numMessagesSent, lostMessages } = details;
  console.log(`Sent ${numMessagesSent} packets, lost ${lostMessages.length}`);
  console.log(`Loss rate: ${(packetLoss * 100).toFixed(2)}%`);
}

AIM scores

getScores()

getScores(): {
  [useCase: string]: {
    points: number;
    classificationIdx: 0 | 1 | 2 | 3 | 4;
    classificationName: 'bad' | 'poor' | 'average' | 'good' | 'great';
  }
}
Returns AIM (Aggregated Internet Measurement) scores that classify the network connection quality per use case (for example, streaming, gaming, or video calling). The score object is keyed by use case name.
getScores()
object
getScores() only returns meaningful results after isFinished is true. Calling it mid-run returns scores based on partial data, which may not reflect the final connection quality.
engine.onFinish = results => {
  const scores = results.getScores();
  console.log(scores);
  // {
  //   streaming:   { points: 87, classificationIdx: 3, classificationName: 'good' },
  //   gaming:      { points: 72, classificationIdx: 3, classificationName: 'good' },
  //   rtc:         { points: 61, classificationIdx: 2, classificationName: 'average' },
  // }

  for (const [useCase, score] of Object.entries(scores)) {
    console.log(`${useCase}: ${score.classificationName} (${score.points} pts)`);
  }
};
See AIM scores for the full reference on classifications, thresholds, and contributing metrics.

Build docs developers (and LLMs) love