---
title: "A player's shot chart and shot zones (NBA) or percentile profile (CoD)"
method: GET
path: "/api/v1/visuals/player"
tags: ["Visuals"]
---

# A player's shot chart and shot zones (NBA) or percentile profile (CoD)

`GET /api/v1/visuals/player`

## Request

- Base URL: `https://api.flashodds.live`
- URL: `https://api.flashodds.live/api/v1/visuals/player`
- Auth: HTTP bearer

Real shot locations (1 ft court cells, espn-halfcourt-v1) and deterministic zone splits for one player and season, with provenance and a status per capability (available, insufficient_sample, offseason, source_stale, source_unavailable, rights_blocked). A miss has its 2/3 value derived from location (the source records 0); the count is in shots.summary.valueFromGeometry. Attribution in provenance.attribution must be shown with any display. For cod, percentileProfile lists league and role percentiles, each with the named population it ranks against; a percentile that cannot be reproduced over a named pool is omitted.

## Query parameters

- `sport` string, required: Sport id. nba has spatial visuals; cod has a percentile profile; other sports answer with no capability.
- `slug` string, required: Canonical player slug (the same slug /archive/player uses).
- `season` integer: Season END year (2026 = 2025-26). Default: the newest published season.
- `lastN` integer: The player's last N games in the filtered view.
- `homeAway` 'home' | 'away'
- `opponent` string: Opponent team slug.
- `period` '1' | '2' | '3' | '4' | 'ot': Quarter 1-4, or ot for any overtime.
- `shotValue` '2' | '3'
- `result` 'made' | 'missed'
- `seasonType` 'regular' | 'postseason' | 'all': Default regular. postseason includes the play-in.

## Response `200`

Player visuals

- PlayerVisual
  - `found` boolean, required
  - `sport` string, required
  - `player` object, required
    - `id` string, nullable, required
    - `slug` string, required
    - `name` string, required
    - `team` string, nullable, required
    - `position` string, nullable, required
  - `filters` object, required
    - `season` integer, required
    - `lastN` integer, nullable, required
    - `homeAway` 'home' | 'away' | 'null', nullable, required
    - `opponent` string, nullable, required
    - `period` union, required
      - integer
      - 'ot'
    - `shotValue` union, required
      - 2
      - 3
    - `result` 'made' | 'missed' | 'null', nullable, required
  - `seasons` integer[], required
  - `filterOptions` object, nullable, required
    - `opponents` object[], required
      - `slug` string, required
      - `name` string, required
  - `capabilities` object[], required
    - `capability` 'trend' | 'distribution' | 'percentile_profile' | 'split_comparison' | 'projection_history' | 'outcome_history' | 'spatial_shots' | 'spatial_zones', required
    - `status` 'available' | 'insufficient_sample' | 'offseason' | 'source_stale' | 'source_unavailable' | 'rights_blocked' | 'unsupported', required
    - `reason` string, nullable, required
  - `provenance` object, required
    - `sources` string[], required
    - `rightsVersions` string[], required
    - `attribution` string[], required
    - `generatedAt` string, required
    - `observedThrough` string, nullable, required
    - `transformVersions` string[], required
  - `trends` unknown[], required
    - unknown
  - `distributions` unknown[], required
    - unknown
  - `splits` unknown[], required
    - unknown
  - `percentileProfile` VisualPercentileMetric[], required
    - `metric` string, required
    - `label` string, required
    - `value` number, required
    - `percentile` integer, required
    - `population` object, required: The named pool the percentile ranks against. Always present: no unnamed percentile claims.
      - `label` string, required
      - `size` integer, required
      - `definition` string, required
  - `shots` object, nullable, required
    - `courtTransformVersion` string, required
    - `geometryAgreement` number, required
    - `summary` object, required
      - `attempts` integer, required
      - `makes` integer, required
      - `threeAttempts` integer, required
      - `threeMakes` integer, required
      - `valueFromGeometry` integer, required
      - `games` integer, required
      - `excluded` object, required
    - `cells` object[], required
      - `x` number, required
      - `y` number, required
      - `attempts` integer, required
      - `makes` integer, required
      - `zone` 'restricted_area' | 'paint_non_ra' | 'midrange_left' | 'midrange_center' | 'midrange_right' | 'corner_3_left' | 'corner_3_right' | 'above_break_3_left' | 'above_break_3_center' | 'above_break_3_right' | 'backcourt', required
  - `zones` object[], nullable, required
    - `id` 'restricted_area' | 'paint_non_ra' | 'midrange_left' | 'midrange_center' | 'midrange_right' | 'corner_3_left' | 'corner_3_right' | 'above_break_3_left' | 'above_break_3_center' | 'above_break_3_right' | 'backcourt', required
    - `label` string, required
    - `attempts` integer, required
    - `makes` integer, required
    - `fieldGoalPct` number, nullable, required
    - `attemptFrequencyPct` number, nullable, required
    - `pointsPerAttempt` number, nullable, required
    - `sampleStrength` 'thin' | 'usable' | 'strong', required

## Other responses

- `400`: Invalid request
- `401`: Missing or invalid API key
- `429`: Rate limit or daily quota exceeded

---

[API](https://skmtc.dev/flashodds/apis/flash-props-api.md) · [All operations](https://skmtc.dev/flashodds/apis/flash-props-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flashodds/apis/flash-props-api/revisions/044161ffbfd8?raw)
