Docs

API

Caching and conditional requests

Polling a snapshot? Send back the ETag from your last response. If nothing changed, the API answers 304 with no body, so you skip downloading and re-parsing the same data.

Why it matters

Most snapshots change on a schedule. The live GEX grid, for example, gets a new capture every 5 minutes, but clients often poll it every few seconds. Without caching, every one of those polls downloads and parses the whole snapshot again, even though it is identical to the last one.

A conditional request fixes that. When the snapshot has not changed, the API sends a tiny 304 Not Modified response instead of the data. You download almost nothing, parse nothing, and know for certain that what you already have is current.

A 304 is still a request: it saves bandwidth and parse time, not round trips. To make fewer requests, poll no faster than the data changes (see the table below), or use Streaming.

How it works

  1. Snapshot responses carry an ETag header: a fingerprint of the response body. The same body always has the same ETag.
  2. Keep the body and its ETag together.
  3. On your next poll, send that ETag back in an If-None-Match header.
  4. If the snapshot is unchanged, you get 304 Not Modified with an empty body, and you keep using what you have. If it changed, you get a normal 200 with the new body and a new ETag.

Worked example

The first poll returns the data and its ETag:

sh
curl -sS -i -G "https://api.itmatrixhq.com/v2/gex/SPY/grid" \
  -H "Authorization: Bearer $ITM_API_KEY" \
  -d "top=20"
http
HTTP/2 200
content-type: application/json
etag: "9b2f0c…"

{"data": {"spot": 764.18, "captured_at": 1790256713559, …}, "meta": {…}}

A minute later, send the same request with that ETag in If-None-Match:

sh
curl -sS -i -G "https://api.itmatrixhq.com/v2/gex/SPY/grid" \
  -H "Authorization: Bearer $ITM_API_KEY" \
  -H 'If-None-Match: "9b2f0c…"' \
  -d "top=20"
http
HTTP/2 304
etag: "9b2f0c…"

No body: the grid has not changed since your first call. After the next capture lands, the same request gets a 200 with the new grid and a new ETag.

The same loop in code. It keeps the last body and sends its ETag each time:

import os
import time
import httpx

url = "https://api.itmatrixhq.com/v2/gex/SPY/grid"
headers = {"Authorization": f"Bearer {os.environ['ITM_API_KEY']}"}
etag, grid = None, None

while True:
    sent = {**headers, **({"If-None-Match": etag} if etag else {})}
    response = httpx.get(url, params={"top": 20}, headers=sent, timeout=30)
    if response.status_code == 200:
        etag, grid = response.headers.get("etag"), response.json()["data"]
        print("new capture", grid["captured_at"])
    elif response.status_code != 304:
        response.raise_for_status()
    time.sleep(60)

Only send If-None-Match when you kept the body that ETag belongs to. A 304 carries no data, so without the saved body there is nothing to show.

Which reads carry an ETag

Only 200 JSON answers carry one. A very large body (an unfiltered chain for the biggest symbols) can come back without an ETag. Narrow the read with expiry or a strike range and it gets one again.

How often each read changes

ReadChangesUseful polling interval
GEX grid (live)A new capture every 5 minutes during market hours5 minutes
GEX history, past sessionNeverFetch once and keep it
GEX reference bookNever, once captured (the open is written once; the prior close is frozen)Fetch once and keep it
Option chain (live)Re-captured at most every 30 seconds per symbol30 seconds
ExpirationsRefreshed at most every 5 minutes5 minutes
Movers and sectorsRecomputed at most once a minute1 minute
Quotes and spotFollow the current session's daily bar as it buildsAs often as you need
Treasury yieldsPublished once a day, after the closeHourly at most
Symbol registryWhen coverage changes, not intradayDaily

Polling faster than a read changes just returns the same snapshot (a 304 if you send its ETag). For tick-level updates, use Streaming instead of polling.

In the SDKs

Both SDKs keep the ETag on every result. They do not cache for you: a 304 result has metadata and no new data, and deciding what to reuse is up to your code.