# Freshness fields

> The times the data carries, what each one means and how to compute a price's age.

## The board time

meta.processed_at, and the X-Processed-At header, are the board's time. X-OddsRelay-Version is the publish instant of the board version served, in epoch milliseconds, the same on every route for one publish.

## Each venue's last read

On the matched boards, meta.last_seen is {bookmaker key: {sport group: ISO time}}: the last instant each venue and sport was read, the oldest observation behind the board. Now minus `last_seen` is an upper bound on that venue's price age. It is keyed by the sport's key base (`soccer`, `horse_racing`, `american_football`), not an event's full `sport_key`: take the group /v2/sports gives each sport key (American Football) and write it lowercase with underscores. A filtered reply's table is cut to the venues and sports it returns.

Keyed /v2/coverage carries the same `last_seen` per venue and sport, with event counts.

A price's age, JavaScript:

```
// groupOf: {sport_key: key base}, built once from GET /v2/sports rows: base = group.toLowerCase().replace(/[^a-z0-9]+/g, "_")
const seen = meta.last_seen[offer.bookmaker]?.[groupOf[event.sport_key]];
const ageSeconds = seen ? (Date.now() - Date.parse(seen)) / 1000 : null;
```

## Raw's `last_update`

On raw, each market's `last_update` is when that venue's market last changed: a price, a lay price or available moved, or a row appeared. A service restart can move it once with nothing changed, so dedupe on content. It is not a heartbeat: a quiet market's `last_update` ages while the venue is still read.

## What is never cut

The API drops no row for age. A venue whose latest read is older than its own window, at most two minutes, is left off the boards until it is read again.

Source: https://oddsrelay.io/docs/guides/freshness · the API contract: https://api.oddsrelay.io/v2/openapi.json
