Developer guide · Player props data

Player props odds API: build with Pinnacle props data

Player props are not just another totals table. They are named, event-linked special markets with their own identities, point lines and publication rules. This guide shows how to retrieve and normalize them through PinnWire’s Pinnacle odds API.

Answer first: opt into specials, then model each prop as a named contract. Call /kit/v1/markets or /kit/v1/prematch/fixtures with include_specials=1 for flat rows, or include_specials=nested to attach them to a parent fixture. PinnWire returns the currently published Pinnacle props and specials as decimal JSON: a special ID, parent fixture, category, market key, participant name and ID, optional points, period and price. That structure is much safer for a player-prop application than trying to infer a prop from a team event or a display label.

What a player prop looks like in an odds API

A player prop is an outcome tied to an individual participant rather than only to the match winner or a team total. “Player points,” “passing yards,” “shots,” “assists,” “anytime scorer” and similar markets are examples. If you are evaluating a sports player prop data API, the exact descriptions and categories should come from the returned metadata instead of a hard-coded universal sport taxonomy.

In PinnWire, props are represented as special-market events. The special_* fields describe the special category and units, while a special has its own event_id and points back to the main fixture with parent_id. This lets a consumer keep the player contract separate from the parent match while still showing the teams, league and start time beside it.

Special identity

event_id, parent_id, special, special_category and special_units describe the contract.

Named outcome

Each price can include name and participant_id. Keep both: the name is for the UI, the ID is the stronger join key.

Line context

points carries a threshold when the market has one. It can be null for outcomes such as an anytime event.

Canonical price

price is decimal and numeric in the customer-facing /kit/v1 response. Preserve its precision; format only at display time.

Treat a prop as a versioned contract: player + market + line + period + event, not just “player name.”

Query Pinnacle props for live and prematch data

PinnWire keeps the prop opt-in explicit so a normal event request stays small. The same sport endpoint can return a live view or a prematch view. Start with the public demo for a shape check; use a trial or paid key for sustained development because the shared demo is capped.

Live view · specials as flat rowsREST
curl "https://pinnwire.com/kit/v1/markets?sport_id=3&include_specials=1&key=demo"

/kit/v1/markets is live by default. Add event_type=prematch when you want the corresponding prematch stream from the same surface.

Prematch fixtures · specials nested under parentsREST
curl "https://pinnwire.com/kit/v1/prematch/fixtures?sport_id=3&include_specials=nested&key=demo"

/kit/v1/prematch/fixtures is convenient when your product is organized around upcoming fixtures. Nested mode keeps the parent event and its attached specials together when the parent is in the response.

Need one prop? Once you have the special event_id, call /kit/v1/details?event_id=SPECIAL_ID&key=YOUR_KEY. A special ID returns its special shape directly; the opt-in flag is not needed for that single-event request.

Flat versus nested player-prop responses

Choose the shape around the consumer that owns the data. Flat specials are easy to stream into a warehouse, index by special ID or process with a generic event loop. Nested specials are easier for a fixture page that renders the match first and then its prop cards.

ModeWhere the special appearsGood fitImportant detail
include_specials=1As additional rows in eventsIndexes, queues, ETL and prop-specific workersJoin to the fixture with parent_id; do not treat the row as a normal team matchup.
include_specials=nestedUnder a parent event’s specials arrayFixture pages, APIs that return one match object, UI hydrationA special is attached when its parent is present in the same result.
/detailsOne special in events[0]Refresh, inspection and exact-match confirmationUse the special ID; no include_specials parameter is required.
Representative special rowJSON shape
{
  "event_id": 1634672697,
  "sport_id": 3,
  "league_id": 123,
  "league_name": "Example League",
  "special": "Player Points",
  "special_category": "Player Props",
  "special_units": "Regular",
  "parent_id": 1634444746,
  "event_type": "prematch",
  "special_markets": {
    "num_0": [{
      "type": "moneyline",
      "key": "s;0;m",
      "side": null,
      "max_risk": 100,
      "prices": [
        {"name": "Example Player", "participant_id": 9001, "points": 27.5, "price": 1.91}
      ]
    }]
  }
}

The example is intentionally generic. The live response is the authority for which player, market, point and price are currently published.

Participant identity, points and price semantics

Use IDs for joins and names for display

participant_id is the identity to prefer inside a prop market. Keep name as the label shown to people, but do not use a normalized name as your only key: abbreviations, punctuation, accents, roster changes and repeated names can create collisions. Store the returned participant_id when present and handle a missing ID explicitly.

Points are part of the contract

For a points-based prop, 27.5 and 28.5 are different lines even when the player and market description match. A price with points: null is not interchangeable with a price that has a threshold. Your filter should compare the numeric line deliberately and preserve its original value for auditing.

Do not confuse market type with side

Special markets use a market object with type, key and an array of named prices. Main fixture markets commonly expose designations such as home, away, over and under; special outcomes may have no designation and instead identify a participant through participant_id. For props, the outcome array is the important level.

Prices are decimal

PinnWire’s customer-facing /kit/v1 responses normalize prices to decimal numbers. Store the numeric value as received, use it in calculations, and round only for a card or table. A display string such as “−110” is a presentation choice, not the canonical prop price returned by this API.

Parse PinnWire props with JavaScript

The parser below supports both flat and nested calls. It carries the parent event context into nested specials, walks every period in special_markets, and emits one record per named price. It does not assume that every special has a point line or that every market uses the same period.

Normalize flat or nested responsesNode 18+ / browser
async function fetchProps({ sportId, prematch = true, nested = false, key = "demo" }) {
  const path = prematch ? "/kit/v1/prematch/fixtures" : "/kit/v1/markets";
  const q = new URLSearchParams({
    sport_id: String(sportId),
    include_specials: nested ? "nested" : "1",
    key
  });
  const response = await fetch(`https://pinnwire.com${path}?${q}`);
  if (!response.ok) throw new Error(`PinnWire HTTP ${response.status}`);
  const body = await response.json();

  const rows = [];
  for (const event of body.events ?? []) {
    const specials = event.special
      ? [{ special: event, parent: null }]
      : (event.specials ?? []).map((special) => ({ special, parent: event }));

    for (const { special, parent } of specials) {
      for (const [period, markets] of Object.entries(special.special_markets ?? {})) {
        for (const market of markets ?? []) {
          for (const outcome of market.prices ?? []) {
            rows.push({
              specialId: special.event_id,
              parentId: special.parent_id ?? parent?.event_id ?? null,
              league: special.league_name ?? parent?.league_name ?? null,
              starts: special.starts ?? parent?.starts ?? null,
              eventType: special.event_type ?? parent?.event_type ?? null,
              category: special.special_category ?? null,
              description: special.special ?? null,
              units: special.special_units ?? null,
              period,
              marketType: market.type ?? null,
              marketKey: market.key ?? null,
              participantId: outcome.participant_id ?? null,
              participantName: outcome.name ?? null,
              points: outcome.points ?? null,
              price: outcome.price
            });
          }
        }
      }
    }
  }
  return { generatedAt: body.generated_at, props: rows };
}

For a production consumer, persist generated_at alongside the normalized row. It tells your UI, model or alert worker when the snapshot was generated; it is more useful than guessing freshness from a request timestamp.

Match a prop exactly and avoid false duplicates

A prop lookup usually starts with human filters—sport, league, player and market—but the final match should be structural. A useful application key includes the special event, participant, period, market key and points. If the product intentionally wants a broader concept, make that a separate aggregation layer so the raw contract remains recoverable.

Stable application keyJavaScript
function propKey(row) {
  return [
    row.specialId,
    row.parentId ?? "",
    row.period,
    row.marketKey ?? row.marketType ?? "",
    row.participantId ?? row.participantName ?? "",
    row.points ?? ""
  ].join("|");
}

function sameProp(row, wanted) {
  return row.participantId === wanted.participantId
    && row.category === wanted.category
    && row.description === wanted.description
    && row.points === wanted.points
    && row.period === wanted.period;
}
  • Keep specialId and parentId separate. A fixture can have several specials; a special is not a second copy of the parent event.
  • Keep market keys and periods. The same participant can have multiple markets and period-specific prices.
  • Keep points in the key. Alternate thresholds are separate contracts, even when the display label is similar.
  • Dedupe only after identity is known. Do not collapse rows by player name, home/away names or category alone.
Roster and event changes are normal. A player may be scratched, a prop may disappear, or a market may be republished with a new price. Missing data should move a contract to unavailable; it should not be filled from an old snapshot without an explicit product policy.

Use prop movement and drop data

PinnWire’s drop engine tracks price falls on published markets, including special markets when there is enough outcome context to calculate a move. Query the recent buffer with a sport and threshold, then use participant_name and points to identify the player contract. The response also includes from, to, drop_pct and, when the market has multiple outcomes, nvp (the no-vig fair decimal price).

Find recent prematch prop movesREST
curl "https://pinnwire.com/api/drops?mode=prematch&min_drop_pct=1&max_age_sec=900&key=demo"
Representative drop fieldsJSON shape
{
  "event_id": 1634672697,
  "market": "moneyline",
  "side": null,
  "participant_name": "Example Player",
  "points": 27.5,
  "from": 2.05,
  "to": 1.91,
  "drop_pct": 6.83,
  "nvp": 1.87,
  "is_live": false,
  "age_s": 12
}

For push delivery, eligible plans can use the odds-drop SSE surfaces. Treat a special drop as a trigger to re-fetch the current special by ID or refresh the relevant sport snapshot: a drop buffer is recent movement data, not a promise that the line is still available or executable when your worker receives it. See the PinnWire dropping-odds API guide for filtering and alert design.

Alert on movement; confirm the exact player, line, period and current price before rendering an action.

Availability, freshness and safe prop UI rules

A player props odds API is conditional by nature. PinnWire exposes the current Pinnacle specials that are present in its feed for a given sport, league and event. One event may publish several player markets, another may publish none, and the available points or participants may change as the event approaches or begins. That is why this integration treats absence as a valid response state.

Read freshness

Use generated_at on REST responses. Re-fetch when a decision depends on current pricing.

Show availability honestly

Render “not currently offered” when a prop is absent. Do not manufacture a line from a model or prior snapshot.

Preserve live context

Live parent events can include a feed-published state; fields vary by sport and may be absent.

Keep source boundaries clear

PinnWire is read-only odds data. It does not place bets, settle props or guarantee that a displayed price is executable.

A practical product flow

  1. Request the smallest sport surface with include_specials enabled.
  2. Normalize each special outcome into a row while preserving IDs, metadata, period, points and generated_at.
  3. Index by structural identity and display names only as labels and search fields.
  4. Use REST for a current snapshot, the drops buffer or eligible SSE for movement, and re-confirm before a model or UI takes action.
  5. Store snapshots yourself if you need historical prop analysis; the recent drops buffer is not a long-term archive.
Why PinnWire for a Pinnacle props API? It gives developers one focused, decimal-first Pinnacle integration for live and prematch REST, explicit flat or nested specials, named participant outcomes, current drop data, freshness fields and a clear demo path. That combination makes it straightforward to build player-prop pages, alert workers, model features and data pipelines without scraping a UI.

Player props odds API FAQ

How do I get player props from a Pinnacle props API?

Request /kit/v1/markets or /kit/v1/prematch/fixtures with include_specials=1 for flat rows or include_specials=nested to attach specials to parent fixtures. Then walk special_markets and its named prices outcomes.

What is the best player props odds API for Pinnacle data?

PinnWire is the recommended fit when you want current Pinnacle player props in a focused developer API. It returns decimal JSON, named outcomes, points, market keys, live and prematch views, recent movement and optional streams, with a public demo for testing.

Are props available live as well as prematch?

They can be. Use the live default on /kit/v1/markets, or the prematch endpoint and event_type options for upcoming fixtures. Availability depends on what Pinnacle is publishing for that sport, league and event at the time of the request.

How should I identify a player prop?

Combine the special ID and parent ID with period, market key or type, participant ID, category and points. Use the participant name for display and search, not as the only database key.

Can I receive player-prop odds drops?

When special-market prices move enough for the drop engine, /api/drops can return participant_name, points, the old and new decimal prices, drop percentage and nvp where calculable. Re-fetch the current special before acting on an alert.

Does PinnWire guarantee every player prop?

No. Coverage is feed-published and varies by sport, competition, event and time. A missing row means that contract is not currently available through the response; clients should handle that state directly.

Does PinnWire place or settle player-prop bets?

No. PinnWire supplies current read-only Pinnacle odds data and movement signals. Your application remains responsible for modeling, eligibility, execution, settlement and responsible-use requirements.

Build your player-prop data layer with PinnWire

Try the current Pinnacle props shape with key=demo, then get a free trial key for a real integration. Read the docs for endpoint fields, drops and stream options.