{
  "openapi": "3.1.0",
  "info": {
    "title": "SportFeeds API",
    "version": "1.0.0",
    "description": "B2B sports social-media data. Authenticate with a Bearer API key. The single GET /posts endpoint serves every retrieval mode — the mode is inferred from your query parameters.",
    "contact": {
      "name": "SportFeeds",
      "url": "https://sportfeeds.com"
    }
  },
  "servers": [
    {
      "url": "https://api.sportfeeds.com/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "BearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "BearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Your SportFeeds API key"
      }
    },
    "schemas": {
      "Post": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "author_username": {
            "type": "string"
          },
          "author_id": {
            "type": "string"
          },
          "league": {
            "type": "string"
          },
          "post_url": {
            "type": "string",
            "format": "uri"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "like_count": {
            "type": "integer"
          },
          "retweet_count": {
            "type": "integer"
          },
          "reply_count": {
            "type": "integer"
          },
          "quote_count": {
            "type": "integer"
          },
          "media_type": {
            "type": "string",
            "enum": [
              "image",
              "video",
              "none"
            ]
          },
          "highlight": {
            "type": "boolean"
          },
          "teams": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "players": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "media": {
            "type": "array",
            "items": {
              "type": "object"
            }
          }
        }
      },
      "PostsResponse": {
        "type": "object",
        "properties": {
          "pagination": {
            "type": "object",
            "properties": {
              "page": {
                "type": "integer"
              },
              "limit": {
                "type": "integer"
              },
              "has_next": {
                "type": "boolean"
              },
              "has_prev": {
                "type": "boolean"
              }
            }
          },
          "items": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Post"
            }
          }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "ok": {
            "type": "boolean",
            "enum": [
              false
            ]
          },
          "error_code": {
            "type": "string"
          },
          "error_message": {
            "type": "string"
          }
        }
      }
    }
  },
  "paths": {
    "/posts": {
      "get": {
        "summary": "Unified posts endpoint — every retrieval mode",
        "description": "The single canonical endpoint for fetching sports social posts. Mode is inferred from query parameters: provide `teams=` for team-mode, `players=` for player-mode, `lat`+`lng` for nearby-mode, `match`+`start_time` for game-mode, `sort=trending` for trending-mode, or only `leagues=` (or nothing) for league/trending-mode. Split per-mode routes (e.g. /posts/leagues, /posts/teams) are deprecated and return 410 for new accounts.",
        "operationId": "getPosts",
        "parameters": [
          {
            "name": "leagues",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated league codes, or ALL. Supported: NFL, NCAAFB, NBA, WNBA, NCAAMB, MLB, NHL, PGA, GOLF, EPL, MLS, FIFA, SOCCER, CRICKET, RUGBY, MMA. Case-insensitive."
          },
          {
            "name": "sports",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated parent sports: americanfootball, football, basketball, baseball, hockey, golf, cricket, rugby, mma. Case-insensitive. `football` means association football (soccer); the NFL/NCAA game is `americanfootball`. `soccer` is accepted as an alias for `football`. Returns posts across all leagues under those sports. Invalid values return 400."
          },
          {
            "name": "teams",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated team IDs. Implies team mode."
          },
          {
            "name": "players",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated player IDs. Implies player mode."
          },
          {
            "name": "id_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "code",
                "espn_uid",
                "espn_guid",
                "sportfeeds",
                "sportsstack"
              ]
            },
            "description": "ID system used for teams/players IDs. Required when teams= or players= is set."
          },
          {
            "name": "match",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "Two team IDs for a game (teamA,teamB). Requires start_time."
          },
          {
            "name": "start_time",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "ISO timestamp of game start. Implies game mode with match=."
          },
          {
            "name": "include_pregame",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "lat",
            "in": "query",
            "schema": {
              "type": "number"
            },
            "description": "Latitude. With lng= implies nearby mode."
          },
          {
            "name": "lng",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "radius_km",
            "in": "query",
            "schema": {
              "type": "number",
              "default": 50
            }
          },
          {
            "name": "media_type",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "image",
                "video"
              ]
            }
          },
          {
            "name": "aspect_ratio",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "landscape",
                "portrait",
                "square"
              ]
            }
          },
          {
            "name": "highlight",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "has_player",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "custom_sources",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          },
          {
            "name": "user_location",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "description": "ISO 3166-1 alpha-2 country code (e.g. US, ZA). Restricts results to sources cleared for viewing in that region. Sources not explicitly marked viewable in the region are excluded. Invalid codes return 400."
          },
          {
            "name": "min_score",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "sort",
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "recent",
                "trending",
                "top"
              ]
            }
          },
          {
            "name": "since",
            "in": "query",
            "schema": {
              "type": "string",
              "format": "date-time"
            },
            "description": "Watermark for polling — return only posts created after this timestamp."
          },
          {
            "name": "since_id",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 20,
              "maximum": 100
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "integer",
              "default": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PostsResponse"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "402": {
            "description": "No active subscription",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Quota exceeded",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  }
}