Retrieve passages with /search, hand them to your model as numbered sources, and cite each source with the view_source_url Cashmere returned for it.
You need an API key. See Creating an API Key.
1. Search
curl "https://cashmere.io/api/v2/search?q=how+do+transformers+handle+long+context&limit=8" \
-H "Authorization: Bearer YOUR_API_KEY"
The response is a JSON array. Each result includes:
| Field | Use it for |
|---|---|
content |
The passage text to put in the prompt |
score |
Relevance, for filtering (see step 2) |
view_source_url |
The citation link |
omnipub_title, section_label, omnipub_publisher |
The human-readable citation |
omnipub_uuid, section_block_uuid |
Reading more of the source later |
limit defaults to 5. To get author names or the publication's external ID as well, add fields, repeated once per field:
curl "https://cashmere.io/api/v2/search?q=attention&fields=omnipub_creators&fields=omnipub_external_id" \
-H "Authorization: Bearer YOUR_API_KEY"
2. Drop weak results
Filter on score before building the prompt, so the model isn't handed passages that don't answer the question.
There is no universally right cutoff. Scores depend on the search model (search_model, or your key's default), and the right threshold depends on your content and your users' queries. Run a sample of your real queries, look at where the results stop being useful, and set the threshold there. Check it again if you change search models.
3. Build the prompt
Number each passage and keep its citation next to it:
import requests
resp = requests.get(
"https://cashmere.io/api/v2/search",
params={"q": question, "limit": 8},
headers={"Authorization": f"Bearer {API_KEY}"},
)
resp.raise_for_status()
results = [r for r in resp.json() if r["score"] >= MIN_SCORE] # MIN_SCORE: tuned in step 2
sources = "\n\n".join(
f"[{i}] {r['omnipub_title']} -- {r.get('section_label') or ''}\n{r['content']}"
for i, r in enumerate(results, start=1)
)
prompt = (
"Answer using only the sources below. Cite sources inline as [n]. "
"If the sources don't answer the question, say so.\n\n"
f"Sources:\n{sources}\n\nQuestion: {question}"
)
After the model answers, render each [n] as a link to results[n-1]["view_source_url"].
4. Cite with view_source_url, unmodified
Use view_source_url exactly as returned. Don't rebuild it, strip its query string, or swap in a URL you built from omnipub_uuid. It is a tracked short link: Cashmere checks access when it's followed, attributes the click to the search that produced it, and then redirects to wherever the publisher has configured (the passage on cashmere.io, or the publisher's own site). Don't assume it lands on cashmere.io.
Don't cache the link. A link stays valid for as long as nobody uses it. The first click starts a 24-hour window, and after that, visitors without their own Cashmere access get a "link expired" page. The window can't be extended. When you show the answer again later, run the search again to get fresh links instead of reusing stored ones. (Publications in collections marked "no license required" are exempt and their links keep working.)
Tracking end users
To see usage per user or session in usage reporting, pass external_ids, repeated once per ID:
curl "https://cashmere.io/api/v2/search?q=attention&external_ids=user123&external_ids=session456" \
-H "Authorization: Bearer YOUR_API_KEY"
See also
- AI Search on Licensed Content -- how
/searchworks - Controlling View Source Redirects -- where a
view_source_urllands - Deep Research over MCP -- read whole sections, not just passages