> ## Documentation Index
> Fetch the complete documentation index at: https://platform.trendlinelabs.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Benchmarks

What we measured, how, and what we have not measured yet. Every number on this page is MEASURED in the run
named with it, with its sample size and the machine it was measured from. A number we have not measured
does not appear in a table.

## Run of 2026-10-21

* When: 2026-10-21, 00:26 to 01:04 UTC (38 minutes).
* From: one virtual server in Vilnius, Lithuania (the location its address geolocates to), over IPv4. This
  machine is far from our servers: its median TCP connect to our API took 198 ms.
* Key: one free-plan key with `market:read`. Our API was paced at 40 requests a minute (the free plan allows
  60\), each venue at no more than one request every 2 seconds, one stream connection at a time.
* Markets: 12 legs on 8 markets, chosen at the start of the run: 4 Kalshi and 4 Polymarket legs from
  `/opportunities` (markets listed on both), and 4 Limitless legs from the top of `/markets/search`.
* Load on the measuring machine: shared with other work, sampled about once a minute (38 readings): a
  1-minute load average of 1.3 to 9.8 on 4 CPUs and 61% to 71% CPU steal in every reading, so this run has
  no unloaded comparison. Under the same load, a TCP connect to Kalshi's nearby edge took 7.5 ms at the
  median (n = 20), which bounds the network-stack delay this machine adds to a request at a few
  milliseconds; delays in our client's own code are not bounded by it, and they can widen the tails.

## Method

1. Each request is timed with a monotonic clock from the moment it is sent: time to first byte (TTFB) when
   the response headers arrive, total when the last body byte arrives.
2. A request is cold when a new TCP and TLS connection was made for it (observed, not assumed) and warm
   when it reused one. At least every fifth request per route was made on a fresh connection (more when a
   server had closed the idle connection).
3. Routes are sampled round-robin over the whole window, so each route sees the same network conditions.
4. Our book against the venue's: right after each of our order-book answers, the venue's own public book
   for the same market is read, and the best YES bid and ask are compared. A match is both within 0.05 cents.
5. The network floor is the TCP connect time to each host, 20 connects per host before the window.
6. Percentiles are linear-interpolated between the closest ranks. The metrics were written down before the
   run started, and every raw sample is kept with the run.

## Network floor

TCP connect time from the measuring machine (n = 20 per host).

| host | min | p50 | p90 | max |
| - | - | - | - | - |
| our REST API | 132 ms | 198 ms | 204 ms | 220 ms |
| our stream host | 184 ms | 199 ms | 209 ms | 213 ms |
| Polymarket CLOB | 22 ms | 80 ms | 103 ms | 194 ms |
| Kalshi trading API | 7 ms | 8 ms | 74 ms | 77 ms |
| Limitless API | 22 ms | 74 ms | 93 ms | 96 ms |

The venues answer from content-delivery edges near this machine; our hosts do not. To judge what our
servers add, compare our numbers with the floor to our hosts, not with the venues' figures.

## REST latency

Warm requests, total time in ms, including the error answers a route gave (see [Reliability](#reliability)).

| route | n | p50 | p90 | max |
| - | - | - | - | - |
| `GET /public/v1/key` | 24 | 200 | 206 | 212 |
| `GET /public/v1/markets/search` (20 results) | 24 | 197 | 205 | 222 |
| `GET /public/v1/markets/{universal_id}/orderbook`, Kalshi | 72 | 293 | 304 | 400 |
| `GET /public/v1/markets/{universal_id}/orderbook`, Polymarket | 96 | 296 | 322 | 395 |
| `GET /public/v1/markets/{universal_id}/orderbook`, Limitless | 90 | 297 | 308 | 395 |
| `GET /public/v1/markets/{universal_id}/trades`, Kalshi | 96 | 295 | 310 | 479 |
| `GET /public/v1/markets/{universal_id}/trades`, Polymarket | 96 | 294 | 315 | 425 |
| `GET /public/v1/markets/{universal_id}/candles` (1 h bars, 24 h) | 24 | 300 | 869 | 995 |
| `GET /public/v1/prices/snapshot` (8 markets) | 24 | 404 | 553 | 714 |
| `GET /public/v1/opportunities` | 24 | 496 | 506 | 597 |
| `GET /public/v1/events` (50 events) | 24 | 798 | 805 | 821 |

A cold request (new connection) to the order book took 791 ms (Kalshi, n = 48), 797 ms (Polymarket,
n = 24) and 791 ms (Limitless, n = 30) at the median: about 500 ms of TCP and TLS handshakes on top.

What it means:

* `/key` and search answer in about one network round trip (200 ms against a 198 ms median connect): from
  this machine, distance is nearly all of their time.
* Order books and trades take about 95 ms more than that, on every venue, which is the time to read and
  check the book or tape on our side.
* `/events` spends about 400 ms more before its first byte and 200 ms more on its body.
* Keep connections open (HTTP keep-alive) and reuse one client: a new connection costs about 500 ms here.

## Freshness as served

The age fields our answers carry, read from every answer during the run.

| what | n | p50 | p90 | max | bound |
| - | - | - | - | - | - |
| Polymarket order book `age_ms` | 120 | 9.8 s | 167 s | 528 s | 1800 s |
| Kalshi order book `age_ms` | 108 | 260 s | 1011 s | 1172 s | 1200 s |
| Limitless order book `age_ms` | 30 | 805 s | 1380 s | 1524 s | 3600 s |
| `/opportunities` `age_s` | 31 | 3 s | 7 s | 10 s | 600 s |
| `/events` `universe_age_s` | 30 | 11 s | 26 s | 35 s | 300 s |
| `/markets/search` `age_s` | 31 | 27 s | 50 s | 58 s | 1200 s |

An order book's age is the time since its last change, not since we last checked it. The next section shows
that the old books were unchanged on the venue too.

On Polymarket, one market's newest trade was 244 s old at the median, and three were 27 to 49 hours old.
On those three, the newest trade time we served was within 3 s of the newest trade in Polymarket's own
public trade history: the markets had not traded for that long.

## Our book against the venue's

Our order book, then the venue's public book for the same market straight after it, compared on the best
YES bid and ask.

| venue | pairs | top of book matched | time between the two reads, p50 | max |
| - | - | - | - | - |
| Kalshi | 108 | 108 (100%) | 300 ms | 1296 ms |
| Polymarket | 120 | 120 (100%) | 201 ms | 2607 ms |
| Limitless | 30 | 30 (100%) | 754 ms | 2402 ms |

Read this table with three limits:

* Pairs are our 200 answers only. The order-book requests we answered 503 are not in it (see
  [Reliability](#reliability)).
* The top of book moved on 1 of the 9 compared legs (a Polymarket market, 7 different tops over 30 pairs);
  on the other 8 it did not change during the run, which is easier to match.
* "The same market" means the market our answer names in `venue_market_id`.

### A second window on busier markets

2026-10-21, 01:38 to 01:51 UTC, same machine and pacing, 9 legs on 3 markets listed on all three venues and
among Polymarket's busiest by 24-hour volume: the Thunder against Spurs game and two US midterm
balance-of-power markets.

| venue | pairs | top of book matched | time between the two reads, p50 | max |
| - | - | - | - | - |
| Kalshi | 53 | 53 (100%) | 300 ms | 601 ms |
| Polymarket | 54 | 54 (100%) | 291 ms | 2386 ms |
| Limitless | 18 | 18 (100%) | 291 ms | 427 ms |

The books changed during this window (our median `age_ms` per leg was 0.2 s to 41 s), but the best bid
and ask prices did not change on any of the 7 compared legs; the comparison covers prices, not sizes.
The other 2 Limitless legs answered 503 throughout and are not in the table.

## Coverage

One walk of `GET /public/v1/events` (32 pages) found 3122 active events.

| venue | active events listing it |
| - | - |
| Polymarket | 2791 |
| Kalshi | 2555 |
| predict.fun | 845 |
| Limitless | 483 |

3007 of the 3122 events (96%) are listed on two or more venues: 2578 on two, 313 on three and 116 on all
four. Reading the details of 40 of those events gave 128 active markets, all 128 listed on two or more
venues (Kalshi 124, Polymarket 121, Limitless 15, predict.fun 10).

## Stream

One connection, 600 s, 36 channels (book, price and trades for each of the 12 legs).

| step | measured |
| - | - |
| connect (TCP, TLS and the WebSocket upgrade) | 793 ms |
| subscribe to its `subscribed` answer | 401 ms |
| subscribe to the first data frame, each live channel | 604 ms |
| frames in 600 s | 672 book, 263 price, 13 trades, 39 heartbeats; no gap in `seq` |
| book frames per minute, busiest channel | 29 |
| a book change's `ts_ms` to the frame's `ts`, median per updating book channel | 17 to 19 ms on 5 of the 6; 1617 ms on a Limitless book with 4 frames |

The three Limitless legs whose books had not changed within the 1 h bound answered a `status` of upstream\_unavailable instead of data, and
one Limitless leg's book and price frames began 250 s after subscribing. Both clocks in the last row are
ours, so it measures our stream's own delay after a change is stamped, not the venue's time to us.

## Reliability

| what | requests | answers |
| - | - | - |
| our API, all routes | 1039 | 757 × 200, 282 × 503 upstream\_unavailable |
| the venues' public endpoints | 959 | 959 × 200 |

The 282 answers of 503 came from 6 of the 12 legs, for two reasons.

The book had not changed for longer than the venue's freshness bound (Limitless 1 h, Kalshi 20 min), so the
API refused it rather than serve it without a fresh timestamp; the venue showed the same unchanged top of book.

* 90 from 3 of the 4 Limitless order books (all 30 requests each). Limitless showed the same two-sided
  top of book on all 58 of our reads per market.
* 12 from 2 Kalshi order books (8 and 4 answers). Their served `age_ms` reached 1159 s and 1166 s against the 1200 s
  bound; the venue's top did not change, and our book matched it on the reads before and after.
* This run did not record the 503 messages; the second window, which does, gave the same reason on all 36
  of its Limitless book refusals: "the book is 4228 s old, older than the limitless bound of 3600 s" (and
  similar, up to 9293 s).

The other 180 answers came from 6 trade tapes (2 Kalshi and 4 Limitless markets, all 30 requests each),
answered with no trade tape (the second window, which records messages, shows "no live tape for this leg"): the market had had no trade since our tape began. Checked against the
venues' public trade history afterwards, the 2 Kalshi markets' newest trades were 8 and 10 days before the
run, 2 of the Limitless markets' 66 and 227 days before, and the other 2 Limitless markets list no trades.

There was no 429, no 500 and no failed connection. In the second window, 91 of 475 answers were 503: 54 "no
live tape for this leg" (3 Limitless tapes), 36 for 2 Limitless books past the 3600 s bound, and 1 "no live book" for a
Kalshi book whose other 17 answers were served.

## Earlier run (2026-10-20, without a key)

A run made the evening before (21:10 to 21:37 UTC, same machine, no key) measured only routes that need no
key. Warm `GET /public/v1/stream/protocol` took 201 ms at the median (n = 147). A stream handshake without
a key was refused with 401 unauthorized in the error envelope every time (n = 5).

## Caveats

* One vantage point, far from our servers. A client close to them sees lower numbers; we have not measured
  that yet.
* The measuring machine was under load (see above), which widens the tails a little.
* A small sample of markets (12 legs, then 9 more) and one stream session.
* The free plan only. No load test was run: this page measures one client at a time, not capacity.
* Trading is not live, so order-placement latency is not measured.

## Reproduce a figure yourself

Time any route with curl from your own machine; run it a few times so the connection is reused:

```sh theme={null}
curl -sS -o /dev/null \
  -w 'connect %{time_connect}s  tls %{time_appconnect}s  ttfb %{time_starttransfer}s  total %{time_total}s\n' \
  -H "Authorization: Bearer $TRENDLINE_API_KEY" \
  "https://systems.trendlinelabs.ai/public/v1/key"
```

Every order book and list answer carries its own freshness (`age_ms`, `universe_age_s`, `age_s`), so you
can measure the data's age as served from your own requests: see [Freshness](/guides/freshness).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.