openapi: 3.0.3 info: title: jService Trivia API description: | jService is an open source Ruby on Rails API that serves Jeopardy! questions (called "clues"), answers, and categories sourced from the J! Archive fan site. The schema in this document is reconstructed from the upstream source repository (`sottenad/jService`, MIT licensed). The historical public deployment at https://jservice.io is offline as of 2025 (parked / for-sale holding page); the project remains self-hostable against PostgreSQL. All endpoints return JSON. None require authentication; no rate limits are documented in the source. version: '1.0.0' contact: name: Steve Ottenad url: https://github.com/sottenad/jService license: name: MIT url: https://github.com/sottenad/jService/blob/master/LICENSE.txt x-status: deprecated x-status-reason: Original hosted endpoint at jservice.io is offline; spec preserved from source. x-data-source: https://j-archive.com servers: - url: http://jservice.io description: Original hosted endpoint (historical, currently offline) - url: http://localhost:3000 description: Default local Rails development server tags: - name: Clues description: Jeopardy! questions, answers, and metadata. - name: Categories description: Category collections of clues. - name: Moderation description: User-driven reporting of invalid clues. paths: /api/random: get: tags: [Clues] summary: Get Random Clues description: Returns N random clues, including the parent category. Default 1, maximum 100. operationId: getRandomClues parameters: - name: count in: query description: Number of clues to return (max 100). required: false schema: type: integer minimum: 1 maximum: 100 default: 1 responses: '200': description: Array of random clues with category embedded. content: application/json: schema: type: array items: $ref: '#/components/schemas/ClueWithCategory' /api/final: get: tags: [Clues] summary: Get Final Jeopardy Clues description: >- Returns N random Final Jeopardy clues (clues with no `value`). Default 1, maximum 100. operationId: getFinalClues parameters: - name: count in: query required: false schema: type: integer minimum: 1 maximum: 100 default: 1 responses: '200': description: Array of Final Jeopardy clues with category embedded. content: application/json: schema: type: array items: $ref: '#/components/schemas/ClueWithCategory' /api/clues: get: tags: [Clues] summary: List Clues description: >- List clues with optional filters. Page size is fixed at 100; use `offset` for pagination. operationId: listClues parameters: - name: value in: query description: Filter by dollar value of the clue. required: false schema: type: integer - name: category in: query description: Filter by category ID. required: false schema: type: integer - name: min_date in: query description: Earliest airdate (parsed via Chronic, e.g. "2010-01-01"). required: false schema: type: string format: date - name: max_date in: query description: Latest airdate (parsed via Chronic). required: false schema: type: string format: date - name: offset in: query description: Pagination offset. required: false schema: type: integer minimum: 0 default: 0 - name: game_id in: query description: Filter by Jeopardy! game ID. required: false schema: type: integer responses: '200': description: Array of clues matching the filters (up to 100 per page). content: application/json: schema: type: array items: $ref: '#/components/schemas/ClueWithCategory' /api/categories: get: tags: [Categories] summary: List Categories description: List categories with offset/count pagination. Default count 1, maximum 100. operationId: listCategories parameters: - name: count in: query description: Number of categories to return (max 100). required: false schema: type: integer minimum: 1 maximum: 100 default: 1 - name: offset in: query description: Pagination offset. required: false schema: type: integer minimum: 0 default: 0 responses: '200': description: Array of categories. content: application/json: schema: type: array items: $ref: '#/components/schemas/Category' /api/category: get: tags: [Categories] summary: Get Single Category description: Get one category by ID, including all of its clues. operationId: getCategory parameters: - name: id in: query description: Category ID. required: true schema: type: integer responses: '200': description: A category with embedded clues. content: application/json: schema: $ref: '#/components/schemas/CategoryWithClues' '404': description: Category not found. /api/invalid: post: tags: [Moderation] summary: Mark Clue Invalid description: Increment the `invalid_count` for a clue, used to flag bad questions/answers. operationId: markClueInvalid parameters: - name: id in: query description: Clue ID to flag as invalid. required: true schema: type: integer responses: '200': description: The updated clue. content: application/json: schema: $ref: '#/components/schemas/Clue' '404': description: Clue not found. components: schemas: Clue: type: object properties: id: type: integer answer: type: string description: The Jeopardy "answer" (read by the host). question: type: string description: The Jeopardy "question" (the contestant's response). value: type: integer nullable: true description: Dollar value of the clue. Null for Final Jeopardy clues. airdate: type: string format: date-time category_id: type: integer game_id: type: integer nullable: true invalid_count: type: integer nullable: true description: Number of times users have flagged this clue as invalid. required: [id, answer, question, category_id] ClueWithCategory: allOf: - $ref: '#/components/schemas/Clue' - type: object properties: category: $ref: '#/components/schemas/Category' Category: type: object properties: id: type: integer title: type: string created_at: type: string format: date-time updated_at: type: string format: date-time clues_count: type: integer default: 0 required: [id, title] CategoryWithClues: allOf: - $ref: '#/components/schemas/Category' - type: object properties: clues: type: array items: $ref: '#/components/schemas/Clue'