Context compiler
One call. Evidence, its structure, and a receipt.
ground takes an objective, a token budget, a freshness window and an evidence threshold, and returns the smallest defensible evidence set — each item tagged with what kind of source it is, where it originated, who owns it and whether it is independent of the others — plus a coverage receipt that says whether the set is enough.
What the agent reads instead of reasoning its way to
What the web holds
24 copies · 17 hosts
- 6 copies · 4 hostsgov.ukofficial · original
- 14 copies · 10 hostsThomson Reuterssecondary · wire
- 4 copies · 3 hostsA trade publicationsecondary · original
collapsed by origin and owner, not by host
receipt
status: sufficient
- origins
- 3
- hosts
- 17
- copies
- 24
- freshnesswithin 7d · newest 37 min
- missingprimary
- context1,420 / 3,000 tokens
- billed5 credits
curl -G https://api.unlob.com/ground \
-H "x-api-key: $UNLOB_API_KEY" \
--data-urlencode "objective=UK AI regulation position this week" \
-d max_age=7d \
-d min_independent_origins=2 \
-d token_budget=3000One request: an objective, a freshness window, the number of independent origins that counts as enough, and a token ceiling. The response below is the same call, annotated.
Where the budget goes
- Evidence packed by ground1,42047%
- Left for reasoning1,58053%
The response, annotated
The field names and the vocabulary are the real contract; the values are an example. The full shape is in the documentation.
{
1
"status": "sufficient",
"evidence": [
{
2
"source_role": "official",
"origin_type": "original",
"owner": "gov.uk",
"independence_score": 1.0,
"risk_flags": [],
"hit": { "url": "https://www.gov.uk/…", "snippet": "…" }
},
{ "source_role": "secondary", "origin_type": "wire", "owner": "Thomson Reuters", "…": "…" }
],
"coverage": {
"complete": true,
3
"independent_origins": 3, "hosts": 17, "copies": 24,
"freshness": { "max_age": 604800, "within_max_age": true },
4
"source_roles_missing": ["primary"],
"known_gaps": ["passages that declare no publish date are excluded by max_age"]
},
5
"budget": { "token_budget": 3000, "context_tokens": 1420, "credits_billed": 5 },
"next_actions": []
}
Read status first — sufficient, insufficient, stale, partial or empty — computed by a fixed rule, never by a model. Never treat partial as complete.
Provenance on every item — source_role says what kind of source; origin_type says whether it is the original, a wire dispatch, a press release or a republish; owner says who is responsible.
Hosts are not origins — 17 hosts and 24 copies collapsed to 3 independently owned origins. That is the number to hold a claim to.
The receipt says what is missing — Which source classes the set lacks, what was not searched, and the freshness of what was — in fields and in plain language.
Explicit budgets — A token ceiling you set, the tokens actually packed, and the 5 credits billed, however many stories were expanded.
Read status first
Five outcomes, computed by a fixed rule in a fixed order. The same corpus and the same request give the same status, because no model is involved anywhere in the call.
sufficientEnough independent origins, and at least one source in a required role (primary or official by default).
Answer from the evidence, citing the origins it lists.
insufficientEvidence exists but does not meet the bar you set: too few independent owners, or no source in a required role.
Say so, or follow next_actions: corroborate, search wider, or relax the requirement deliberately.
staleNothing inside max_age. Older material is listed separately as stale_evidence, never substituted.
Report that nothing is current, or widen_max_age if older evidence is acceptable.
partialPart of the index was unreachable, and the receipt names what was not searched.
Never treat the answer as complete. retry, or answer with the gap stated.
emptyNothing found for the objective.
Abstain. why_not explains whether a specific URL you expected was ever admitted.
Insufficient is an answer
Almost every retrieval layer is built to find something. Handed a short or one-sided set of results, a model does what it was trained to do and answers anyway — and the answer reads as confidently as one that was well supported.
ground is built to say when it has not found enough. You state the bar — which source roles must be present, how many independently owned origins, how recent — and the call reports insufficient, staleor partial when the evidence does not clear it, withnext_actions naming what would. A model that reads the status can abstain, ask, or search deliberately wider, instead of guessing.
That is why calibrated abstention — answering when the evidence supports it, declining when it does not — is the first metric in ouragent benchmark.
The reverse holds too: sufficient means the evidence cleared the bar you set — enough independent origins, a source in a required role, inside your freshness window — not that the claim is true. ground does not decide what is true; it reports what supports the claim and what is missing, and the judgement stays with your model.
Where ground sits in your agent loop
Seen from your side of the API. The agent asks once, reads the status before anything else, fetches the full text of only the item it chose, and hands the evidence to whichever model you run. unlob never writes the answer.
Five layers, one API
Each layer is a caller-observable contract. Together they are harder for a search wrapper to reproduce than any one of them — and they are a different thing from what a research agent optimises for.
| Layer | unlob's responsibility |
|---|---|
| Index | Cheap, fast, passage-level retrieval over an admission-controlled open-web index. |
| Evidence graph | Stories, origins, owners, hosts, entities, topics and the link structure — computed when the index is built. |
| Epistemic layer | Supported, contradicted, partial, stale, unknown, uncovered: what the evidence is, said out loud. |
| Context compiler | The smallest defensible evidence set for a task, within a token, latency and source-policy budget. |
| Temporal layer | What changed since an instant: new evidence, origins gained, an official source appearing, what you relied on disappearing. |
The distinction, stated plainly
A research agent
"Give us the task and we'll research it." The provider searches, reads, reasons and writes the answer on its own models, and attaches citations and confidence to what it concluded. Excellent when you want the provider's judgment; expensive, and the reasoning is theirs.
The evidence layer
"Bring whatever agent or model you want. We'll give it the smallest defensible evidence package, tell it exactly how that evidence is related, and tell it what we don't know." Epistemics as a retrieval primitive, before the reasoning model — deterministic, model-independent, and priced like the infrastructure it is.
The internal question every release is judged by: how little reasoning does the agent have to waste on deciding what evidence to trust and whether it has enough?
Frequently asked questions
Why does ground not generate an answer?
Because an answer is the one thing every other provider is racing to generate, on their own models, and it is the part that should belong to you. ground returns the evidence and its structure — who said it, who copied it, whether the sources are independent, what is missing — so whichever model you run reasons over it. That keeps the call cheap, predictable and model-independent, and it keeps unlob the layer beneath the reasoning rather than a competitor to it.
What does "independent origins" count?
Origins with distinct owners. Within a story, passages that carry the same wire dateline, the same press-release distributor, the same canonical URL or the same normalised title collapse into one origin; each origin is assigned an owner — the wire service, the media group from a curated table, or the registrable domain. Seventeen URLs of one Reuters story are one origin. Two outlets under one group are two origins with one owner, and count once.
How do I know the evidence is fresh enough?
Set max_age and it becomes a contract, not a preference: evidence published inside the window is evidence; outside it is reported as stale_evidence and the status is stale. Pages that declare no publish date are excluded by a window and the receipt says so. The API does not crawl on demand behind ground, and its capability catalogue says exactly that, so an agent can plan around it.
What are risk flags?
The web is an input channel into your agent. Every evidence item carries risk_flags when a passage looks like it is addressing an AI system rather than a reader — instruction-like text, hidden text, chat-role markers — or comes from a low-trust host. They flag rather than decide, and you can ask for flagged sentences to be stripped before the text reaches a context window.
Is the reasoning behind these labels a model?
No. Every label is deterministic and documented: the same corpus and the same request give the same status. Source roles come from host and content-type rules with a confidence; origins from wire, press-release, canonical-URL and title matching; independence from the ownership table. The one experimental part — relating evidence to a claim you supply as supports, contradicts, qualifies or unresolved — is a lexical heuristic and is labelled as such in the response.
Ground your first objective
10,000 free credits a month, no card. One call, and you can read the receipt before you believe anything on this page.