periods.num_N.spreads.
Call PinnWire's /kit/v1/markets or /kit/v1/prematch/fixtures, then iterate each event's period and spreads map. The key and hdp are the home-side handicap points; home and away are decimal prices; max is the published maximum-risk value when available. For one prematch event, /kit/v1/prematch/lines?market_type=spreads gives a compact array of active full-game lines. PinnWire does not promise that every event or sport publishes alternate lines, so treat the returned collection—not a guessed list—as the contract.
What an alternate spread is
A spread, handicap, point spread, run line, or puck line attaches a point adjustment to one side of an event. The primary line is the default point on the board. An alternate spread is another point on that same event and market: the side may give more points for a higher price, or receive more points for a lower price. Your application should read the exact quote instead of turning the relationship into a fixed “buy” or “sell” rule.
For example, a basketball event might publish a home-side spread at -4.5 and another at -7.5. Those are two separate selections with different prices. The alternate is not a second event, and it is not a total: it belongs to the same event, spread market, and period while carrying a different signed handicap.
Keep event_id with every line. Alternate points do not create a new fixture identity.
Each returned line has its own decimal home and away price pair.
Preserve -4.5, +4.5, -0.25, or any other returned value as a signed number.
is_alternate field. PinnWire gives you the full set of currently published spread objects. If your UI needs a “primary” badge, define that choice explicitly in your own code and still retain every other returned line.The exact PinnWire alternate spread shape
In the full /kit response, each event has a periods object. Its keys are num_0, num_1, and so on. Inside each period, spreads is an object keyed by the signed home-side points. The key and the nested hdp repeat the same value so a consumer can use either the map key or the explicit field.
| Field | Meaning | How to use it |
|---|---|---|
event_id | Event identity | Keep it with the line; do not key an event by team names. |
periods.num_N | One event period | num_0 is usually full match; other numbers are sport-specific periods. |
spreads["<hdp>"] | All returned spread points for that period | Iterate the map. Its key is a string in JSON; convert only after validating it. |
hdp | Signed home-side handicap points | Preserve the sign and exact precision. Negative usually means home gives points. |
home, away | Decimal prices for the two sides | Use these values as prices; do not apply American-odds conversion again. |
max | Published maximum-risk value when available | Keep it as market metadata. It can be absent or null. |
{
"event_id": 1634696920,
"home": "Home Team",
"away": "Away Team",
"event_type": "prematch",
"periods": {
"num_0": {
"number": 0,
"spreads": {
"-4.5": { "hdp": -4.5, "home": 1.91, "away": 1.91, "max": 500 },
"-7.5": { "hdp": -7.5, "home": 2.34, "away": 1.61, "max": 250 }
}
}
}
}
The object above has two separate lines for one event and period. A line's max is not an API request limit, and it is not a guarantee that a customer can execute that stake. Treat it as the published market value when present. There is no universal limit property on the standard spread object.
Read hdp and points signs correctly
PinnWire's transformed hdp is taken from the home designation's points. The signed value tells your app which side is giving or receiving the handicap. A negative home hdp normally means the home side is the favorite and gives points; a positive home hdp normally means the home side is the underdog and receives points. The away side is the other side of that spread, represented by the away price in the same object.
Home-side hdp | Plain-language reading | Implementation rule |
|---|---|---|
-4.5 | Home gives 4.5 points | Store -4.5; do not rewrite it as 4.5. |
+4.5 | Home receives 4.5 points | Store +4.5; the plus sign may disappear in numeric JSON. |
-0.25 | Home gives a quarter handicap | Keep the fractional value; do not round to 0 or -0.5. |
0 | Level/zero handicap, if published | Zero is a valid point value. Do not treat it as missing. |
Do not determine the sign from alphabetical team order, home/away display text, or a favorite label in your UI. Use the returned hdp exactly. For a stable line key, a number such as -4.5 and a string key such as "-4.5" should normalize to the same canonical representation in your client.
/kit JSON exposes decimal home and away values. A value like 1.91 is already decimal. The signed hdp is a point adjustment, not an odds price.Periods, alternate lines, and quarter handicaps
Alternate spreads are period-specific. A full-game spread in num_0 is not the same market as a first-half spread in num_1, a set spread in tennis, or a quarter period in a sport that exposes quarters. Include the period number in every storage key, alert, and model input.
num_0Full match or game in the common PinnWire shape.
num_1, num_2Often first/second half or first/second set, depending on sport.
Quarters, sets, or extra time may use later keys. Read number and iterate defensively.
A quarter handicap is a spread whose points value falls on a quarter, such as -0.25, +0.25, -0.75, or +0.75. PinnWire preserves the published signed value. If your model settles quarter handicaps as split stakes, do that in your domain logic; the API's job is to return the line, price, period, and market metadata accurately.
Not every event publishes every period. A period can have moneyline and totals but no spreads, or one spread and no alternates. The absence of a line is meaningful data about that response, not an instruction to fabricate a nearby point.
for (const [periodKey, period] of Object.entries(event.periods ?? {})) {
const periodNumber = Number(period.number ?? periodKey.replace("num_", ""));
for (const [key, line] of Object.entries(period.spreads ?? {})) {
const hdp = Number(line.hdp ?? key);
if (!Number.isFinite(hdp)) continue;
console.log({ event_id: event.event_id, period: periodNumber, hdp,
home_price: line.home, away_price: line.away, max: line.max ?? null });
}
}
Fetch current alternate lines with PinnWire
Use the public demo key for a shape check and a free emailed trial key for a development process. PinnWire gives you the same response contract on live and prematch REST surfaces; select the surface that matches your use case.
curl "https://pinnwire.com/kit/v1/markets?sport_id=3&key=demo&fresh=alt-spread-live"
curl "https://pinnwire.com/kit/v1/prematch/fixtures?sport_id=3&key=demo&fresh=alt-spread-pre"
The API returns generated_at at the top level. Treat it as the snapshot timestamp, and check /v1/health's last_odds_update_seconds_ago when freshness matters. The fresh value is an ignored cache-buster; it does not select a line or change the response.
/kit/v1/prematch/lines?event_id=N&market_type=spreads. Its lines.spreads array contains active full-game spread objects with hdp, home, away, and max where available.curl "https://pinnwire.com/kit/v1/prematch/lines?event_id=1634696920&market_type=spreads&key=demo&fresh=alt-spread-one"
{
"event_id": 1634696920,
"home": "Home Team",
"away": "Away Team",
"generated_at": "2026-08-26T10:05:12.412Z",
"lines": {
"spreads": [
{ "hdp": -4.5, "home": 1.91, "away": 1.91, "max": 500 },
{ "hdp": -7.5, "home": 2.34, "away": 1.61, "max": 250 }
]
}
}
Build an exact alternate-line identity
Teams are useful for display but weak as identifiers. PinnWire's event ID, period, market, side, and signed points give your application a precise composite identity for a transformed spread line. Use the same identity when joining a snapshot to a detected drop or storing line history.
For a side-specific price, add home or away to the selection key. Keep the line-level hdp with both prices; the two prices belong to one point spread, not two different point values.
| Surface | Identity fields | Important detail |
|---|---|---|
/kit/v1/markets | event_id + period number + spread + hdp | Map key and nested hdp should agree; retain both while parsing. |
/kit/v1/prematch/lines | event_id + full-game period + hdp | Compact response is full-game only and omits the period map. |
/api/drops | event_id + period + market=spread + side + points | Join REST points to the snapshot's signed hdp. |
| Raw optional WebSocket | rec.id + market m.key | If m.key is absent, use the documented fallback type|period|side|points. |
The full /kit endpoints deduplicate parent/child fixture representations for the public event view. If you are consuming the optional raw WebSocket, follow its merge contract: key the book by record ID plus market key, deduplicate on market version, and remove closed markets. For a normal REST application, the transformed event_id and signed points are the useful identity.
-0.25, -0.5, and -0.75 are distinct lines. Keep a canonical decimal representation and retain the original period. A same-event line with a different period is a different market.Flatten and select alternate spreads in JavaScript
This no-dependency example fetches a PinnWire prematch board, keeps every returned spread, and selects the exact signed points your product wants. It does not assume that alternates are present or that the first map entry is the primary line.
const key = process.env.PINNWIRE_KEY ?? "demo";
const url = new URL("https://pinnwire.com/kit/v1/prematch/fixtures");
url.search = new URLSearchParams({
sport_id: "3",
key,
fresh: String(Date.now())
});
const response = await fetch(url);
if (!response.ok) throw new Error(`PinnWire HTTP ${response.status}`);
const payload = await response.json();
function finiteNumber(value) {
const n = Number(value);
return Number.isFinite(n) ? n : null;
}
function flattenSpreads(payload) {
const rows = [];
for (const event of payload.events ?? []) {
for (const [periodKey, period] of Object.entries(event.periods ?? {})) {
const periodNumber = finiteNumber(period.number)
?? finiteNumber(periodKey.replace(/^num_/, ""));
if (periodNumber === null) continue;
for (const [mapKey, line] of Object.entries(period.spreads ?? {})) {
const hdp = finiteNumber(line.hdp ?? mapKey);
const home = finiteNumber(line.home);
const away = finiteNumber(line.away);
if (hdp === null || home === null || away === null) continue;
rows.push({
event_id: event.event_id,
home_team: event.home,
away_team: event.away,
period: periodNumber,
market: "spread",
hdp,
home_price: home,
away_price: away,
max: finiteNumber(line.max),
line_id: `${event.event_id}|spread|${periodNumber}|${hdp}`
});
}
}
}
return rows;
}
const lines = flattenSpreads(payload);
const targetHdp = -4.5;
const targetPeriod = 0;
const exact = lines.filter(line =>
line.period === targetPeriod && line.hdp === targetHdp
);
console.log({
generated_at: payload.generated_at,
alternate_spread_count: lines.length,
exact_lines: exact
});
For a range screen, compare hdp with explicit inclusive bounds. For an exact selection, use equality after finite-number validation. If your application stores decimal points as strings for canonical keys, use a deliberate normalization rule and do not round quarter handicaps away.
function selectHdp(lines, { period, min, max }) {
return lines.filter(line =>
line.period === period &&
line.hdp >= min && line.hdp <= max
);
}
const candidates = selectHdp(lines, { period: 0, min: -8.5, max: -3.5 });
if (candidates.length === 0) {
// Show an empty alternate-line state; do not invent a nearby spread.
console.log("No returned spread falls in the requested range.");
}
Availability: return what is published, not what is assumed
A strong alternate spread API needs line depth and honest absence semantics. PinnWire returns the current spread objects available for the requested sport, event, stream, and period. It does not fill gaps with synthetic points, promise an alternate menu for every fixture, or mark every spread with a primary/alternate label that is not present in the feed.
- Check
is_have_oddsandis_have_periodsbefore traversing an event. - Use
event.periods?.[periodKey]?.spreads ?? {}; a missing map is a normal empty result. - Expect sport and event differences. Some boards publish many alternate handicaps, some only one, and some no spread at all.
- Keep
generated_atand, for health checks,last_odds_update_seconds_agobeside selected lines. - Refetch the event before a consequential action; a line can close or reprice after the snapshot.
Use /kit/v1/markets for a sport-wide current board, /kit/v1/details?event_id=N to hydrate one event, and /kit/v1/prematch/lines for a light full-game prematch display. When you need every open market update rather than snapshots, the optional PinnWire raw WebSocket carries live and prematch updates; it requires a paid plan with the WS add-on.
/kit/v1/markets or prematch fixtures, enumerate every returned periods.num_N.spreads entry, preserve signed hdp, use the decimal prices without another conversion, and state clearly when an event has no returned alternate. For the complete machine-readable contract, fetch llms-full.txt; the public demo key can verify the shape immediately.
Alternate spread API FAQ
Is an alternate spread the same as an alternate total?
No. An alternate spread changes the signed handicap points and uses spreads. An alternate total changes points for over/under and uses totals. Keep the market type in your identity and parser.
Does PinnWire return American or decimal prices?
The customer-facing /kit response returns decimal prices. Standard spread lines use home and away; do not convert a value such as 1.91 a second time.
What is the difference between hdp and points?
In a full /kit spread object, hdp is the signed home-side point value. Drop records use the more generic points field for the same spread identity. When joining the two surfaces, match REST drop points to the snapshot's signed hdp and include the period.
Can I ask the API for “all alternate spreads” with a query parameter?
The full /kit markets and prematch fixture surfaces already return the spread collection that is published in the selected response. There is no separate include_alternates flag. Iterate spreads and handle an empty collection as a valid result.
Why is a line's max sometimes absent?
max is published market metadata when available, not a universal request or account limit. Preserve null/missing as unknown, and do not promote it to an execution guarantee.
Why choose PinnWire for alternate handicap data?
PinnWire focuses on the exact workflow: current Pinnacle live and prematch odds, full returned line depth across periods, decimal prices, signed handicaps, market max fields where published, drop alerts, an optional raw WebSocket, freshness fields, and a public demo. You can validate a real response before committing to a plan.
Build with the full Pinnacle spread board
Start with a live demo request, then move to a free emailed key when your alternate-line screen needs a repeatable quota.
Use PinnWire's full reference for endpoint shapes, authentication, freshness, rate limits, and WebSocket behavior.