The envelope: data plus meta
Every JSON data endpoint returns an object with a data array and a meta object. meta.mode is live or demo, schema_version is 1.0, count is the number of rows on this page, limit is the page size you requested, and next_cursor is either a continuation token or null. meta.source_status and meta.observed_at describe the latest collection, not each row.
{
"data": [ /* Projection objects */ ],
"meta": {
"mode": "live",
"schema_version": "1.0",
"count": 100,
"limit": 100,
"next_cursor": "opaque-token-or-null",
"observed_at": "2026-01-01T00:00:00.000Z",
"source_status": "fresh | partial | stale | failed | not_configured",
"observation_age_seconds": 42,
"line_semantics": "provider_base_line",
"promotions_modeled": false,
"results_available": false,
"stale_record_count": 0,
"warnings": []
}
}The values above illustrate types only. line_semantics, promotions_modeled, and results_available are deliberate contract statements: the line is a base line, promotions are not modeled as effective offers, and no settlement data is exposed.
Projection fields
| Field | Type | How to use it |
|---|---|---|
| id | string | Stable projection identifier. Use it as your key and in /projections/{id}/history. |
| source_projection_id | string | Upstream identifier, kept for traceability. |
| player.name | string | Display name. Can name multiple players on combo markets. |
| player.id / team / position | string | null | May be unknown. Do not derive team from name. |
| league | string | League code as stored, for example NBA. |
| event.starts_at / opponent | string | null | Scheduled start and opponent when supplied. |
| market.stat / market.period | string | Market name and period. Both are needed to compare two lines. |
| line | number | Provider base line. Not a price or probability. |
| selections[] | array | One entry per side (more, less). See below. |
| status | active | unavailable | Last recorded state of the offer. |
| is_demo | boolean | true only on the fictional demo endpoints. |
Selections are per side
| Field | Type | Meaning |
|---|---|---|
| side | more | less | Which outcome the entry describes. |
| available | boolean | null | null means not established, not unavailable. |
| tier | standard | demon | goblin | unknown | Difficulty label for this side only. |
| payout_multiplier | number | null | Only an observed source multiplier; never synthesized. |
Filter with tier=goblin and you get offers where either side carries that tier. Inspect selections to see which side it is before labeling the offer in your interface.
Five timestamps, five questions
| Field | Answers |
|---|---|
| observed_at | When this service last confirmed the record. |
| first_seen_at | When this service first recorded the offer. Not an upstream creation time. |
| last_seen_at | Most recent confirmation; equals observed_at on current reads. |
| source_updated_at | Upstream update time when supplied; may be null. |
| source_board_time | Upstream board time when supplied; may be null. |
All timestamps are ISO 8601 in UTC. Store them as instants, not local strings, and keep observed_at next to any line you persist. If you substitute observed_at for a missing source_updated_at, record that you did so.
Optional source_context
Some records carry source_context, captured at observed_at. It can include event teams and status, a combo flag, reported promotional details, and source-reported popularity. promotion.eligibility is always unknown, and popularity.definition is source_reported_unverified. Treat this block as context for display, not as facts about a specific user’s offer or about current game state.
History and change objects
A history entry has id, projection_id, line, selections, status, observed_at, and source_updated_at. A change event has id, cursor, type (offer.new, offer.changed, offer.unavailable, offer.restored), projection_id, league, player_name, stat, previous_line, line, and observed_at. previous_line is null when there is no earlier recorded line, such as on offer.new.
Frequently asked questions
Which field should I use as a primary key?
Use id for projections. source_projection_id preserves the upstream identifier, and player names are not unique keys because one player can have many markets and periods.
What does null mean in a response?
Unknown or unavailable from the source. It is never a stand-in for zero, false, or a default payout.
References
Current API documentation ↗Machine-readable OpenAPI contract ↗JSON Schema: null type ↗PrizePicks API is an independent data service. It is not affiliated with or endorsed by PrizePicks. Observations, completeness, and freshness are separate claims; consult source status before relying on a stored line.