Pinnacle odds WebSocket in Swift
Answer: create a URLRequest for wss://pinnfeed.io/v1/stream, set the x-api-key header, open it with URLSession.shared.webSocketTask(with:), send a JSON subscribe, and run a receive loop that replies {"type":"pong"} to each ping (every 20 s). Use it on macOS or a server; do not put a key inside an iOS app. Builder or Pro key required.
main.swift (macOS command-line tool)#
import Foundation
let key = ProcessInfo.processInfo.environment["PINNFEED_KEY"]! // ps_live_...
var request = URLRequest(url: URL(string: "wss://pinnfeed.io/v1/stream")!)
request.setValue(key, forHTTPHeaderField: "x-api-key")
let task = URLSession.shared.webSocketTask(with: request)
task.maximumMessageSize = 32 * 1024 * 1024 // snapshot parts can be large
task.resume()
let sub = #"{"type":"subscribe","sports":["hockey"],"markets":["moneyline","total"],"main":true}"#
task.send(.string(sub)) { if let e = $0 { print("send error", e) } }
func receive() {
task.receive { result in
switch result {
case .success(.string(let text)):
if text.hasPrefix(#"{"type":"ping""#) {
task.send(.string(#"{"type":"pong"}"#)) { _ in }
} else {
print(text.prefix(160))
}
case .success(.data(let data)):
print("binary frame", data.count)
case .failure(let error):
print("closed:", task.closeCode.rawValue, error.localizedDescription)
return
@unknown default:
break
}
receive()
}
}
receive()
RunLoop.main.run()
Parse frames with JSONDecoder in real code; types follow the schema (snapshot, delta with changes[] of market/event upserts and removes).
Close codes#
4002 no_subscription (no subscribe in 30 s), 4008 slow_consumer (buffer over 16 MB), 4001 key_revoked. Reconnect with backoff and expect a fresh snapshot: reconnect.
Plans#
Builder (€99/mo, 3 connections) or Pro. 3-day trial; a card is required and it is charged after day 3 unless you cancel.
Educational content for adults (18+).
Questions people ask#
Why not call PinnFeed from the iPhone app?#
Anyone can extract a key from an app bundle. Run the stream on a server and expose your own API to the app: browser and app keys.
Why raise maximumMessageSize?#
The default limit is 1 MB and the first snapshot parts can be larger on broad subscriptions.
Vapor or SwiftNIO?#
Both have WebSocket clients that accept custom headers; the frames are identical.