{
  "openapi": "3.1.0",
  "info": {
    "title": "PFF Developer API (SAMPLE)",
    "version": "0.0.0-sample",
    "x-sample": true,
    "description": "**This is a SAMPLE specification** used to build the developer portal's reference UI before the real API ships. Endpoint shapes are illustrative \u2014 the real spec is published by the API team and replaces this file automatically at build time.\n\nThe PFF Developer API gives PFF Pro subscribers programmatic access to player grades and premium stats. Authenticate with your PFF account via the Restish CLI (see the [guide](https://developer.pff.com/guide/)); all data endpoints require an active Pro subscription.",
    "contact": {
      "name": "PFF Developer Portal",
      "url": "https://developer.pff.com"
    },
    "termsOfService": "https://www.pff.com/terms-of-service"
  },
  "servers": [
    {
      "url": "https://api.pff.com"
    }
  ],
  "security": [
    {
      "clerkAuth": []
    }
  ],
  "tags": [
    {
      "name": "Grades",
      "description": "PFF player grades"
    },
    {
      "name": "Stats",
      "description": "Premium stats facets"
    },
    {
      "name": "Players",
      "description": "Player lookup and search"
    },
    {
      "name": "Meta",
      "description": "Service metadata"
    }
  ],
  "paths": {
    "/v1/seasons/{season}/grades": {
      "get": {
        "tags": [
          "Grades"
        ],
        "operationId": "list-season-grades",
        "summary": "List player grades for a season",
        "description": "Season-level PFF grades, filterable by position, team, and week. Paginated.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Season"
          },
          {
            "name": "position",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "QB",
                "RB",
                "WR",
                "TE",
                "T",
                "G",
                "C",
                "DI",
                "ED",
                "LB",
                "CB",
                "S"
              ]
            }
          },
          {
            "name": "team",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Team abbreviation, e.g. KC"
          },
          {
            "name": "week",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of player grades",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlayerGradePage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ProRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/players/{playerId}/grades": {
      "get": {
        "tags": [
          "Grades"
        ],
        "operationId": "get-player-grades",
        "summary": "Get a player's grades",
        "description": "Grade history for one player across a season, by week.",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlayerId"
          },
          {
            "$ref": "#/components/parameters/SeasonQuery"
          }
        ],
        "responses": {
          "200": {
            "description": "The player's grades",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "player",
                    "grades"
                  ],
                  "properties": {
                    "player": {
                      "$ref": "#/components/schemas/Player"
                    },
                    "grades": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/PlayerGrade"
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ProRequired"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/seasons/{season}/stats/{facet}": {
      "get": {
        "tags": [
          "Stats"
        ],
        "operationId": "list-season-stats",
        "summary": "List premium stats for a facet",
        "description": "Premium stats by facet (passing, rushing, receiving, defense). Paginated.",
        "parameters": [
          {
            "$ref": "#/components/parameters/Season"
          },
          {
            "name": "facet",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "enum": [
                "passing",
                "rushing",
                "receiving",
                "defense"
              ]
            }
          },
          {
            "name": "week",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          },
          {
            "$ref": "#/components/parameters/Cursor"
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "A page of stat lines",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/StatLinePage"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ProRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/players": {
      "get": {
        "tags": [
          "Players"
        ],
        "operationId": "search-players",
        "summary": "Search players by name",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "minLength": 2
            }
          },
          {
            "$ref": "#/components/parameters/Limit"
          }
        ],
        "responses": {
          "200": {
            "description": "Matching players",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Player"
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/ProRequired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/v1/players/{playerId}": {
      "get": {
        "tags": [
          "Players"
        ],
        "operationId": "get-player",
        "summary": "Get a player",
        "parameters": [
          {
            "$ref": "#/components/parameters/PlayerId"
          }
        ],
        "responses": {
          "200": {
            "description": "The player",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Player"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/health": {
      "get": {
        "tags": [
          "Meta"
        ],
        "operationId": "health",
        "summary": "Service health",
        "security": [],
        "responses": {
          "200": {
            "description": "Service is healthy",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": {
                      "type": "string",
                      "const": "ok"
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "clerkAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT",
        "description": "Clerk-issued token from your PFF account (browser login via the Restish CLI). Requires the `tier: pro` claim \u2014 non-Pro accounts receive 403."
      }
    },
    "parameters": {
      "Season": {
        "name": "season",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer",
          "minimum": 2006
        },
        "example": 2026
      },
      "SeasonQuery": {
        "name": "season",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 2006
        },
        "example": 2026
      },
      "PlayerId": {
        "name": "playerId",
        "in": "path",
        "required": true,
        "schema": {
          "type": "string"
        },
        "example": "11765"
      },
      "Cursor": {
        "name": "cursor",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Opaque cursor from the previous page's `next_cursor`."
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 50
        }
      }
    },
    "schemas": {
      "Player": {
        "type": "object",
        "required": [
          "id",
          "name",
          "position",
          "team"
        ],
        "properties": {
          "id": {
            "type": "string",
            "example": "11765"
          },
          "name": {
            "type": "string",
            "example": "Patrick Mahomes"
          },
          "position": {
            "type": "string",
            "example": "QB"
          },
          "team": {
            "type": "string",
            "example": "KC"
          },
          "jersey_number": {
            "type": "integer",
            "example": 15
          }
        }
      },
      "PlayerGrade": {
        "type": "object",
        "required": [
          "season",
          "overall"
        ],
        "properties": {
          "season": {
            "type": "integer",
            "example": 2026
          },
          "week": {
            "type": "integer",
            "nullable": true,
            "example": 1
          },
          "overall": {
            "type": "number",
            "example": 92.4
          },
          "offense": {
            "type": "number",
            "nullable": true
          },
          "defense": {
            "type": "number",
            "nullable": true
          },
          "snaps": {
            "type": "integer",
            "example": 68
          }
        }
      },
      "PlayerGradeRow": {
        "allOf": [
          {
            "$ref": "#/components/schemas/PlayerGrade"
          },
          {
            "type": "object",
            "required": [
              "player"
            ],
            "properties": {
              "player": {
                "$ref": "#/components/schemas/Player"
              }
            }
          }
        ]
      },
      "StatLine": {
        "type": "object",
        "required": [
          "player",
          "season",
          "stats"
        ],
        "properties": {
          "player": {
            "$ref": "#/components/schemas/Player"
          },
          "season": {
            "type": "integer"
          },
          "week": {
            "type": "integer",
            "nullable": true
          },
          "stats": {
            "type": "object",
            "additionalProperties": {
              "type": "number"
            },
            "description": "Facet-specific stat fields, e.g. `attempts`, `yards`, `epa_per_play`.",
            "example": {
              "attempts": 38,
              "yards": 312,
              "epa_per_play": 0.21
            }
          }
        }
      },
      "PlayerGradePage": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlayerGradeRow"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "StatLinePage": {
        "type": "object",
        "required": [
          "items"
        ],
        "properties": {
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/StatLine"
            }
          },
          "next_cursor": {
            "type": "string",
            "nullable": true
          }
        }
      },
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "string"
          },
          "message": {
            "type": "string"
          },
          "upgrade_url": {
            "type": "string",
            "description": "Present on 403 for non-Pro accounts."
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid token \u2014 log in via the CLI.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "ProRequired": {
        "description": "Authenticated but no active PFF Pro subscription.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            },
            "example": {
              "error": "pro_required",
              "message": "PFF Pro subscription required.",
              "upgrade_url": "https://www.pff.com/pro"
            }
          }
        }
      },
      "NotFound": {
        "description": "No such resource.",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded \u2014 respect Retry-After.",
        "headers": {
          "Retry-After": {
            "schema": {
              "type": "integer"
            },
            "description": "Seconds to wait."
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    }
  }
}