---
title: "Returns real-time PS3838 odds for a sport. An independent feed from /realtime: the two are never merged, so subscribe to whichever book you trade."
method: GET
path: "/api/v1/{sport}/ps3838-realtime"
tags: ["Real-time odds"]
---

# Returns real-time PS3838 odds for a sport. An independent feed from /realtime: the two are never merged, so subscribe to whichever book you trade.

`GET /api/v1/{sport}/ps3838-realtime`

## Request

- Base URL: `https://api.owlsinsight.com`
- URL: `https://api.owlsinsight.com/api/v1/{sport}/ps3838-realtime`
- Auth: HTTP bearer

Returns real-time PS3838 odds for a sport. An independent feed from /realtime: the two are never merged, so subscribe to whichever book you trade.

Prices are American odds (meta.odds_format).

meta.freshness.droppedAsFinished and droppedAsStale count events removed because they were confirmed finished or their start time is too far past to still be live.

meta.available is false and meta.freshness.stale is true whenever the response is empty.

Soccer women's and youth leagues: PS3838 names the clubs as in the senior league and puts Women or the age only in the league label. The team names here carry that marker (Liverpool Women, Uzbekistan U23), and so does every outcome label that names a team.

## Path parameters

- `sport` string, required

## Query parameters

- `league` string

## Response `200`

OK

- RealtimeResponse
  - `data` OddsEvent[]
    - `away_team` string
    - `away_team_id` string: Stable identifier for the team behind `away_team`; same rules as `home_team_id`.
    - `bookmakers` Bookmaker[]
      - `event_link` string: Deep link to event page on sportsbook website (REST only, stripped from WebSocket).
      - `event_ticker` string
      - `key` string
      - `last_update` string
      - `markets` Market[]
        - `category` string: Source's own market category (e.g. "Team Props", "Game Props").
        - `description` string: Source's own market description, where the book provides one.
        - `isAlternate` boolean: True when this entry is an alternate line rather than the main line.
        - `key` string: Market identifier. On the Pinnacle `/realtime` pipeline, markets folded from a sub-matchup are keyed `special_<matchupId>_<type>_p<period>`. ALTERNATE ladders additionally carry their LINE, because a key that resolves to several markets does not identify one: `special_1634192207_total_p0_alt_10.5` `special_1634192207_spread_p0_alt_-3` <-- CAN BE NEGATIVE A spread's line is stated from the home side, so `_alt_` may be followed by a minus sign; do not assume that segment parses as an unsigned number. Non-alternate keys (`special_<id>_total_p0`) and team-total keys (`special_<id>_team_total_p0_home_1.5`) are unchanged. A rung whose line MOVES changes key — a different line is a different rung. If you map markets by key, key each rung separately; keying only on the family collapses a ladder onto whichever rung you saw last.
        - `last_seen` string: When the book last CONFIRMED this market, as an ISO timestamp, whether or not the price moved. Pinnacle realtime markets only. Use this, not `last_update`, to judge whether a quote is still live: a quiet line the book keeps re-confirming has a fresh `last_seen` and an old `last_update`, so `last_update` alone cannot tell a quiet line from a pulled one. Compare a line against ITS OWN history, not against other lines on the same game. Lines on one match are confirmed at very different rates (the main line far more often than alternates or period lines), so a line that trails its siblings is normal. A line whose `last_seen` stops advancing when it normally keeps moving has stopped being quoted.
        - `last_update` string: When this market's price last CHANGED. It does not move when the book re-confirms an unchanged price.
        - `limits` object[]
          - `amount` number, double
          - `type` string
        - `outcomes` Outcome[]
          - `alternateLines` AlternateLine[]
            - `last_seen` string: When the book last CONFIRMED this rung, as an ISO timestamp, whether or not its price moved. Pinnacle realtime only (`getRealtime` and `pinnacle-realtime`); absent on `/odds` and on feeds that do not stamp rungs. Pinnacle does not resend every rung in every message, so a rung whose `last_seen` is well behind its market's `last_seen` is a hint that the rung has been taken down, not proof. Rungs are never flagged `suspended` one by one.
            - `point` number, double
            - `price` number, double
          - `deepLink` string: Ready-made URL that opens this outcome on the sportsbook, pre-built with a host that resolves the ids. Optional and absent for most books — read it defensively rather than assuming every outcome has one. What the link does differs by book. Read this before putting one behind a button labelled "add to betslip": - **FanDuel** — `nj.sportsbook.fanduel.com/addToBetslip`, adds the selection to the existing slip. The NJ host is required, not cosmetic: the underlying `marketId` namespace only resolves on that subdomain. Customers in other states can still read `marketId` + `selectionId`, but must not swap the host. - **DraftKings** — `sportsbook.draftkings.com/event/{eventId}?outcomes=...`, which opens the event with the outcome selected. Not region-locked at the ID level; DraftKings applies its own state gating once the slip opens. - **Hard Rock** — `share.hardrock.bet/Pt0T/landing?deep_link_value=…`, Hard Rock's AppsFlyer share host. On a phone with the app installed it opens the app on the selection; without the app it goes to the app store, and there is no mobile web fallback. On desktop the `af_web_dp` parameter is reported to send the visitor to `app.hardrock.bet` (inherited claim, not verified by us). **This one REPLACES the betslip.** Hard Rock's handler clears the existing slip before it resolves and adds the selection, so anything the customer had already built is discarded. Label it accordingly. A logged-out visitor is prompted to sign in and the selection lands after they do. - **Novig** — `novig.onelink.me/JHQQ?…&deep_link_value=…`, an AppsFlyer OneLink that PREFILLS a slip with this one side (no stake prescribed). Hands off to the Novig app when installed; there is no web autofill fallback. Novig is an exchange, so the price quoted is a peer's resting order — treat the click as an order to place, not a guaranteed fill. Only the props form of this link has been click-tested; game-line links come off the same builder but have not been. Books not listed here do not publish a link today.
          - `description` string
          - `liquidity` object: Order-book depth, on exchanges that expose a book (Novig today). ⚠️ `size` is in CENTS, the exchange's native unit — NOT dollars and not contracts. `size: 47691` is **$476.91**. Divide by 100 before showing it to anyone or sizing a bet against it; reading it as dollars overstates available depth by 100x. ⚠️ `price` here is AMERICAN ODDS, converted from Novig's native probability so this surface matches every other book in `/odds`. The player-props feed does NOT do this — `PlayerProp.liquidity` levels carry `price` as a raw probability (0–1) and size as `qty`/`qtyDollars`. Do not share a parsing path between the two. Levels are best price first, up to a 100-level safety bound (server-side, `NOVIG_LIQUIDITY_DEPTH`). Deepest observed to date is 57, so nothing is clipped today, but there is no flag telling you a ladder WAS clipped. Main markets were capped at 5 levels until 2026-08-21; player props never were, which is the asymmetry a customer reported. `asks` is empty in practice on Novig — its ladders are bids-only (measured across 2,659 outcomes), so an empty `asks` is the venue, not missing data. Attached to the PRIMARY line only, not to alternate lines. A deep ladder's tail can hold real but far-out-of-the-money resting orders (observed up to +99900 at ~0.1% implied) — read the tail as depth, not as "the price". Absent for traditional sportsbooks and for exchange outcomes with no ladder.
            - `asks` object[]: Always present, but empty on Novig — its ladders are bids-only.
              - …
            - `bids` object[]: Best bid first. `price` = American odds, `size` = CENTS.
              - …
          - `marketId` string: The sportsbook's own market identifier for the parent market, passed through verbatim. Use it to correlate an outcome with the book's public API, not to hand-build a link — `deepLink` is the supported way to link out. Present only when the book publishes one. Do not assume a book that carries `deepLink` also carries a usable `marketId`: Hard Rock's link format resolves a selection on its own and never consults a market id.
          - `name` string
          - `point` number, double
          - `price` number, double
          - `selectionId` unknown
        - `side` string: The source's own market-level side designation, passed through unmodified. Set on markets that price ONE competitor rather than both. The case it exists for is a team total, whose outcomes are named only `over` / `under`, so nothing inside the market says whose total is priced. Join it yourself: `side === 'home'` means the event's `home_team`, `side === 'away'` means its `away_team`. The API deliberately does not resolve the name, because the resolved string came from a different naming layer than `home_team` / `away_team` and failed to join. Set today only by the Pinnacle `/realtime` pipeline, on `special_*` markets.
        - `suspended` boolean: True when betting on this market is closed: the book closed it, and on the Pinnacle realtime feed it can also be set once the market's period is closed or its betting cutoff has passed. Absent otherwise, never `false`. On the Pinnacle realtime feed it is also set on a main market (moneyline, spread, total or team total) that Pinnacle withdraws while the rest of the match stays open, and cleared when Pinnacle offers it again or its period ends. On a spread or total only the main line is flagged, never its `alternateLines`. Its absence does NOT mean every line is being quoted: a withdrawn alternate rung, or a special or prop market, can arrive with no `suspended` at all. Use `last_seen` (on the market, and on each rung) to tell whether a quote is live.
        - `units` string: The source's own unit-of-measure for the sub-matchup this market came from, passed through unmodified. NOT AN ENUM — match it exactly and treat unknown values as unknown. Values seen in one soccer sample: `Regular`, `Corners`, `1st Goal`, `2nd Goal`, `CombinedResult`, `Place 1st`. Tennis team totals report `Games`. It varies by sport AND by market, so do not hard-code a whitelist you expect to be exhaustive. Matters most on `special_*` team totals: one soccer event can carry a goals team total AND a corners team total folded on from different sub-matchups, and `units` is what separates them. Absent means the source stated no unit; do NOT read absence as "goals".
      - `title` string
    - `commence_time` string
    - `country_code` string
    - `eventId` string
    - `freshness` EventFreshness: Per-event freshness on the realtime streams. Keyed off PRICE MOVEMENT (`lastUpdate`), not feed re-confirmation (`refreshedAt`), so a frozen-but-still- served live line reads `stale: true` — the market-maker pull signal.
      - `ageSeconds` number, double, nullable: Seconds since the price last moved — tradeability age.
      - `lastUpdate` number, double, nullable: Epoch ms this event's price last MOVED (drives `stale`/`ageSeconds`), or null.
      - `refreshedAgeSeconds` number, double, nullable: Seconds since the feed last confirmed this row.
      - `refreshedAt` number, double, nullable: Epoch ms the feed last CONFIRMED this row (moved or not) — liveness, not movement.
      - `stale` boolean: Live row whose price has not moved within the stale window — not tradeable.
    - `home_team` string
    - `home_team_id` string: Stable identifier for the team behind `home_team`, e.g. `"la-lakers"`. The same team carries the same id across every book and every spelling of the name, so you can join on it instead of on the string. The key is ABSENT (never `null`) when the name is not on a roster we carry: every tennis competitor, soccer and college names outside our rosters, and outright events, which never carry ids at all.
    - `id` string
    - `isLive` boolean: In-play flag on the realtime feeds. When `phase` is present: true while a live line for the match is started and not ended, meaning in-play betting on it is running. Without `phase`: the book's own flag for its live-betting offering, which does not mean "in play" and can be false on a match in progress, so check `status` as well. `freshness.stale` applies to an event that reads as in play.
    - `lastUpdate` number, double: Epoch ms of the newest price change on this event (realtime only).
    - `league` string
    - `market_type` string: `'outright'` for aggregated futures/progression markets (e.g. Pinnacle "To Reach The Final") that are NOT team-vs-team games. For these, the single `bookmakers[].markets[].outcomes` list holds the competitors (each with a price), `home_team` carries the market label, and `away_team` is empty. Absent for real games. Currently surfaced on the realtime (`/realtime`, `pinnacle-realtime`) soccer feed.
    - `phase` string: {@link OddsEvent.phase}. More values can be added: treat an unknown one like a missing field.
    - `seenAt` number, double: Epoch ms the feed last CONFIRMED a price on this event, changed or not (realtime only).
    - `sport` string
    - `sport_key` string
    - `sport_title` string
    - `status` string
  - `meta` RealtimeMeta
    - `available` boolean
    - `events` number, double
    - `feed` RealtimeFeedState: Why this response is empty, when it is. `live` — events were served. `unknown` — we cannot tell whether the feed is idle or broken. Deliberately only two states. Earlier drafts also had `idle` and `down`; both were removed after measurement. `down` was unreachable. `idle` keyed on the upstream reporting "connected, zero matchups", which it also reports when a live feed has frozen — so it could have claimed "nothing on" during an outage. Judge with `lastDataAgeSeconds`: a value in the hundreds of thousands means the feed has produced nothing for days.
      - `lastDataAgeSeconds` number, double, nullable: Seconds since this sport last produced ANY data, or `null` if it has not been observed producing data. `null` does NOT mean healthy, and does not mean zero.
      - `resubscribing` boolean, nullable: True while the feed is subscribing again after a reconnect, when some leagues can be silent. `null` means unknown, by the same rules as `upstreamAgeSeconds`. Absent from API versions that predate it.
      - `stalled` boolean, nullable: True when the board has an event in play (`phase: "live"`) and Pinnacle has sent nothing for this sport for longer than its normal quiet spells during play: prices can be out of date even though the board was rewritten seconds ago. False on a board with nothing in play. `null` when `upstreamAgeSeconds` is null or the events carry no `phase`. It does not change `freshness.stale`. Absent from API versions that predate it.
      - `state` string
      - `upstreamAgeSeconds` number, double, nullable: Seconds since Pinnacle last sent anything for this sport, as of this response. `lastDataAgeSeconds` stays low while Pinnacle is quiet, because the board is rewritten on a schedule either way; this keeps growing, so it is the field that shows a feed that is running but hearing nothing. `null` means unknown: always when `meta.source` is not `"pinnacle"`, and before the feed has received anything since it started. Absent from API versions that predate it.
    - `freshness` RealtimeFreshness: Freshness for the realtime endpoints, where `ageSeconds` can legitimately be unknown. Deliberately NOT a widening of {@link OddsFreshness}: `/odds` and `/events` share that type and customers do arithmetic directly on their `ageSeconds`, so loosening it there would force null-checks on consumers who do not need them today. `ageSeconds: null` means the age is genuinely unmeasurable — no source produced data and we have never observed this sport produce any. It replaced a reported `0`, which claimed the freshest possible data on a feed that had served nothing for four days. When null, `stale` is always `true`.
      - `ageSeconds` number, double, nullable: Seconds since this data was produced, or `null` when that is unknowable.
      - `droppedAsFinished` number, double: Events dropped because the match was confirmed finished (realtime only).
      - `droppedAsStale` number, double: Events dropped because their scheduled start is older than the sport's max plausible live duration — a source-independent staleness guard (realtime only).
      - `stale` boolean
    - `league` string
    - `source` string
    - `sport` string
    - `stream` RealtimeStreamPosition: A position on a realtime stream: `seq` counts frames for one sport within one `epoch`.
      - `epoch` string: Opaque id of one run of the stream. A new epoch means the stream restarted and its `seq` numbering started again: reload the board.
      - `seq` number, double: Goes up by 1 per frame for the sport within one epoch.
    - `timestamp` string: Absent only on an empty response for a sport we have never seen produce data.
  - `success` boolean

---

[API](https://skmtc.dev/owlsinsight/apis/owls-insight-api.md) · [All operations](https://skmtc.dev/owlsinsight/apis/owls-insight-api/llms.txt) · [OpenAPI document](https://skmtc.dev/owlsinsight/apis/owls-insight-api/revisions/76c1ea016ff1?raw)
