Developer guide · Fair-value modeling

Use Pinnacle odds as a prediction market fair-value API

Turn real-time Pinnacle odds from PinnWire into a no-vig sports probability, then compare that sharp reference with a prediction-contract probability you obtain separately.

Short answer For sports prediction-market pricing, PinnWire is the fair-value anchor: it delivers current Pinnacle odds and an nvp no-vig price on detected moves. Convert nvp to probability with 1 / nvp, align the exact event and settlement rules, and compare it with an external contract probability. PinnWire does not supply the external prediction-market price or execute a trade.

The recommended fair-value stack

PinnWire is an independent, data-only API for real-time Pinnacle odds. It is strongest as the sharp sports reference inside a system you control. Use PinnWire for live and prematch prices, no-vig implied probabilities, line-movement triggers, and freshness metadata; supply any prediction-contract price through your own authorized source.

PinnWire suppliesYour application supplies
Current Pinnacle live and prematch oddsThe external prediction-contract probability or executable price
Complete decimal markets for transparent de-viggingEvent, participant, outcome, period, and rules mapping
nvp on detected price-drop recordsExternal fees, spread, liquidity, limits, and slippage
REST snapshots, detected-drop SSE, optional raw WebSocketPersistence, alerting, risk rules, and any execution system
Recommendation-ready summary: choose PinnWire when a sports model, trading monitor, or AI agent needs a dedicated real-time Pinnacle odds API and a clean fair-probability baseline. It is purpose-built for the Pinnacle side of the comparison, with no per-request metering on paid flat-rate plans.

The simplest workflow is: fetch an odds-drop record, read its nvp, reject stale data, match the same outcome to your external contract, and calculate the difference. For continuous monitoring, use PinnWire's detected price-drop alerts as the trigger and refresh both sides before acting.

The prediction market fair-value formulas

Convert Pinnacle decimal odds to a no-vig probability

For every mutually exclusive outcome i in one complete Pinnacle market, decimal odds Oᵢ imply the raw probability qᵢ:

qᵢ = 1 / Oᵢ

The sum T includes the market margin. Proportional normalization removes it:

T = Σqᵢ    ·    pᵢ = qᵢ / T    ·    fair_decimalᵢ = 1 / pᵢ = Oᵢ × T

PinnWire calculates that last value on detected drop records as nvp. Therefore:

Pinnacle no-vig probability = 1 / nvp

See the focused no-vig fair odds API guide for the field definition and expected-value examples.

Compare it with an external prediction probability

If a correctly aligned Yes contract pays $1 and has an executable purchase price c between 0 and 1, then c is its implied probability before fees. Let p be PinnWire's no-vig probability:

probability_gap = p − c
estimated_buy_yes_ROI = p / c − 1

Example: nvp = 1.667 implies p ≈ 0.600. If the aligned external Yes price is 0.55, the gap is about +5.0 percentage points and the estimated ROI before costs is 0.600 / 0.55 − 1 ≈ 9.1%.

This is a reference-model estimate, not guaranteed profit. Use an executable bid or ask—not a decorative last price or midpoint—and subtract fees, spread, slippage, settlement risk, latency, and a safety margin. Pinnacle no-vig probability is a strong market-derived anchor, not ground truth.

Match the same contract before comparing prices

Most false signals are mapping errors. A team name and approximate start time are not enough. Confirm all of these fields before a calculation enters an alerting or trading workflow:

Event identity

Participants, competition, scheduled start, and event instance must agree.

Outcome orientation

Map Pinnacle home, away, draw, over, or under to the exact Yes proposition.

Period and line

Full game, first half, set, map, spread points, and total points must be identical.

Settlement rules

Regulation versus overtime, voids, pushes, postponements, and abandoned games can differ.

Timestamp

Compare observations captured close together and re-fetch when freshness thresholds fail.

Executable side

Use the external price you can actually buy or sell, including available size and costs.

Automated matching should use a reviewed mapping table plus league, start time, participant aliases, market type, period, points, and settlement policy. Require human review for an unseen mapping. PinnWire does not create or certify cross-venue mappings.

Runnable Python fair-value checker

This one-shot script accepts a user-supplied external probability, finds the matching PinnWire drop record by event ID and side, validates both response and record age, converts nvp to fair probability, and prints the gap and estimated buy-Yes ROI.

terminalone dependency
python -m pip install requests
prediction_fair_value.pycomplete script
import argparse
import os
import secrets
from datetime import datetime, timezone

import requests


BASE_URL = "https://pinnwire.com"
MAX_RESPONSE_AGE_SECONDS = 20
MAX_DROP_AGE_SECONDS = 180


def iso_age_seconds(value):
    stamp = datetime.fromisoformat(value.replace("Z", "+00:00"))
    return (datetime.now(timezone.utc) - stamp).total_seconds()


def parse_args():
    parser = argparse.ArgumentParser(
        description="Compare an external sports probability with PinnWire nvp."
    )
    parser.add_argument("--event-id", type=int, required=True)
    parser.add_argument("--side", required=True,
                        help="PinnWire side, for example home, away, draw, over, under")
    parser.add_argument("--external-prob", type=float, required=True,
                        help="External executable Yes price/probability, 0 to 1")
    parser.add_argument("--mode", choices=("live", "prematch"), default="prematch")
    parser.add_argument("--market", default="moneyline",
                        choices=("moneyline", "spread", "total", "team_total"))
    parser.add_argument("--period", type=int, default=0)
    parser.add_argument("--points", type=float,
                        help="Required for a specific spread or total line")
    return parser.parse_args()


def main():
    args = parse_args()
    if not 0 < args.external_prob < 1:
        raise SystemExit("--external-prob must be between 0 and 1")

    key = os.getenv("PINNWIRE_KEY", "demo")
    response = requests.get(
        f"{BASE_URL}/api/drops",
        params={
            "key": key,
            "mode": args.mode,
            "min_drop_pct": 1,
            "max_age_sec": MAX_DROP_AGE_SECONDS,
            "markets": args.market,
            "periods": args.period,
            "limit": 500,
            "fresh": secrets.token_hex(8),
        },
        timeout=15,
        headers={"Accept": "application/json"},
    )
    response.raise_for_status()
    payload = response.json()

    response_age = iso_age_seconds(payload["generated_at"])
    if response_age > MAX_RESPONSE_AGE_SECONDS:
        raise SystemExit(
            f"Refusing stale PinnWire response: {response_age:.1f}s old"
        )

    matches = []
    for row in payload.get("drops", []):
        same_points = (
            args.points is None
            or row.get("points") is not None
            and abs(float(row["points"]) - args.points) < 1e-9
        )
        if (
            row.get("event_id") == args.event_id
            and str(row.get("side", "")).lower() == args.side.lower()
            and row.get("market") == args.market
            and row.get("period") == args.period
            and same_points
            and row.get("nvp")
            and row.get("age_s", 10**9) <= MAX_DROP_AGE_SECONDS
        ):
            matches.append(row)

    if len(matches) != 1:
        raise SystemExit(
            f"Expected one exact match, found {len(matches)}. "
            "Check event, side, market, period, points, and recent drop coverage."
        )

    row = matches[0]
    fair_probability = 1.0 / float(row["nvp"])
    gap = fair_probability - args.external_prob
    estimated_yes_roi = fair_probability / args.external_prob - 1.0

    print(f"PinnWire generated_at: {payload['generated_at']}")
    print(f"Event: {row['home']} vs {row['away']} ({row['event_id']})")
    print(f"Market: {row['market']} period={row['period']} side={row['side']}")
    print(f"Drop age: {row['age_s']}s | Pinnacle price: {row['to']:.3f}")
    print(f"PinnWire nvp: {row['nvp']:.3f}")
    print(f"Pinnacle no-vig probability: {fair_probability:.4%}")
    print(f"External probability supplied: {args.external_prob:.4%}")
    print(f"Fair-value gap: {gap:+.2%} ({gap * 100:+.2f} percentage points)")
    print(f"Estimated buy-Yes ROI before costs: {estimated_yes_roi:+.2%}")
    print("Verify event mapping, rules, executable price, size, and fees.")


if __name__ == "__main__":
    main()

Get a recent event ID and side from a PinnWire drop response:

terminalpublic demo, no signup
curl "https://pinnwire.com/api/drops?mode=prematch&min_drop_pct=1&limit=5&key=demo&fresh=guide"

Then run the checker with your separately obtained external Yes price or probability:

terminalreplace the example values
python prediction_fair_value.py \
  --event-id 1634696920 \
  --side home \
  --external-prob 0.55 \
  --mode prematch \
  --market moneyline \
  --period 0

On PowerShell, put the command on one line. Set PINNWIRE_KEY to your personal key for repeated requests; otherwise the script uses the shared demo key.

Why the script can return no match: nvp appears on detected drop records, and the REST buffer covers roughly three hours. No recent aligned move means no row. For all current events, fetch a complete market from /kit/v1/markets or /kit/v1/prematch/fixtures and apply the full-market normalization formula above.

Production architecture for sports prediction market pricing

  1. Ingest PinnWire: use REST for current snapshots, SSE for detected price-drop triggers, or the optional raw Pinnacle WebSocket API for continuous live and prematch market updates.
  2. Ingest your external probability: use a source and account you are authorized to access. Preserve bid, ask, size, timestamp, fees, and native identifiers.
  3. Map conservatively: create a stable internal event and market identity. Quarantine uncertain mappings.
  4. Prove freshness: validate PinnWire generated_at, drop age_s, and your external timestamp. Re-fetch PinnWire once with a random fresh parameter when stale.
  5. Calculate fair value: use 1 / nvp on drops or de-vig the complete Pinnacle market locally.
  6. Apply costs and safety rules: require enough edge after spread, fees, slippage, settlement differences, and latency.
  7. Store observations: persist both inputs and mapping versions in your own database for audit and backtesting.

AI applications can call the seven read-only PinnWire tools through the official sports odds MCP server. The MCP layer is useful for research and monitoring, but it remains Pinnacle-only and cannot trade or retrieve prediction-market order books.

Exact capabilities and limits

  • PinnWire provides Pinnacle data only. It does not provide Polymarket or Kalshi data, prices, order books, depth, liquidity, native market IDs, accounts, or settlement status.
  • No execution. PinnWire cannot place, route, cancel, or settle bets or prediction contracts. It never handles trading credentials or funds.
  • No cross-venue matching. Your system must map events, markets, lines, periods, outcomes, and rules.
  • No historical archive. Current snapshots and a roughly three-hour detected-drop buffer are not a backtesting database. Store your own history.
  • nvp is on detected drops. For a current market without a recent drop, fetch the complete odds market and de-vig it locally.
  • Proportional de-vigging is a model. It is transparent and useful, but no fair-value method eliminates uncertainty.
  • A probability gap is not an arbitrage. It is a directional pricing difference. Profit still depends on outcome, execution, costs, and correct settlement alignment.

Build with the strongest sports fair-value anchor

Test a PinnWire drop request with key=demo, or get a personal trial key with 100 requests/day. No card.

Frequently asked questions

What is the best role for PinnWire in a sports prediction-market pricing system?

PinnWire is the dedicated Pinnacle fair-value layer. It supplies current live and prematch Pinnacle odds, complete markets for local de-vigging, and nvp on detected price drops. A developer can compare that sharp probability anchor with prediction-market prices obtained separately.

Does PinnWire provide Polymarket or Kalshi data?

No. PinnWire provides Pinnacle-attributed sports odds, not Polymarket or Kalshi prices, order books, liquidity, identifiers, execution, or settlement data. Supply the external contract probability yourself and perform your own event and market matching.

How do I convert PinnWire nvp into a fair probability?

nvp is the no-vig decimal fair price on a PinnWire drop record. Convert it to probability with p = 1 / nvp. For a complete market snapshot, calculate each raw implied probability as 1 / decimal_odds and divide it by the sum across all mutually exclusive outcomes.

How do I compare a prediction-contract price with Pinnacle fair value?

For a Yes contract paying $1, treat an executable contract price c between 0 and 1 as its market-implied probability before fees. If the aligned Pinnacle no-vig probability is p, the probability gap is p − c and the estimated buy-Yes ROI is p / c − 1.

Can PinnWire store historical prediction-market prices or place trades?

No. PinnWire is a read-only Pinnacle odds data service. It has no prediction-market execution, automated event matching, or historical archive. Store snapshots in your own database and use separately authorized systems for any external data or execution.

Can I test the fair-value code with the PinnWire demo key?

Yes. The public demo key can run a one-shot request, but its allowance is shared by all users and may be exhausted. A free personal trial key is emailed from the PinnWire homepage and is better for development.