Skip to content

Measure your feed delivery

Don't take our speed claims on trust. Get a free key, connect from your own server and test the feed yourself. See how delivery performs in the setup you plan to use.

What this test measures

This guide measures the time between our v2 server dispatch timestamp and your application receiving the message. Your location, network route and client workload all affect the result.

The result is dispatch-to-client delivery. Original publication and detection happen earlier; measuring those intervals needs a separate publication reference. This calculation uses our server timestamp and your receive clock, so their accuracy matters.

Connect with a free key

Open @NLF_websocket_bot on Telegram and get a FREE key. Allow about a minute for a new key to activate, then use it as the bearer token for wss://tokyo.newlistings.pro/v2/full. This guide uses the v2 full schema.

The first READY message reports your serving endpoint, subscription.plan and subscription.delay_ms. Save those values so each result identifies the connection you actually tested.

Free access lets you inspect the stream and measure delivery to your host. The configured delay happens before sent_time_us is stamped for dispatch, so this calculation measures the delivery interval after that wait. Keep the applied plan and delay with your results; a paid-key comparison needs its own capture.

Fields, units and the calculation

For live v2 events, calculate observed_send_to_receive_ms = (received_us - sent_time_us) / 1000. Capture receive time before parsing JSON, logging or doing other work. This measures delivery to your application, including buffering and scheduling before the message handler runs.

Scroll horizontally to compare all columns.

Measurement fields for /v2/full
FieldUnitMeaning
sent_time_usUnix epoch microsecondsOur server timestamp taken during event serialization for dispatch, after any configured tier delay. It is not an exchange publication time or a network-card transmit timestamp.
received_usUnix epoch microsecondsYour client timestamp at the start of the message handler, before JSON parsing. This is a field created by the example, not sent by the feed.
detected_time_usUnix epoch microsecondsThe recorded detection timestamp. It is not proof of first exchange availability and is not the starting point for this delivery calculation.
subscription.delay_msMillisecondsThe configured publish delay reported in READY. Keep it with each run; the calculation above excludes the wait before dispatch.
endpoint / subscription.planTextThe serving endpoint and applied plan from READY. Record your receiver region separately.
received_mono_nsLocal monotonic nanosecondsA local monotonic timestamp, saved as a decimal string. Use it for local intervals; its origin is unrelated to Unix epoch timestamps.

Check the clocks before trusting the number

The IETF one-way delay standard explains that clock synchronization error and clock resolution affect a timestamp subtraction. If your clock is ahead of ours, the observed delay is inflated; if it is behind, the delay is understated. A negative result needs a clock or timestamp check. Keep it in the raw log.

Check synchronization before and after the run. On a Linux host using chrony, save chronyc tracking and chronyc sources -v. Record system offset, root delay, root dispersion, reference time and synchronization status. These describe your clock; they do not establish the error of our server clock. If the combined uncertainty is unknown, label the result an uncorrected observation.

The Node.js example combines performance.timeOrigin with performance.now() to estimate epoch time. It captures process.hrtime.bigint() separately for local intervals. Microsecond units do not guarantee microsecond accuracy. Synchronize the clock before starting Node, and restart the capture after a clock correction.

Run a five-minute Node.js capture

Use Node.js on the host where you intend to consume the feed. Install ws, set NLF_API_KEY to your free key and NLF_RECEIVER_REGION to your host location. Save the script as measure-feed.cjs, then run node measure-feed.cjs > feed-measurement.jsonl. Use a new output filename for each run.

The script captures five minutes after connection, or stops when you press Ctrl+C or the connection closes. It records the applied plan and delay without logging your key. Check the end record for counts and the stop reason. Repeat across sessions to collect more events; if a window contains no announcements, run another capture.

This example makes one connection, with no automatic reconnect or replay. It measures when the Node.js message handler starts. Earlier logging and other work can delay later handlers, so use the same client and workload across runs.

// npm install ws
// Set NLF_API_KEY and NLF_RECEIVER_REGION in your environment.
// Run: node measure-feed.cjs > feed-measurement.jsonl
const WebSocket = require("ws");
const { performance } = require("node:perf_hooks");

if (!process.env.NLF_API_KEY || !process.env.NLF_RECEIVER_REGION) {
  throw new Error("Set NLF_API_KEY and NLF_RECEIVER_REGION first.");
}
const context = {
  endpoint_url: "wss://tokyo.newlistings.pro/v2/full",
  receiver_region: process.env.NLF_RECEIVER_REGION,
};
const counts = { events: 0, unmeasured: 0, negative: 0 };
const record = row => console.log(JSON.stringify(row));
let timer;
let finished = false;
const ws = new WebSocket(context.endpoint_url, {
  headers: { authorization: `Bearer ${process.env.NLF_API_KEY}` },
  handshakeTimeout: 10000,
});
function finish(reason, closeCode = null) {
  if (finished) return;
  finished = true;
  clearTimeout(timer);
  record({ kind: "end", utc: new Date().toISOString(), reason,
    close_code: closeCode, ...counts });
  ws.terminate();
}
ws.on("open", () => {
  record({ kind: "start", ...context, utc: new Date().toISOString(),
    node_version: process.version });
  timer = setTimeout(() => finish("five-minute window complete"), 300000);
});
ws.on("message", raw => {
  const receivedUs = Math.round(
    (performance.timeOrigin + performance.now()) * 1000
  );
  const receivedMonoNs = process.hrtime.bigint().toString();
  if (finished) return;
  let event;
  try { event = JSON.parse(raw.toString()); } catch { event = null; }
  if (event?.code === "READY") {
    context.serving_endpoint = event.endpoint;
    context.plan = event.subscription?.plan;
    context.delay_ms = event.subscription?.delay_ms;
    record({ kind: "ready", ...context });
    return;
  }
  const sentUs = event?.sent_time_us;
  if (!Number.isSafeInteger(sentUs) || sentUs < 1e15 || sentUs >= 1e16) {
    counts.unmeasured++;
    record({ kind: "unmeasured", received_us: receivedUs,
      code: event?.code ?? null });
    return;
  }
  const deltaMs = (receivedUs - sentUs) / 1000;
  counts.events++;
  counts.negative += Number(deltaMs < 0);
  record({ kind: "event", ...context, id: event.id, url: event.url,
    sent_time_us: sentUs, received_us: receivedUs,
    received_mono_ns: receivedMonoNs,
    observed_send_to_receive_ms: deltaMs,
    clock_check_required: deltaMs < 0 });
});
ws.on("close", code => finish("connection closed", code));
ws.on("error", () => finish("connection error"));
process.once("SIGINT", () => finish("user stopped"));

Read your result

This shortened record is synthetic, only to show the arithmetic. It is not an NLF benchmark. The delay value is illustrative; your READY message supplies the actual setting.

The two timestamps differ by 2,500 microseconds, giving an observed 2.5 milliseconds after dispatch. That value is uncorrected for clock error and excludes the configured wait before dispatch. Build your summary from the complete capture.

{
  "kind": "event",
  "serving_endpoint": "tokyo/full",
  "receiver_region": "Tokyo",
  "plan": "feed_free",
  "delay_ms": 3000,
  "sent_time_us": 1789171200000000,
  "received_us": 1789171200002500,
  "observed_send_to_receive_ms": 2.5,
  "clock_check_required": false
}

Keep enough evidence to reproduce your result

Report the UTC start and end, endpoint, receiver region, applied plan and delay, client version, clock checks and event count. Keep the original timestamp rows. State any filters and the number of rows they remove.

For each consistent setup, report the median, p95, p99 and maximum with the event count and connection duration. Include the clock-check flags, unmeasured-frame count and any disconnects so the capture is reproducible. Use a larger sample for meaningful percentiles; this short test measures delivery rather than service uptime.

To check announcement arrival against another observation you control, match the same official announcement URL and event type on one receiver. Capture both arrival times with the same monotonic clock. Account for differences in content and processing before interpreting the gap. Our dispatch timestamp alone cannot establish who delivered an announcement first.

Check the event as well as the timing

Follow the original announcement URL and check the exchange, classification and ticker symbols. Review the fields your application needs alongside your timing results; paid keys also include token enrichment.

Keep reading

Explore the WebSocket API

Get a free key