Overview
Citations turn a RAG answer from a claim into a checkable claim. With Claude, use the API’s native citations so the pointers are parsed and validated by the API; with other models, tag chunks with IDs and validate the markers yourself. Either way, cite every fact and render the sources as first-class UI. For the chunks that feed this step see rag-retrieval and rag-chunking.
Use native citations on Claude
Citations are generally available on the Claude API (no beta header) and supported by all active models; search results support all active models except Claude Haiku 3. Set citations: {"enabled": true} on each search_result or document block. Claude then returns text blocks with a citations list, and cited_text does not count toward output tokens.
Return retrieved chunks as search_result blocks inside a tool result, or place them as top-level user content:
{"type": "tool_result", "tool_use_id": tool_use.id, "content": [
{"type": "search_result", "source": c.url, "title": c.title,
"content": [{"type": "text", "text": c.text}],
"citations": {"enabled": True}}
for c in chunks
]}Each citation is a search_result_location with search_result_index, start_block_index, end_block_index, and cited_text. The text block is the smallest citable unit, so split a chunk into several content blocks for finer citations.
For whole documents, use document blocks with citations enabled:
| Source type | Chunking | Citation points to |
|---|---|---|
| Plain text | sentences | char_location, 0-indexed character range |
| sentences | page_location, 1-indexed page range | |
| Custom content | none (your blocks) | content_block_location, 0-indexed block range |
To let Claude cite single sentences of a retrieved chunk, pass the chunk as a plain-text document. To control granularity yourself, pass custom content blocks.
Respect the native-citation constraints
- Citations are all or nothing within a request: every document, or every search result, must have them enabled or all must have them disabled.
- Citations are incompatible with structured outputs. Enabling both (
output_config.format, or the deprecatedoutput_format) returns a 400 error. Pick one: native citations, or a JSON schema. - Within one
tool_result, if any block is asearch_result, every block must be. - Citations work with prompt caching; put
cache_controlon the top-level document blocks. The citation blocks in responses cannot be cached directly. See prompt-caching-strategies. - Enabling citations adds a small number of input tokens for system prompt additions and document chunking.
Fall back to chunk IDs when native citations do not fit
Use this path for models without native citations, or when you need a custom JSON schema from Claude. Give every retrieved chunk a stable ID and require the model to cite it.
Context:
[1] (source: postgres-17-release-notes.md#jit) ...
[2] (source: backend/postgres.md#tuning) ...
Answer with inline citations like [1] or [2] for each factual claim.
If the context does not answer the question, say so. Do not guess.Map each ID back to the chunk’s metadata (source_url, heading_path, chunk_index). For a JSON schema, return {"answer": "...", "citations": [{"chunk_id": "abc123", "supports": "..."}]} through structured outputs (see structured-output).
Cite every fact
If a claim cannot be cited, it should not be made. Every factual sentence ends with at least one citation; opinions and synthesis cite the chunks that ground them. Only meta-text (“Here is what I found”) is exempt. In a domain agent, “common knowledge” is still a retrieval target.
Validate prompt-based citations after generation
A model can invent a [4] that does not exist, or cite [1] for a claim [1] does not support. Strip any marker whose ID is not in the retrieved set, score faithfulness (did the cited chunk contain the claim?) with a judge or an overlap check, and retry or return a fallback when it fails. Native citations guarantee valid pointers to your documents, but whether the cited text supports the claim still belongs in the eval. See rag-eval.
Build verifiable links
Give each chunk a deep link in its metadata: source_url#heading-anchor, a line range for code, a page number for PDFs. A bare [1] forces the user to hunt for the passage.
Surface sources as first-class UI
Render each citation as a clickable chip (title - heading - last_updated). Show the cited text on hover so users see what the model saw, and open the deep link on click. When retrieval finds nothing, instruct the model to say it has no source, render that as an empty state with a “show what was retrieved” link, and log every refusal; a spike means retrieval changed.
Score citation behavior
Track three numbers per release: coverage (share of factual sentences with a citation), validity (share of markers or pointers that resolve to retrieved content), and faithfulness (share of citations whose source supports the claim). A change that lifts answer relevance while faithfulness drops is a regression.