---
title: "Search opinions"
method: POST
path: "/search"
---

# Search opinions

`POST /search`

Search for opinions using semantic (vector), keyword (full-text), or hybrid search.

**Search Modes:**
- `semantic` - Vector similarity search using AI embeddings. Best for conceptual queries like "cases about breach of fiduciary duty". Returns results ranked by semantic similarity.
- `keyword` - Traditional full-text search using Elasticsearch. Supports Lexis/Westlaw-style boolean operators. Best for exact phrase matching and known terms. Returns results ranked by BM25 relevance.
- `hybrid` - Combines semantic and keyword search using reciprocal rank fusion (RRF) with deduplication. Best for comprehensive search coverage.

**Boolean Operators (keyword mode only):**
- `AND` - Both terms must appear (e.g., `negligence AND damages`)
- `OR` - Either term may appear (e.g., `breach OR default`)
- `NOT` - Exclude term (e.g., `contract NOT employment`)
- `"..."` - Exact phrase (e.g., `"summary judgment"`)
- `*` - Wildcard for any characters (e.g., `negligen*`)
- `?` - Single character wildcard (e.g., `wom?n`)
- `W/n` - Proximity search within n words (e.g., `negligence W/5 damages`)
- `()` - Grouping (e.g., `(contract OR agreement) AND breach`)

Note: Boolean operators must be UPPERCASE. They only work in `keyword` mode.

**Pagination:**
- Use `page` (1-indexed) and `page_size` for pagination
- Maximum `page_size` is 100
- Semantic search has limited deep pagination (~500 unique results max)
- Keyword and hybrid support deep pagination via Elasticsearch

## Request body

- SearchRequest
  - `query` string, required — Search query text
  - `mode` 'semantic' | 'keyword' | 'hybrid' — Search mode: - `semantic` - Vector similarity search (default) - `keyword` - Full-text search - `hybrid` - Combined semantic and keyword results using reciprocal rank fusion (RRF)
  - `page` integer — Page number (1-indexed)
  - `page_size` integer — Results per page (max 100)
  - `filters` SearchFilters — Optional filters to narrow search results
    - `court_ids` string[] — Filter by court IDs (e.g., "ca9", "scotus", "nysd")
    - `jurisdictions` string[] — Filter by jurisdiction type using canonical values from Midpage court metadata.
    - `states` string[] — Filter by state using full canonical names from Midpage court metadata.
    - `publish_status` 'published' | 'unpublished' | 'unknown' | 'in_chambers' | 'separate' | 'errata' | 'relating_to' — Filter by publication status. Use `unknown` to include opinions where publication metadata is missing or uncertain.
    - `date_filed` object — Filter by filing date range
      - `start` string, date — Start date (YYYY-MM-DD)
      - `end` string, date — End date (YYYY-MM-DD)
  - `include_facets` boolean — Include facet breakdowns in the response showing result counts by court, jurisdiction, state, and year. Defaults to `false`. Set to `true` to enable. **Note:** Facets are only available in `keyword` and `hybrid` modes (not `semantic`). In `hybrid` mode, facet counts reflect keyword search results only; semantic results are not included.

## Response `200`

Search results

- SearchResponse
  - `results` SearchResultItem[]
    - `opinion_id` string — Opinion identifier
    - `score` number — Relevance score. Scale varies by mode: - Semantic: 0-1 (cosine similarity) - Keyword: 0-100+ (BM25 score)
    - `case_name` string — Case name/caption
    - `midpage_url` string, uri — Canonical public Midpage opinion page. Use this URL instead of constructing a bare opinion-ID document URL.
    - `court_id` string — Court identifier
    - `court_name` string — Full court name
    - `court_abbreviation` string — Court citation abbreviation
    - `jurisdiction` string — Jurisdiction type
    - `state` string, nullable — State name (null for federal courts)
    - `publish_status` 'published' | 'unpublished' | 'unknown' | 'in_chambers' | 'separate' | 'errata' | 'relating_to' | 'null', nullable — Publication status, when available
    - `date_filed` string, date — Filing date (YYYY-MM-DD)
    - `docket_number` string, nullable — Court docket number, when available
    - `snippet` string — Relevant text excerpt from the opinion
    - `source` 'semantic' | 'keyword' — Source of this result. Always present in API responses: - semantic mode: "semantic" - keyword mode: "keyword" - hybrid mode: varies per item
  - `pagination` PaginationMeta
    - `page` integer — Current page number
    - `page_size` integer — Results per page
    - `total_results` integer — Total matching results (capped at 10,000 for keyword/hybrid)
    - `total_pages` integer — Total pages available
    - `has_next` boolean — Whether more pages exist
    - `has_prev` boolean — Whether previous pages exist
  - `metadata` object
    - `mode` 'semantic' | 'keyword' | 'hybrid' — Search mode used
    - `query` string — Original query
    - `processing_time_ms` integer — Server processing time in milliseconds
    - `boolean_query` object — Present in keyword mode when boolean operators are detected
      - `detected` boolean — Whether boolean operators were detected
      - `operators` string[] — List of detected operators (e.g., ["AND", "OR", "W/5"])
  - `facets` SearchFacets — Facet breakdowns showing result counts by various dimensions. Only present when `include_facets: true` in request and mode is `keyword` or `hybrid`. Counts are computed across the full result set (after filters), not just the current page.
    - `court_abbreviation` FacetBucket[] — Breakdown by court abbreviation (e.g., "9th Cir.", "S.D.N.Y.")
      - `key` string — The value (e.g., "9th Cir.", "Federal Appellate", "2024")
      - `count` integer — Number of documents matching this value
    - `jurisdiction` FacetBucket[] — Breakdown by jurisdiction type (e.g., "Federal Appellate", "State Trial")
      - `key` string — The value (e.g., "9th Cir.", "Federal Appellate", "2024")
      - `count` integer — Number of documents matching this value
    - `state` FacetBucket[] — Breakdown by state name
      - `key` string — The value (e.g., "9th Cir.", "Federal Appellate", "2024")
      - `count` integer — Number of documents matching this value
    - `year_filed` FacetBucket[] — Breakdown by filing year
      - `key` string — The value (e.g., "9th Cir.", "Federal Appellate", "2024")
      - `count` integer — Number of documents matching this value
    - `note` string — Explanatory note about facet limitations. Present in hybrid mode to indicate that facet counts reflect keyword search results only.

## Other responses

- `400` — Bad request
- `401` — Unauthorized

## Changes

- **2026-07-26** `8d436f13cd7e` — 1 info
  - added the optional property `results/items/midpage_url` to the response with the `200` status

[Change history](https://skmtc.dev/midpage/apis/midpage-data-services-api/changes/search/post.md)

---

[API](https://skmtc.dev/midpage/apis/midpage-data-services-api.md) · [All operations](https://skmtc.dev/midpage/apis/midpage-data-services-api/llms.txt) · [OpenAPI document](https://skmtc-service-production.skmtc.workers.dev/v1/apis/midpage/midpage-data-services-api/revisions/8d436f13cd7e/schema)
