{
  "openapi": "3.0.3",
  "info": {
    "title": "Case Repository API",
    "version": "1.16",
    "description": "A read-only JSON API over ~10 million published U.S. court opinions, with full text, citation counts and bulk export. The authoritative guide is https://caserepository.com/doc.txt; this document is the machine-readable index of it. Version tracks the response-shape epoch: the minor number moves whenever any response body changes.\n\nThe three facts consumers most often miss: listings return rows in id order, never ranked, so one page is an arbitrary slice (GET /search ranks, GET /stats counts); bulk collection is POST /exports, never pagination; and unrecognized parameters are ignored, with a top-level `warnings` array naming what was dropped. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset headers.",
    "contact": {
      "name": "Nicholas Clark",
      "url": "https://caserepository.com",
      "email": "nick@caserepository.com"
    }
  },
  "externalDocs": {
    "description": "The complete guide, worked examples and the semantics of every field",
    "url": "https://caserepository.com/doc.txt"
  },
  "servers": [
    {
      "url": "https://caserepository.com/api/v1"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    },
    {
      "apiKeyQuery": []
    }
  ],
  "paths": {
    "/whoami": {
      "get": {
        "operationId": "whoami",
        "summary": "The calling key: name, request count, expiry",
        "responses": {
          "200": {
            "description": "The key that made this request",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "name": {
                      "type": "string"
                    },
                    "requests_count": {
                      "type": "integer",
                      "description": "Requests this key made before this one; exact"
                    },
                    "key_active_since": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "expires_at": {
                      "type": "string",
                      "format": "date-time",
                      "nullable": true,
                      "description": "Null for a key that never expires"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/meta": {
      "get": {
        "operationId": "meta",
        "summary": "What is in the corpus: counts, coverage, the citable snapshot",
        "responses": {
          "200": {
            "description": "Snapshot facts; two requests against the same snapshot always agree",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "corpus": {
                      "type": "object",
                      "properties": {
                        "cases": {
                          "type": "integer"
                        },
                        "courts": {
                          "type": "integer"
                        },
                        "snapshot_date": {
                          "type": "string",
                          "format": "date",
                          "description": "The snapshot's name; record this in a method section"
                        },
                        "coverage": {
                          "type": "object",
                          "properties": {
                            "earliest": {
                              "type": "string",
                              "format": "date",
                              "nullable": true
                            },
                            "latest": {
                              "type": "string",
                              "format": "date",
                              "nullable": true
                            }
                          }
                        },
                        "implausible_dates": {
                          "type": "integer",
                          "description": "Queryable records whose date_filed cannot be real; excluded from coverage only"
                        },
                        "source": {
                          "type": "string"
                        }
                      }
                    },
                    "api": {
                      "type": "object",
                      "properties": {
                        "version": {
                          "type": "string"
                        },
                        "docs": {
                          "type": "string"
                        },
                        "openapi": {
                          "type": "string"
                        }
                      }
                    },
                    "generated_at": {
                      "type": "string",
                      "format": "date-time"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/courts": {
      "get": {
        "operationId": "listCourts",
        "summary": "All 3,361 courts; call this instead of guessing slugs",
        "parameters": [
          {
            "$ref": "#/components/parameters/jurisdiction"
          },
          {
            "$ref": "#/components/parameters/in_use"
          }
        ],
        "responses": {
          "200": {
            "description": "Courts matching the filters",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "count": {
                      "type": "integer"
                    },
                    "courts": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Court"
                      }
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/courts/{id}": {
      "get": {
        "operationId": "getCourt",
        "summary": "One court by slug (case-insensitive)",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string"
            },
            "example": "scotus"
          }
        ],
        "responses": {
          "200": {
            "description": "The court",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Court"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cases": {
      "get": {
        "operationId": "listCases",
        "summary": "Filter cases; rows come back in id order (date order under a date filter), never ranked",
        "description": "Combine filters freely; follow next_url verbatim to paginate. There is no total here: GET /stats takes the same filters and returns one. To rank, use GET /search. To collect in bulk, POST /exports.",
        "parameters": [
          {
            "$ref": "#/components/parameters/court"
          },
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/text"
          },
          {
            "$ref": "#/components/parameters/cite"
          },
          {
            "$ref": "#/components/parameters/filed_after"
          },
          {
            "$ref": "#/components/parameters/filed_before"
          },
          {
            "$ref": "#/components/parameters/min_citations"
          },
          {
            "$ref": "#/components/parameters/max_citations"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/cites"
          },
          {
            "$ref": "#/components/parameters/cited_by"
          },
          {
            "$ref": "#/components/parameters/per_page"
          },
          {
            "$ref": "#/components/parameters/after_id"
          },
          {
            "$ref": "#/components/parameters/after_date"
          }
        ],
        "responses": {
          "200": {
            "description": "One page; next_url is null on the last page",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "cases": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/CaseListItem"
                      }
                    },
                    "per_page": {
                      "type": "integer",
                      "description": "The size actually used; out-of-range requests are clamped and echoed here"
                    },
                    "next_url": {
                      "type": "string",
                      "nullable": true,
                      "description": "Request verbatim; never build the cursor yourself"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cases/{id}": {
      "get": {
        "operationId": "getCase",
        "summary": "One case: caption, opinions, its own reporter citations",
        "parameters": [
          {
            "$ref": "#/components/parameters/caseId"
          }
        ],
        "responses": {
          "200": {
            "description": "The case. citations are how lawyers cite THIS case, ordered by citation type; citations[0] is not \"the\" official cite",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaseDetail"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cases/{id}/text": {
      "get": {
        "operationId": "getCaseText",
        "summary": "Full text of every opinion in the case; uncapped, so size before bulk fetching",
        "parameters": [
          {
            "$ref": "#/components/parameters/caseId"
          },
          {
            "$ref": "#/components/parameters/include_markup"
          }
        ],
        "responses": {
          "200": {
            "description": "All opinions, whole. Median ~6KB, p99 ~83KB, largest observed 1.5MB; include_markup roughly doubles it",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "case_id": {
                      "type": "integer"
                    },
                    "case_name": {
                      "type": "string",
                      "nullable": true
                    },
                    "include_markup": {
                      "type": "boolean"
                    },
                    "opinions": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/TextOpinion"
                      }
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cases/{id}/parentheticals": {
      "get": {
        "operationId": "getCaseParentheticals",
        "summary": "What later courts say the case stands for, best first; capped at 100 with no cursor",
        "parameters": [
          {
            "$ref": "#/components/parameters/caseId"
          },
          {
            "$ref": "#/components/parameters/per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "Compare total against the array length: for heavily cited cases 100 is a hard ceiling and the set is a sample, not a census",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "case_id": {
                      "type": "integer"
                    },
                    "case_name": {
                      "type": "string",
                      "nullable": true
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "total": {
                      "type": "integer",
                      "description": "How many exist, which is not how many you got"
                    },
                    "parentheticals": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/Parenthetical"
                      }
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/cases/{id}/similar": {
      "get": {
        "operationId": "getSimilarCases",
        "summary": "Cases that read like this one, by embedding similarity; max 200, no cursor",
        "parameters": [
          {
            "$ref": "#/components/parameters/caseId"
          },
          {
            "$ref": "#/components/parameters/similar_per_page"
          }
        ],
        "responses": {
          "200": {
            "description": "Nearest cases by meaning, best first. An empty results array is ordinary (about 17% of opinions have no embeddings), not an error. Similar is not the same as relevant: nothing here walks the citation graph",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "integer",
                      "description": "The case asked about"
                    },
                    "case_name": {
                      "type": "string",
                      "nullable": true
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SimilarCase"
                      }
                    },
                    "per_page": {
                      "type": "integer",
                      "description": "The size actually used; out-of-range requests are clamped and echoed here"
                    },
                    "method": {
                      "type": "string",
                      "description": "How similarity was computed, in a sentence"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/search": {
      "get": {
        "operationId": "search",
        "summary": "Ranked top matches with snippets; bounded depth, no pagination",
        "description": "The preview form of full-text search. Default order is text-match quality, not importance: sort=citation_count ranks a doctrine's leading cases. The same query as text= on POST /exports downloads every match.",
        "parameters": [
          {
            "$ref": "#/components/parameters/q"
          },
          {
            "$ref": "#/components/parameters/sort"
          },
          {
            "$ref": "#/components/parameters/per_page"
          },
          {
            "$ref": "#/components/parameters/court"
          },
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/cite"
          },
          {
            "$ref": "#/components/parameters/filed_after"
          },
          {
            "$ref": "#/components/parameters/filed_before"
          },
          {
            "$ref": "#/components/parameters/min_citations"
          },
          {
            "$ref": "#/components/parameters/max_citations"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/cites"
          },
          {
            "$ref": "#/components/parameters/cited_by"
          }
        ],
        "responses": {
          "200": {
            "description": "Top matches, at most 100. match_estimate.exact false means the count is a floor; note then says what the cap did to this ordering",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "query": {
                      "type": "string"
                    },
                    "sort": {
                      "type": "string",
                      "enum": [
                        "relevance",
                        "citation_count"
                      ]
                    },
                    "sorts": {
                      "type": "string",
                      "description": "Both orderings and when to use each, so the alternative is discoverable from a successful response"
                    },
                    "results": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/SearchResult"
                      }
                    },
                    "per_page": {
                      "type": "integer"
                    },
                    "match_estimate": {
                      "$ref": "#/components/schemas/MatchEstimate"
                    },
                    "bulk": {
                      "type": "string",
                      "description": "How to download every match instead of the preview"
                    },
                    "note": {
                      "type": "string",
                      "description": "Present only past the candidate cap; read it, the two sorts saturate differently"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/stats": {
      "get": {
        "operationId": "stats",
        "summary": "Cases per year for any selection; the free total and the export-splitting tool",
        "parameters": [
          {
            "$ref": "#/components/parameters/court"
          },
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/text"
          },
          {
            "$ref": "#/components/parameters/cite"
          },
          {
            "$ref": "#/components/parameters/filed_after"
          },
          {
            "$ref": "#/components/parameters/filed_before"
          },
          {
            "$ref": "#/components/parameters/min_citations"
          },
          {
            "$ref": "#/components/parameters/max_citations"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/cites"
          },
          {
            "$ref": "#/components/parameters/cited_by"
          }
        ],
        "responses": {
          "200": {
            "description": "total is by_year plus undated, always",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "total": {
                      "type": "integer"
                    },
                    "by_year": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "year": {
                            "type": "integer"
                          },
                          "count": {
                            "type": "integer"
                          }
                        }
                      }
                    },
                    "undated": {
                      "type": "integer",
                      "description": "Matching cases with no filing date"
                    },
                    "query_params": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/exports": {
      "get": {
        "operationId": "listExports",
        "summary": "The calling key's recent export jobs, newest first",
        "responses": {
          "200": {
            "description": "At most 50 jobs",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "exports": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/ExportJob"
                      }
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      },
      "post": {
        "operationId": "createExport",
        "summary": "Create an async bulk export; poll GET /exports/{id}, then download",
        "description": "Same filters as /cases. Text exports cap at 10,000 cases, metadata at 1,000,000; GET /exports/estimate checks a candidate for free. Files expire one day after the job finishes. Parameters ride the query string.",
        "parameters": [
          {
            "$ref": "#/components/parameters/court"
          },
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/text"
          },
          {
            "$ref": "#/components/parameters/cite"
          },
          {
            "$ref": "#/components/parameters/filed_after"
          },
          {
            "$ref": "#/components/parameters/filed_before"
          },
          {
            "$ref": "#/components/parameters/min_citations"
          },
          {
            "$ref": "#/components/parameters/max_citations"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/cites"
          },
          {
            "$ref": "#/components/parameters/cited_by"
          },
          {
            "$ref": "#/components/parameters/include_text"
          },
          {
            "$ref": "#/components/parameters/format"
          }
        ],
        "responses": {
          "202": {
            "description": "The queued job; corpus_snapshot is stamped when it starts running",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExportJob"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/exports/estimate": {
      "get": {
        "operationId": "estimateExport",
        "summary": "Size an export before creating it; free, no quota spent",
        "parameters": [
          {
            "$ref": "#/components/parameters/court"
          },
          {
            "$ref": "#/components/parameters/name"
          },
          {
            "$ref": "#/components/parameters/text"
          },
          {
            "$ref": "#/components/parameters/cite"
          },
          {
            "$ref": "#/components/parameters/filed_after"
          },
          {
            "$ref": "#/components/parameters/filed_before"
          },
          {
            "$ref": "#/components/parameters/min_citations"
          },
          {
            "$ref": "#/components/parameters/max_citations"
          },
          {
            "$ref": "#/components/parameters/status"
          },
          {
            "$ref": "#/components/parameters/cites"
          },
          {
            "$ref": "#/components/parameters/cited_by"
          },
          {
            "$ref": "#/components/parameters/include_text"
          }
        ],
        "responses": {
          "200": {
            "description": "fits true means the POST with these exact parameters will be accepted",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "estimate": {
                      "$ref": "#/components/schemas/MatchEstimate"
                    },
                    "include_text": {
                      "type": "boolean"
                    },
                    "limit": {
                      "type": "integer"
                    },
                    "fits": {
                      "type": "boolean"
                    },
                    "query_params": {
                      "type": "object",
                      "additionalProperties": true
                    },
                    "message": {
                      "type": "string",
                      "description": "Present only when the selection is over the cap; names the ways to split it"
                    },
                    "warnings": {
                      "$ref": "#/components/schemas/Warnings"
                    }
                  }
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/exports/{id}": {
      "get": {
        "operationId": "getExport",
        "summary": "One export job; poll until status is completed, then GET download_url",
        "parameters": [
          {
            "$ref": "#/components/parameters/exportId"
          }
        ],
        "responses": {
          "200": {
            "description": "The job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ExportJob"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    },
    "/exports/{id}/download": {
      "get": {
        "operationId": "downloadExport",
        "summary": "The export file; stored gzipped, labeled by your Accept-Encoding",
        "description": "Send Accept-Encoding: gzip and the body arrives Content-Encoding: gzip named .jsonl or .csv; send anything else and the same bytes arrive as application/gzip named .jsonl.gz or .csv.gz. Read the headers rather than assuming.",
        "parameters": [
          {
            "$ref": "#/components/parameters/exportId"
          }
        ],
        "responses": {
          "200": {
            "description": "JSONL (one case object per line), or CSV for format=csv jobs",
            "content": {
              "application/jsonl": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "text/csv": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              },
              "application/gzip": {
                "schema": {
                  "type": "string",
                  "format": "binary"
                }
              }
            }
          },
          "default": {
            "$ref": "#/components/responses/Error"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Authorization: Bearer ck_yourkey (preferred)"
      },
      "apiKeyQuery": {
        "type": "apiKey",
        "in": "query",
        "name": "api_key",
        "description": "Query fallback for one-off calls; keys in URLs end up in logs"
      }
    },
    "parameters": {
      "caseId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        },
        "description": "A case id from list results"
      },
      "exportId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": {
          "type": "integer"
        },
        "description": "An export job id belonging to the calling key"
      },
      "court": {
        "name": "court",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Court slug, lowercase, like scotus or ca8; GET /courts lists all valid ids",
        "example": "scotus"
      },
      "name": {
        "name": "name",
        "in": "query",
        "schema": {
          "type": "string",
          "minLength": 3
        },
        "description": "Literal case-insensitive substring of the case name; nothing is normalized, so prefer cite= when you have a citation"
      },
      "text": {
        "name": "text",
        "in": "query",
        "schema": {
          "type": "string",
          "maxLength": 256
        },
        "description": "Full-text filter over opinions, websearch syntax: \"quoted phrases\", OR, -exclusions; set semantics, composes with every filter and with exports"
      },
      "cite": {
        "name": "cite",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "Exact reporter citation, volume reporter page; abbreviations normalized, Westlaw/LEXIS cites resolve",
        "example": "559 U.S. 356"
      },
      "filed_after": {
        "name": "filed_after",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "ISO 8601 lower bound on date_filed"
      },
      "filed_before": {
        "name": "filed_before",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "ISO 8601 upper bound on date_filed"
      },
      "min_citations": {
        "name": "min_citations",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 0
        },
        "description": "Only cases cited at least this many times"
      },
      "max_citations": {
        "name": "max_citations",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 0
        },
        "description": "Only cases cited at most this many times"
      },
      "status": {
        "name": "status",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "Published",
            "Unpublished",
            "Unknown",
            "Errata",
            "Separate",
            "Relating-to",
            "In-chambers"
          ]
        },
        "description": "Precedential status"
      },
      "cites": {
        "name": "cites",
        "in": "query",
        "schema": {
          "type": "integer"
        },
        "description": "Only cases that cite the named case; rows gain citation_depth"
      },
      "cited_by": {
        "name": "cited_by",
        "in": "query",
        "schema": {
          "type": "integer"
        },
        "description": "Only cases the named case itself cites (its authorities); rows gain citation_depth"
      },
      "per_page": {
        "name": "per_page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 100
        },
        "description": "Page size, max 100; out-of-range values are clamped and the response echoes what was used"
      },
      "after_id": {
        "name": "after_id",
        "in": "query",
        "schema": {
          "type": "integer"
        },
        "description": "Pagination cursor; take it from next_url rather than building it"
      },
      "after_date": {
        "name": "after_date",
        "in": "query",
        "schema": {
          "type": "string",
          "format": "date"
        },
        "description": "Date-ordered cursor half; only valid together with after_id, and only from next_url"
      },
      "include_text": {
        "name": "include_text",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "true",
            "false",
            "1",
            "0"
          ]
        },
        "description": "Include full opinion text per case; drops the cap from 1,000,000 to 10,000"
      },
      "include_markup": {
        "name": "include_markup",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "true",
            "false",
            "1",
            "0"
          ]
        },
        "description": "Also return the structured source, raw; roughly doubles the payload, sanitize before rendering"
      },
      "format": {
        "name": "format",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "jsonl",
            "csv"
          ]
        },
        "description": "csv is metadata-only (opinions nest and rows do not); jsonl is the lossless default"
      },
      "q": {
        "name": "q",
        "in": "query",
        "required": true,
        "schema": {
          "type": "string",
          "maxLength": 256
        },
        "description": "The ranked query, same websearch syntax as text=; quote a phrase when you mean the phrase"
      },
      "sort": {
        "name": "sort",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "relevance",
            "citation_count"
          ]
        },
        "description": "relevance (default) is best text match, not importance; citation_count ranks a doctrine's leading cases"
      },
      "jurisdiction": {
        "name": "jurisdiction",
        "in": "query",
        "schema": {
          "type": "string"
        },
        "description": "F federal appellate, S state supreme, SA state appellate, FD federal district; unrecognized codes return an empty result"
      },
      "in_use": {
        "name": "in_use",
        "in": "query",
        "schema": {
          "type": "string",
          "enum": [
            "true",
            "false",
            "1",
            "0"
          ]
        },
        "description": "true for courts still hearing cases, false for retired ones, omit for all"
      },
      "similar_per_page": {
        "name": "per_page",
        "in": "query",
        "schema": {
          "type": "integer",
          "minimum": 1,
          "maximum": 200,
          "default": 10
        },
        "description": "Neighbourhood depth, default 10, max 200: deeper than every other endpoint on purpose, because similarity decays slowly. Out-of-range values are clamped and the response echoes what was used"
      }
    },
    "responses": {
      "Error": {
        "description": "Every error is JSON naming a machine-readable code, the fix in plain language, and where the guide explains it",
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/Error"
            }
          }
        }
      }
    },
    "schemas": {
      "Warnings": {
        "type": "array",
        "items": {
          "type": "string"
        },
        "description": "Present only when an unrecognized parameter was ignored; a warned 200 answered a different question than the one you meant to ask"
      },
      "Error": {
        "type": "object",
        "required": [
          "error",
          "message"
        ],
        "properties": {
          "error": {
            "type": "string",
            "enum": [
              "unauthorized",
              "key_expired",
              "key_revoked",
              "unknown_court",
              "court_not_found",
              "case_not_found",
              "export_not_found",
              "endpoint_not_found",
              "invalid_date",
              "invalid_cite",
              "invalid_integer",
              "invalid_name",
              "invalid_status",
              "invalid_cursor",
              "invalid_parameter",
              "invalid_query",
              "invalid_sort",
              "invalid_jurisdiction",
              "invalid_in_use",
              "invalid_include_text",
              "invalid_include_markup",
              "invalid_format",
              "query_too_long",
              "missing_query",
              "unsupported_parameter",
              "search_unavailable",
              "search_timeout",
              "search_busy",
              "stats_timeout",
              "query_timeout",
              "export_too_large",
              "export_not_ready",
              "export_expired",
              "too_many_active_exports",
              "rate_limited",
              "similar_unavailable"
            ]
          },
          "message": {
            "type": "string",
            "description": "Written to be self-explanatory; read it and follow its instructions"
          },
          "docs": {
            "type": "string"
          },
          "section": {
            "type": "string",
            "description": "The doc.txt heading to search for, verbatim; absent only on the catch-all 404"
          },
          "retry_after_seconds": {
            "type": "integer",
            "description": "On 429s only, beside the Retry-After header"
          },
          "query_params": {
            "type": "object",
            "additionalProperties": true,
            "description": "On export_expired only: re-POST these to regenerate the file"
          }
        }
      },
      "CaseListItem": {
        "type": "object",
        "description": "One case row: /cases items and every line of a metadata export are this one shape. Anything can be null except id",
        "properties": {
          "id": {
            "type": "integer"
          },
          "case_name": {
            "type": "string",
            "nullable": true
          },
          "date_filed": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "court": {
            "type": "string",
            "nullable": true
          },
          "citation_count": {
            "type": "integer",
            "description": "Citing opinions, not cases; runs a little above what cites= returns"
          },
          "precedential_status": {
            "type": "string",
            "nullable": true
          },
          "citation_depth": {
            "type": "integer",
            "description": "Only with exactly one of cites=/cited_by=: how many times the edge is drawn; the field to rank authorities by"
          }
        }
      },
      "SearchResult": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "case_name": {
            "type": "string",
            "nullable": true
          },
          "date_filed": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "court": {
            "type": "string",
            "nullable": true
          },
          "citation_count": {
            "type": "integer"
          },
          "precedential_status": {
            "type": "string",
            "nullable": true
          },
          "rank": {
            "type": "number",
            "description": "Comparable within one response only; text-match quality, not importance"
          },
          "snippet": {
            "type": "string",
            "nullable": true,
            "description": "Matches bolded with **word**, fragments joined with an ellipsis"
          },
          "citation_depth": {
            "type": "integer"
          }
        }
      },
      "CaseDetail": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "case_name": {
            "type": "string",
            "nullable": true
          },
          "case_name_full": {
            "type": "string",
            "nullable": true
          },
          "date_filed": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "court": {
            "type": "string",
            "nullable": true
          },
          "citation_count": {
            "type": "integer"
          },
          "precedential_status": {
            "type": "string",
            "nullable": true
          },
          "judges": {
            "type": "string",
            "nullable": true,
            "description": "Comma-separated, empty for roughly half of cases; do not rely on it"
          },
          "docket_id": {
            "type": "integer",
            "nullable": true
          },
          "opinions": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/OpinionSummary"
            }
          },
          "citations": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "This case's own reporter citations, ordered by citation type; match the reporter you want rather than taking position 0"
          },
          "warnings": {
            "$ref": "#/components/schemas/Warnings"
          }
        }
      },
      "OpinionSummary": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "type": {
            "type": "string",
            "description": "Sorts in reading order as strings: 010combined < 020lead < 030concurrence < 040dissent; a combined opinion usually contains the others"
          },
          "author": {
            "type": "string",
            "nullable": true
          },
          "page_count": {
            "type": "integer",
            "nullable": true
          }
        }
      },
      "TextOpinion": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "type": {
            "type": "string"
          },
          "author": {
            "type": "string",
            "nullable": true
          },
          "text_source": {
            "type": "string",
            "nullable": true
          },
          "text_length": {
            "type": "integer",
            "nullable": true
          },
          "text": {
            "type": "string",
            "nullable": true,
            "description": "Plain text; null when nothing is digitized. May carry *359-style U.S. Reports page markers"
          },
          "markup": {
            "type": "string",
            "nullable": true,
            "description": "Only with include_markup=true. Three dialects, one structureless; sanitize before rendering"
          },
          "markup_source": {
            "type": "string",
            "nullable": true,
            "description": "Only with include_markup=true"
          }
        }
      },
      "Parenthetical": {
        "type": "object",
        "properties": {
          "text": {
            "type": "string"
          },
          "score": {
            "type": "number",
            "nullable": true,
            "description": "0 to 1; above ~0.9 is reliably quotable"
          },
          "describing_case": {
            "type": "object",
            "properties": {
              "id": {
                "type": "integer"
              },
              "case_name": {
                "type": "string",
                "nullable": true
              },
              "date_filed": {
                "type": "string",
                "format": "date",
                "nullable": true
              }
            }
          }
        }
      },
      "Court": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "short_name": {
            "type": "string",
            "nullable": true
          },
          "full_name": {
            "type": "string",
            "nullable": true
          },
          "jurisdiction": {
            "type": "string",
            "nullable": true
          },
          "citation_string": {
            "type": "string",
            "nullable": true
          },
          "in_use": {
            "type": "boolean",
            "nullable": true
          },
          "start_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "end_date": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "warnings": {
            "$ref": "#/components/schemas/Warnings"
          }
        }
      },
      "MatchEstimate": {
        "type": "object",
        "properties": {
          "count": {
            "type": "integer"
          },
          "exact": {
            "type": "boolean",
            "description": "False means the count is a floor, not a total"
          }
        }
      },
      "ExportJob": {
        "type": "object",
        "properties": {
          "id": {
            "type": "integer"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "running",
              "completed",
              "failed"
            ]
          },
          "query_params": {
            "type": "object",
            "additionalProperties": true,
            "description": "Re-POST these to reproduce the job, including after a 410"
          },
          "record_count": {
            "type": "integer",
            "nullable": true
          },
          "file_size_bytes": {
            "type": "integer",
            "nullable": true
          },
          "corpus_snapshot": {
            "type": "string",
            "format": "date",
            "nullable": true,
            "description": "The snapshot the file was built from; cite it beside query_params. Null until the job starts"
          },
          "created_at": {
            "type": "string",
            "format": "date-time"
          },
          "completed_at": {
            "type": "string",
            "format": "date-time",
            "nullable": true
          },
          "download_url": {
            "type": "string",
            "nullable": true,
            "description": "Present only while completed and unexpired; files expire one day after finishing"
          },
          "error": {
            "type": "string",
            "nullable": true
          },
          "poll": {
            "type": "string",
            "description": "Present only while queued or running: the next step, spelled out"
          },
          "warnings": {
            "$ref": "#/components/schemas/Warnings"
          }
        }
      },
      "SimilarCase": {
        "type": "object",
        "description": "A CaseListItem plus its similarity to the case asked about",
        "properties": {
          "id": {
            "type": "integer"
          },
          "case_name": {
            "type": "string",
            "nullable": true
          },
          "date_filed": {
            "type": "string",
            "format": "date",
            "nullable": true
          },
          "court": {
            "type": "string",
            "nullable": true
          },
          "citation_count": {
            "type": "integer"
          },
          "precedential_status": {
            "type": "string",
            "nullable": true
          },
          "similarity": {
            "type": "number",
            "description": "Cosine similarity in [0,1], best first; a strong neighbour is ~0.75-0.85. Only comparable within one response, never across two cases' results"
          }
        }
      }
    }
  }
}
