About this tool
An independent, public finding aid for recent Michigan appellate decisions. It helps you locate documents; it does not write summaries, answer legal questions, or generate any text. It is not a product of, affiliated with, or endorsed by the Michigan courts, and nothing here is legal advice.
What is in the collection
| Set | Court | Period | Documents |
|---|---|---|---|
| orders | Supreme Court | 1987–2026 | 41,485 |
| unpublished opinions | Court of Appeals | 2016–2026 | 17,733 |
| orders | Court of Appeals | 2026 | 5,994 |
| published opinions | Court of Appeals | 2016–2026 | 2,002 |
| opinions | Supreme Court | 2000–2026 | 1,077 |
| other opinions (dissents, remand, reissued) | Court of Appeals | 2026 | 8 |
| Total | 68,299 | ||
Every document was retrieved from the Michigan Courts’ own case-search API at courts.michigan.gov — the Court of Appeals sets and Supreme Court opinions on August 25, 2026, the Supreme Court orders on August 26, 2026 — and every record links back to its official PDF. About 280 documents (almost all one-page orders, most of them from the 1980s and 1990s) were scanned images or had unreadable text layers and were converted with optical character recognition; those pages are flagged. Half of the Supreme Court orders carry no docket number in the court’s own index; for those, the case number was read from the face of the order itself.
How the collection is built
Every stage re-runs from the PDFs, so the collection can be rebuilt from scratch and the result checked against what is live. A gate between the passage-building stage and the database refuses the build if measured defect rates rise — passages that end mid-sentence, passages shorter than their own header, a separate opinion attributed to the wrong author.
The courts publish some decisions twice: a slip opinion on the decision date, then the reporter’s advance-sheets version reset for the Michigan Appeals Reports, and a consolidated appeal is filed identically under each of its docket numbers. 1,488 such copies are collapsed so a case appears once, and the surviving record answers to every docket number its copies carried.
How the documents are described
Each PDF was converted to text and read for the fields you can filter on: court, document type, publication status, decision date, docket and lower-court numbers, the parties in the caption, the trial court and county, the panel and authoring judge, the disposition (affirmed, reversed, remanded, leave denied…), and the statutes (MCL) and court rules (MCR) cited. Nothing is guessed by a model — every value comes from a pattern on the page or from the court’s own index.
Area of law is decoded from the two-letter case-type code the State Court Administrative Office assigns to every lower-court case number (for example 22-107852-CZ is a general civil case; 2023-001994-NA is a child protective case; FC and FH are felony prosecutions). Where a document has no lower-court number, the area is inferred from the caption (People v — criminal; In re … Minor — child welfare).
How search works
Three ways of finding a case run at once, and the results are merged:
- By number. A six-digit appellate docket, or a lower-court number like
22-107852-CZ, goes straight to an exact lookup — that document leads the results. - By name. Party names are matched against the case caption, so searching a party returns that case rather than every case that happens to mention the name.
- By exact phrase. Words in quotation marks must appear verbatim. The phrase decides which decisions are eligible and everything else ranks within them, so “every case using this language” is a question you can actually ask.
- By description. This is the one most people need. Describe what you remember about a case — the facts, the issue, a half-remembered phrase — and the system matches on meaning, not just matching words. “A dog bit a guest and the court ordered it euthanized” finds the case even though the opinion says “dangerous animal” and “euthanize” and never uses your phrasing.
The description search combines two signals. Every passage of every document is stored as a vector — a numeric representation of its meaning, produced by an embedding model (Qwen3-Embedding-4B) — and your description is turned into a vector the same way; passages whose vectors sit closest to yours are candidates. In parallel, a conventional full-text index finds passages containing your distinctive terms. The two candidate lists are fused, and a cross-encoder (Jina) re-reads the top candidates against your description and reorders them. All filters are applied inside the database query, so counts and results always agree.
Exact phrases and operators
The ordinary search box accepts the syntax you would expect, and the advanced search puts the exact phrase and the description in separate fields with an explanation of what each one does.
| You type | What it means |
|---|---|
“public building exception” | those words, in that order, verbatim |
-settlement | exclude decisions containing the word |
euthanized OR euthanasia | either word. Capitalised — a lower-case “or” inside a sentence is read as an ordinary word, because legal prose is full of it |
358358 | a docket or lower-court number goes straight to an exact lookup |
Tools and stack
Everything is open and inspectable. The pipeline is Python, the site is a Next.js application, and the whole index lives in one Postgres database — there is no separate search service, which is why a filter and a ranking can be expressed in a single query.
| Layer | What is used | Why |
|---|---|---|
| Collection | Python + requests, against the courts’ own case-search API | Public documents, fetched with a record of where and when |
| Text extraction | PyMuPDF; Tesseract for 293 scanned documents | The PDFs are born-digital, so a layout model would add cost without accuracy |
| Metadata | Python, pattern-based | No model guesses a field; every value traces to a pattern on the page |
| Embeddings | qwen/qwen3-embedding-4b at 1,024 dimensions, via OpenRouter | Measured against alternatives; 1,024 dimensions cost nothing detectable against the full width |
| Database | Postgres 17 with pgvector (HNSW over halfvec) and tsvector full text | Vectors, text and filters in one place, so filters run inside the query |
| Re-ranking | jina-reranker-v2-base-multilingual over the top 40 | Reorders candidates; currently under evaluation and may be removed |
| Site | Next.js 15 on Vercel; Supabase-hosted Postgres | — |
| Evaluation | 460-question benchmark, bootstrap confidence intervals, a build gate | Every quality claim is reproducible from the repository |
What it does not do well
These are measured, not suspected, and each is tracked in the open. Knowing them is part of using the tool.
- “Everything matching X” questions. “All of Judge Swartzle’s 2024 criminal opinions” is a database question wearing a sentence, and ranking by similarity cannot answer it: 14 of 20 such test questions score zero. Every field involved is already a filter, so the fix is a route from the search box to those filters. Issue 1.
- Judges who share a surname. The judge filter matches on surname, so Kelly returns Kirsten Frank Kelly and Michael J. Kelly together — 5,166 decisions belonging to two people. Four other collisions are confirmed. Issue 2.
- A described holding combined with a broad filter is slow — around twelve seconds when the index is cold, three when warm, against under four seconds for everything else.
- The benchmark has not yet been reviewed by a lawyer. The 460 questions were drafted with software and are being checked by hand; until that is finished, any accuracy figure is provisional.
Reading the results responsibly
- Unpublished Court of Appeals opinions are not binding precedent (MCR 7.215(C)(1)). Published opinions are.
- An order denying leave to appeal decides nothing about the merits.
- Opinions marked “for publication” remain subject to revision until they appear in the Michigan Appeals Reports.
Privacy and logging
Every search is recorded: the words you typed, any filters you applied, how many documents came back, and how long it took. There are no accounts and no cookies that identify you; your IP address is not stored, only a truncated one-way hash of it, which lets us count distinct visitors without knowing who they are. The point of the log is narrow — finding searches that return nothing, or that are too broad to narrow anything down, so the search can be improved. Please do not type anything into the search box you would not want recorded.
Contact and source
Built by Brian E. Perron, PhD, University of Michigan School of Social Work, as a companion to the Michigan Court Rules search. Found an error? Email beperron@umich.edu.