Search Strategies
Choosing a Search Type
The Moonlit API offers five search endpoints, each optimized for different use cases. Choosing the right one is the single most important decision for search quality: Keyword search: Best for known references (ECLI numbers, statute names, specific article numbers), Boolean queries with AND/OR/NOT, and exact phrase matching. Results are deterministic and fast. Use this when precision on specific terms matters more than discovering related concepts. Semantic search: Best for natural language questions and conceptual queries. Uses dense vector embeddings to find documents by meaning rather than exact word matches. Finds relevant results even when the terminology differs (e.g., searching in English finds Dutch-language judgments on the same concept). Semantic search + reranking: Same retrieval as semantic search, but applies a cross-encoder model to re-score the top results for higher precision. Adds 200-400ms latency but significantly improves the ordering of results. Use when the top 3-5 results must be highly accurate. Hybrid search: Combines keyword and semantic retrieval using Reciprocal Rank Fusion (RRF). This is the recommended default for most applications. It captures both exact term matches and conceptual relevance. Hybrid search + reranking: The highest-quality search mode. Combines both retrieval methods and reranks the fused results. Best for RAG pipelines, critical legal research, and any workflow where precision outweighs latency.
Search Decision Tree
Use this decision tree to pick the right endpoint for your use case: 1. Do you have an exact ECLI number, case number, or statute reference? -> Keyword search (it will match the exact identifier) 2. Are you building Boolean queries with AND/OR/NOT operators? -> Keyword search (only keyword search supports Boolean syntax) 3. Is the user typing a natural-language question? -> Hybrid search (combines the best of both approaches) 4. Do you need the absolute best precision for the top 5 results? -> Hybrid search + reranking (adds ~300ms but dramatically better ordering) 5. Are you building an autocomplete or suggestion feature? -> Keyword search with short queries (fastest response time) 6. Are you building a RAG pipeline for an LLM? -> Hybrid search + reranking (best context quality for generation) 7. Are you exploring a legal concept you cannot describe with keywords? -> Semantic search (finds conceptually related documents across languages) 8. General-purpose search for a UI? -> Hybrid search with default semantic_weight (0.6) When in doubt, start with hybrid search. It performs well across all query types and is the safest default.
Boolean Syntax for Keyword Search
The keyword search endpoint supports Boolean operators for precise queries. These operators must be uppercase: AND: Both terms must appear. This is the default operator when no operator is specified between terms. Example: GDPR AND enforcement AND Netherlands OR: Either term may appear. Use to broaden your search. Example: (opzegging OR ontbinding) AND huurovereenkomst NOT: Exclude documents containing the term. Example: arbeidsrecht AND NOT ambtenaar Quotes: Exact phrase matching. The entire phrase must appear as written. Example: "right to be forgotten" AND GDPR Parentheses: Group sub-expressions to control operator precedence. Example: ("Article 17" OR "Article 85") AND GDPR AND (fine OR penalty) Common patterns: - Search by ECLI: "ECLI:NL:HR:2024:1234" - Search by case number: "22/04567" - Search by article: "Article 6" AND GDPR AND "legitimate interest" - Exclude a jurisdiction's procedural cases: bestuursrecht AND NOT "voorlopige voorziening"
{
"query": "("arbeidsrecht" OR "employment law") AND NOT "interim"",
"jurisdictions": ["Netherlands"],
"documentTypes": ["case_law"],
"sort_type": 1,
"num_results": 20
}Semantic Weight Tuning
In hybrid search, the semantic_weight parameter controls the balance between keyword and semantic components. It accepts a value between 0.0 and 1.0: 0.0 = Pure keyword (identical results to the keyword endpoint) 0.3 = Keyword-heavy hybrid (good for queries with specific legal terms) 0.6 = Slightly semantic-leaning balance (default, recommended starting point) 0.7 = Semantic-heavy hybrid (good for natural-language questions) 1.0 = Pure semantic (identical results to the semantic endpoint) Practical tuning guidelines: Lower the weight (0.2-0.4) when: - Users search for specific article numbers ("Article 267 TFEU") - Queries contain ECLI numbers or case references - Exact terminology matters (statutory definitions) Keep the default (0.6) when: - You have a mix of query types in your application - You are unsure what users will search for Raise the weight (0.6-0.8) when: - Users ask questions in natural language - You want cross-language discovery (Dutch query finding German cases) - The legal concept is more important than specific terms You can A/B test different weights to find the optimal value for your specific user base.
{
"query": "liability for defective AI systems in healthcare",
"semantic_weight": 0.7,
"jurisdictions": ["European Union", "Netherlands", "Germany"],
"documentTypes": ["case_law", "legislation"],
"num_results": 10
}When to Use Reranking
The reranked variants of semantic and hybrid search apply a cross-encoder model to re-score the initial results. Cross-encoders are more accurate than bi-encoders (used in the first retrieval stage) because they jointly encode the query and document together, but they are also slower. Use reranking when: - You are building a RAG pipeline and the top 3-5 results must be the most relevant (bad retrieval = bad generation) - Users are performing detailed legal research where the first result matters - You are comparing across jurisdictions and need the best match per country - You are doing compliance checks where missing a relevant regulation is costly Skip reranking when: - You are building a fast search UI where sub-200ms response time matters - You are paginating through many results (reranking only helps the top results) - You are doing exploratory/browse-style search where approximate relevance is fine - You are making many parallel requests and want to minimize API latency Latency impact: reranking adds approximately 200-400ms to the response time. For most legal research applications, this tradeoff is worthwhile.
Using Facets for Dynamic Filtering
Set facets: true in any search request to receive aggregate counts alongside your results. Facets show how many documents match your query broken down by jurisdiction, document type, source (court), and field of law. Facets are essential for building search UIs with dynamic filter counts. Instead of showing static filter options, you can show users how many results exist for each filter value, and disable options with zero results. Facets do not affect the search results themselves -- they are computed in parallel and returned in a separate facets object in the response. They are available on all five search endpoints. A common pattern: run the initial search with facets enabled to show counts, then let users click on a facet value to add it as a filter. The next search request includes that filter and returns updated facet counts for the remaining values.
{
"query": "platform liability user-generated content",
"jurisdictions": ["European Union", "Netherlands", "Germany"],
"facets": true,
"num_results": 10
}
// Response includes:
// "facets": {
// "jurisdictions": { "European Union": 245, "Netherlands": 89, "Germany": 134 },
// "document_types": { "case_law": 312, "legislation": 87, "parliamentary": 69 },
// "sources": { "CJEU": 98, "Hoge Raad": 34, "BGH": 67, "Raad van State": 23 }
// }