Premier League Odds API: Live Pinnacle EPL Lines

A Premier League odds API guide using Pinnacle data: live and prematch endpoints, quarter lines, specials, odds-drop SSE, and the caveats nobody mentions.
A Premier League odds API is any endpoint that hands you current prices for EPL fixtures — 1X2, Asian handicaps, totals, props — as JSON you can parse without scraping a page. If you want Pinnacle's prices specifically, you need a third-party feed now: Pinnacle closed its public API in July 2025. With pinnodds, one authenticated call to /kit/v1/markets?event_type=live returns every live soccer event, EPL included, and /kit/v1/prematch/fixtures covers the rest of the season's card. Auth is one header: x-api-key: YOUR_KEY.
That answers the search. The rest of this is the part that bites once you start building: isolating one league out of a global soccer feed, not losing the quarter-ball ladder, choosing push over polling, and knowing when a Pinnacle-only feed is the wrong tool for what you're doing.
Prematch and live are two different rhythms
People treat these as one feed with a flag on it, then wonder why their in-play numbers look like they were printed yesterday.
Prematch is the season-long card. Fixtures sit there for weeks, prices drift on team news and money, and you can walk the whole EPL schedule from /kit/v1/prematch/fixtures, then pull structure from /kit/v1/prematch/markets and individual price rows from /kit/v1/prematch/lines. Cadence here can be relaxed. A minute is fine for most models; a lot of Tuesday-afternoon prematch movement is noise anyway.
Live is a different animal. A goal in the 71st minute rewrites the entire totals ladder before your next poll fires. /kit/v1/markets?event_type=live gives you an honest snapshot, and it's the right call for a dashboard that refreshes on user action. But if you're sitting in a while True loop hitting it every two seconds for in-play work, you've already lost — you're sampling a stream. Open the WebSocket at /ws/feed and let updates arrive as Pinnacle publishes them.
For a season-long EPL project you want both, and they serve different jobs: a nightly prematch sweep to build your fixture universe and closing-line baseline, plus a live socket on match days.
Getting the EPL out of a global soccer feed
There is no /premier-league endpoint, and honestly, be suspicious of any provider that offers one. Leagues get renamed, sponsors change, tiers restructure, and hardcoded league routes rot within two seasons. You filter on the event row, cache the league identifier the first time you resolve it, and move on with your life.
import requests
BASE = "https://api.pinnodds.com"
HEADERS = {"x-api-key": "YOUR_KEY"}
def epl_live():
r = requests.get(
f"{BASE}/kit/v1/markets",
params={"event_type": "live"},
headers=HEADERS,
timeout=10,
)
r.raise_for_status()
events = r.json()
return [
e for e in events
if "premier league" in str(e.get("league", "")).lower()
and "england" in str(e.get("country", e.get("league", ""))).lower()
]
for ev in epl_live():
print(ev["id"], ev.get("home"), "v", ev.get("away"))
The country check isn't paranoia. "Premier League" matches Russia, Egypt, Belarus and a long tail of others, and if you ship a naive substring filter you will eventually alert someone about a Belarusian handicap at 3am. Resolve the match once at startup, store the league ID, filter on the ID afterwards. String comparison on every tick is CPU you're burning for nothing.
Or skip the plumbing. npm install pinnodds or pip install pinnodds gives you low-dependency wrappers over the same REST and SSE surface, including the retry and auth code you'd otherwise write badly at 2am the night before a deadline. Field-by-field shapes live in the docs.
Quarter totals and quarter handicaps: the part to verify before you buy
This is where a lot of "EPL odds JSON" providers quietly fall short, and it's the single thing I'd test on a trial key before committing.
Pinnacle prices Asian markets on a quarter-ball ladder. Soccer totals at 1.75, 2.25, 2.75, 3.25. Handicaps at -0.75, -1.25, -1.75, -2.25. A quarter line is a split bet — half your stake on 2.5, half on 3.0 — and a large share of serious soccer volume sits there rather than on the round numbers. If a feed rounds you to the nearest half goal, your model is measuring itself against a price nobody can actually take.
pinnodds passes through every line Pinnacle prices, quarter lines included, on every plan. Depth doesn't scale with your subscription. Plans differ on rate limit and push access, not on how much of the ladder you're allowed to see.
Two fields on each row deserve more attention than they usually get:
limit— Pinnacle's maximum stake on that selection. On a midweek EPL total this tells you more about how confident the book is than the price does. Limits climb toward kickoff and collapse during chaotic in-play sequences.nvp— the no-vig fair price. Use this, not the offered price, when you benchmark another book. Comparing a vigged price to a vigged price and calling the gap "edge" is the most common modelling error in this space, and it's an expensive one.
Specials: include_specials=1 and why it's off by default
Add include_specials=1 and you get player and team props, exact scores, futures and outrights as their own event rows. Each carries special, special_category, special_markets and a parent_id pointing back at the parent fixture, so joining them to your EPL match universe is one key lookup.
They're opt-in because they dominate the payload. Soccer prematch is roughly 1,500 events without the flag and roughly 12,400 with it. That's an order of magnitude more bytes for data most consumers scan once and throw away.
For a season-long EPL build I'd run the base fixture and market sync without specials on a tight loop, and pull specials on a separate slower job — once when the market opens, once a few hours before kickoff. Join on parent_id. Your ingest stays cheap and your props stay fresh enough for anything short of live prop trading.
Odds drops: the SSE payload and the REST row are not the same object
If your Premier League project is about catching movement rather than photographing prices, use the drop feeds. There are two, and the shapes genuinely differ — worth internalising before you write a parser that expects one and receives the other.
The SSE stream at /odds-drop (or /odds-drop-prematch) pushes an alert the moment a price falls against its own recent history. The payload names the market in sect, the moving selection in outcome, the prices in from_price and to_price, the event in id, and carries limit and nvp. What it does not carry is a drop percentage. Compute it yourself:
const es = new EventSource(
"https://api.pinnodds.com/odds-drop?key=YOUR_KEY"
);
es.onmessage = (e) => {
const d = JSON.parse(e.data);
const pct = ((d.from_price - d.to_price) / d.from_price) * 100;
console.log(
`${d.id} ${d.sect} ${d.outcome}: ${d.from_price} -> ${d.to_price}`,
`(${pct.toFixed(2)}%, limit ${d.limit}, nvp ${d.nvp})`
);
};
/api/drops?mode=live (or mode=prematch) is the enriched form of the same event: market, designation, from, to, event_id, plus a precomputed drop_pct. My rule of thumb — REST when you want a sorted recent-movement table for a dashboard, SSE when the alert only has value on arrival. Both are documented on /docs.
The honest caveats
It's Pinnacle only. If your product promises best-price comparison across forty bookmakers, this is the wrong feed and an aggregator is the right one. Pinnacle is a sharp reference price, not a market survey. Plenty of teams run both — pinnodds for the reference number, an aggregator for coverage — and that's a perfectly sane architecture.
There is no historical odds archive. You cannot query last season's Arsenal closing lines. If you need history for backtesting, closing-line value, or charting a market over weeks, you record it yourself starting today. Stand up a writer process against the WebSocket or a prematch cron before you build anything that depends on the past, because retroactively there is nothing to fetch.
Live and prematch are separate surfaces. The fixture's identity carries across, but you're calling different endpoints with different shapes, and your ingestion has to handle both. One call will not cover a match's full lifecycle.
SSE push is a plan feature. Pro at $99/mo covers the REST surface. SSE push sits on Pro + SSE at $149/mo and Scale at $229/mo, with quarterly and semi-annual options. If drop alerts are core to the product, budget for it up front — see /#pricing.
League filtering is your job. The feed is global soccer by design. That's the right call for a data provider, but it does mean a small amount of client-side work to isolate the EPL.
A season-long architecture that actually holds up
For a Premier League project running August to May, here's the shape I'd build:
- Nightly — sweep
/kit/v1/prematch/fixturesfor the next 14 days and upsert into your own fixtures table keyed by event ID. - Every few minutes — pull
/kit/v1/prematch/marketsand/kit/v1/prematch/linesfor those fixtures and append every observation to a time-series table. This is your closing-line archive. Nobody builds it for you. - Twice per fixture — a specials pass with
include_specials=1, joined onparent_id. - Match days —
/ws/feedopen for the duration, writing raw ticks before any parsing. Reconnect with backoff and treat a dropped socket as routine, not as an incident. - Optional —
/odds-dropSSE into a queue for alerting, withdrop_pctcomputed on your side.
Health-check the whole thing against /health and /ping so your on-call can tell "the feed is down" apart from "my parser threw on a field I didn't expect". Those two failures look identical in a dashboard and cost very different amounts of sleep.
Takeaway
Two things separate a working Premier League odds API integration from a weekend toy. Filter to the EPL on a cached league ID rather than a string comparison per tick. And store nvp next to the raw price from the very first row you write — fair price is what you compare against another book, the offered price only tells you what you'd pay. Since there's no historical archive to fall back on, the day you start logging is the day your backtest begins. Start logging before you start modelling.
Frequently asked questions
Is there still a public Pinnacle API for Premier League odds?
No. Pinnacle closed its public API in July 2025, so Pinnacle-sourced EPL prices now come from independent services. pinnodds carries the same market data over REST, WebSocket and SSE.
How do I get live Premier League odds as JSON?
Call GET /kit/v1/markets?event_type=live with an x-api-key header and filter the returned soccer events to England's Premier League. For in-play work, use the pushed WebSocket feed at /ws/feed instead of polling that endpoint in a loop.
Does the API include Asian quarter lines for EPL matches?
Yes. Every line Pinnacle prices comes through, including soccer totals at 1.75 / 2.25 / 2.75 / 3.25 and quarter-ball handicaps at -0.75 / -1.25 / -1.75 / -2.25. Line depth is identical on every plan.
Can I get Premier League player props and outrights?
Add include_specials=1 to your request. Props, exact scores, futures and outrights arrive as their own event rows with special, special_category, special_markets and a parent_id linking back to the fixture.
Is there historical Premier League odds data available?
No, there is no historical odds archive. If you need closing lines or long-run market charts, record the feed yourself from day one using the WebSocket or a prematch cron job.
How much does a Premier League odds API cost?
A free trial key takes seconds and needs no card. Paid plans start at $99/mo for Pro (REST), with Pro + SSE at $149/mo and Scale at $229/mo for push access.
Get real-time Pinnacle odds in your code
Live & prematch markets with instant odds-drop alerts over SSE and WebSocket. Free trial key in seconds — no card.
Start free trial