{
  "openapi": "3.1.0",
  "info": {
    "title": "QueerCade Public API",
    "version": "1.0.0",
    "description": "Read-only access to QueerCade's curated catalog of video games with LGBTQ+ characters and stories.\n\nOnly publicly visible, editor-approved content is returned. Editorial notes, review-queue data, import history and member data are never exposed.\n\nAuthentication: none required. Every endpoint is GET only.\nPaging: offset/limit, maximum 48 items per request, maximum offset 10000. List responses include total and nextOffset (null on the last page).\nRate limit: approximately 120 requests per 60 seconds per client, enforced per server instance. Exceeding it returns 429 with Retry-After.\nCaching: successful responses are cacheable for 5 minutes by clients and 1 hour by shared caches. Please cache rather than re-poll.\nErrors: every failure returns { error: { code, message, status } } with a stable code.\nCORS: all origins allowed.",
    "contact": {
      "url": "https://queercade.gay/developers"
    },
    "license": {
      "name": "Catalog data from IGDB; editorial content by QueerCade",
      "url": "https://queercade.gay/licenses"
    }
  },
  "servers": [
    {
      "url": "https://queercade.gay/api/public/v1"
    }
  ],
  "paths": {
    "/games": {
      "get": {
        "operationId": "searchGames",
        "summary": "Search or list games",
        "description": "Matches game titles and LGBTQ+ character names. Without q, lists games alphabetically. Filters combine with AND.",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "description": "Title or character name prefix search.",
            "schema": {
              "type": "string",
              "maxLength": 100
            },
            "example": "chloe"
          },
          {
            "name": "genre",
            "in": "query",
            "description": "Genre slug, as returned by /facets.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{0,60}$"
            },
            "example": "role-playing-rpg"
          },
          {
            "name": "platform",
            "in": "query",
            "description": "Platform slug, as returned by /facets.",
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{0,60}$"
            },
            "example": "nintendo-switch"
          },
          {
            "name": "theme",
            "in": "query",
            "description": "Theme name, as returned by /facets.",
            "schema": {
              "type": "string",
              "maxLength": 60
            }
          },
          {
            "name": "year",
            "in": "query",
            "description": "Exact release year.",
            "schema": {
              "type": "integer",
              "minimum": 1970,
              "maximum": 2100
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 48,
              "default": 24
            }
          },
          {
            "name": "offset",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 0,
              "maximum": 10000,
              "default": 0
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Matching games.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GameList"
                }
              }
            }
          },
          "400": {
            "description": "Invalid query parameters.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "invalid_request",
                    "message": "Invalid query parameters.",
                    "status": 400
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Retry after the number of seconds in the Retry-After header. Limit: 120 requests per 60 seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix seconds when the window resets."
              },
              "RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Limit."
              },
              "RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Remaining."
              },
              "RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/games/{slug}": {
      "get": {
        "operationId": "getGame",
        "summary": "Get one game with its full public metadata",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{1,120}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Game.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/GameDetail"
                }
              }
            }
          },
          "404": {
            "description": "No such record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "Game not found.",
                    "status": 404
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Retry after the number of seconds in the Retry-After header. Limit: 120 requests per 60 seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix seconds when the window resets."
              },
              "RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Limit."
              },
              "RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Remaining."
              },
              "RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/collections": {
      "get": {
        "operationId": "listCollections",
        "summary": "List curated collections",
        "responses": {
          "200": {
            "description": "Collections.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CollectionList"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Retry after the number of seconds in the Retry-After header. Limit: 120 requests per 60 seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix seconds when the window resets."
              },
              "RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Limit."
              },
              "RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Remaining."
              },
              "RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/collections/{slug}": {
      "get": {
        "operationId": "getCollection",
        "summary": "Get one collection and its games in editorial order",
        "parameters": [
          {
            "name": "slug",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "pattern": "^[a-z0-9-]{1,120}$"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Collection with its games.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CollectionDetail"
                }
              }
            }
          },
          "404": {
            "description": "No such record.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "Game not found.",
                    "status": 404
                  }
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Retry after the number of seconds in the Retry-After header. Limit: 120 requests per 60 seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix seconds when the window resets."
              },
              "RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Limit."
              },
              "RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Remaining."
              },
              "RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/facets": {
      "get": {
        "operationId": "listFacets",
        "summary": "List the genre, platform and theme values that filters accept",
        "responses": {
          "200": {
            "description": "Available filter values.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Facets"
                }
              }
            }
          },
          "429": {
            "description": "Rate limited. Retry after the number of seconds in the Retry-After header. Limit: 120 requests per 60 seconds.",
            "headers": {
              "Retry-After": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds to wait before retrying."
              },
              "X-RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                }
              },
              "X-RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Unix seconds when the window resets."
              },
              "RateLimit-Limit": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Limit."
              },
              "RateLimit-Remaining": {
                "schema": {
                  "type": "integer"
                },
                "description": "RFC 9652 alias of X-RateLimit-Remaining."
              },
              "RateLimit-Reset": {
                "schema": {
                  "type": "integer"
                },
                "description": "Seconds until the window resets."
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Catalog temporarily unavailable.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "status"
            ],
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_request",
                  "not_found",
                  "rate_limited",
                  "service_unavailable"
                ]
              },
              "message": {
                "type": "string"
              },
              "status": {
                "type": "integer"
              }
            }
          },
          "message": {
            "type": "string",
            "description": "Same human-readable message, flattened."
          }
        }
      },
      "GameSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "igdbId": {
            "type": "integer"
          },
          "releaseYear": {
            "type": [
              "integer",
              "null"
            ]
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "coverUrl": {
            "type": [
              "string",
              "null"
            ],
            "format": "uri"
          },
          "genres": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "lgbtqCharacters": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      },
      "GameDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/GameSummary"
          },
          {
            "type": "object",
            "properties": {
              "firstReleaseDate": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "summary": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "storyline": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "themes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "gameModes": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "developer": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "publisher": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "franchise": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "series": {
                "type": [
                  "string",
                  "null"
                ]
              },
              "igdbRating": {
                "type": [
                  "number",
                  "null"
                ]
              },
              "igdbRatingCount": {
                "type": [
                  "integer",
                  "null"
                ]
              },
              "ageRatings": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "externalLinks": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "storeLinks": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              },
              "screenshots": {
                "type": "array",
                "items": {
                  "type": "string",
                  "format": "uri"
                }
              },
              "alternativeNames": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              },
              "lgbtqCharacters": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          }
        ]
      },
      "GameList": {
        "type": "object",
        "required": [
          "games",
          "total",
          "limit",
          "offset",
          "nextOffset"
        ],
        "properties": {
          "games": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/GameSummary"
            }
          },
          "total": {
            "type": "integer",
            "description": "Total matches across all pages."
          },
          "limit": {
            "type": "integer"
          },
          "offset": {
            "type": "integer"
          },
          "nextOffset": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Offset of the next page, or null on the last page."
          },
          "version": {
            "type": "string"
          }
        }
      },
      "CollectionSummary": {
        "type": "object",
        "properties": {
          "slug": {
            "type": "string"
          },
          "title": {
            "type": "string"
          },
          "url": {
            "type": "string",
            "format": "uri"
          },
          "description": {
            "type": [
              "string",
              "null"
            ]
          },
          "featured": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "gameCount": {
            "type": "integer"
          }
        }
      },
      "CollectionList": {
        "type": "object",
        "required": [
          "collections",
          "total"
        ],
        "properties": {
          "collections": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/CollectionSummary"
            }
          },
          "total": {
            "type": "integer"
          },
          "version": {
            "type": "string"
          }
        }
      },
      "CollectionDetail": {
        "allOf": [
          {
            "$ref": "#/components/schemas/CollectionSummary"
          },
          {
            "type": "object",
            "properties": {
              "games": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/GameSummary"
                }
              }
            }
          }
        ]
      },
      "Facets": {
        "type": "object",
        "properties": {
          "genres": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                }
              }
            }
          },
          "platforms": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "name": {
                  "type": "string"
                },
                "slug": {
                  "type": "string"
                }
              }
            }
          },
          "themes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        }
      }
    }
  }
}