From request to response.
Standard HTTP. Versioned JSON. Enough context to understand what every observation means.
Your first request
Start with the public sample endpoint. It uses fictional players and deterministic observations, requires no key, and does not consume your workspace allowance.
curl "https://prizepicksapi.com/api/v1/demo/projections?league=NBA&limit=5"
Explore the response in the interactive playground. Sample responses include is_demo: true and meta.mode: "demo". They are never substituted into live endpoints.
Authentication
Create a workspace, then generate a key in API keys. Send it in the Authorization header. Store keys server-side; the full value is revealed once.
curl "https://prizepicksapi.com/api/v1/projections?league=NBA" \ -H "Authorization: Bearer YOUR_API_KEY"
Revoking a key prevents subsequent authenticated requests. All keys in a workspace share the plan’s request allowance.
The API surface
Production base path: /api/v1. Public samples use /api/v1/demo.
/projectionsCurrent observations, filterable by league, market, player, tier and status.
/projections/{id}/historyRetained observations for a projection, with line, status and timestamp.
/changesCursor-based stream of new, changed, unavailable and restored offers.
/coverageDeclared league and market coverage, timestamps, and source status.
/exportA bounded CSV export of matching projection records.
Filters & pagination
| Parameter | Purpose | Example |
|---|---|---|
| league | League identifier | NBA |
| search | Player or team search | Jordan |
| stat | Stat market name | Points |
| tier | Selection difficulty tier | standard |
| status | Availability status | active |
| limit | Maximum records in this page | 20 |
| cursor | Opaque continuation token | meta.next_cursor |
When meta.next_cursor is non-null, pass it as cursor with the same filters. Treat cursors as opaque values. Don’t infer a complete board from a single page.
Read the data correctly
| Field | Meaning |
|---|---|
| observed_at | When this observation was collected. |
| source_updated_at | Upstream timestamp when supplied and parseable; may be null. |
| first_seen_at | Earliest observation retained here, not necessarily the opening offer. |
| selections[] | Side-level availability and tier. More and Less may differ. |
| payout_multiplier | Only populated when actually available; null is not zero. |
| meta.source_status | Source-health context. A successful HTTP response alone is not a freshness guarantee. |
Errors & limits
Errors return JSON containing an error object with a machine-readable code and human-readable message.
{
"error": {
"code": "unauthorized",
"message": "A valid API key is required."
}
}| 400 | Invalid parameters or cursor. |
| 401 | Missing, invalid, or revoked API key. |
| 403 | The requested operation is not permitted. |
| 429 | Request rate or monthly allowance exceeded. |
| 503 | A required service is temporarily unavailable. |
Check workspace usage for the current allowance. Use retry backoff, respect rate-limit headers, and avoid refreshing a full board independently for every end user.
Change notifications
Register a public HTTPS endpoint in your webhook settings. Subscribe to offer events and store the signing secret shown at creation.
Deliveries originate from live ingestion. Sample playground requests do not emit real webhook messages. Use the delivery log to inspect attempts, and make your consumer idempotent.
offer.new offer.changed offer.unavailable offer.restored
Each delivery includes X-Webhook-Id, X-Webhook-Timestamp, and X-Webhook-Signature. Verify the signature against the exact raw request body before parsing JSON.
expected = "v1=" + HMAC_SHA256_HEX( signing_secret, timestamp + "." + raw_body )
Use a constant-time comparison and reject timestamps more than five minutes from your server’s clock. Deduplicate using the webhook event ID so retries cannot apply the same change twice. The sender retries failed deliveries up to five attempts.
For the full endpoint contract, download the OpenAPI specification .