Guides
Recency-weighted ranking
Bias search results toward recent documents with a single recency_weight parameter — so yesterday's note about a customer outranks a vague mention from six months ago, without throwing away semantic relevance.
By default, search ranks purely by semantic similarity: a six-month-old transcript and yesterday's compete on equal footing. For agent-memory and longitudinal use cases that is almost always wrong — the recent observation is usually the one you want. recency_weight lets you blend a recency signal into the ranking, server-side, with no extra ML and no measurable latency cost.
This is a re-ranking of the candidates the search already found — it never changes which documents match your filters, only the order of the top results. To restrict results to a window instead of just nudging the order, use the time filters (since / until / last_n_days); the two compose.
Recency is keyed to when a document was created. For documents that are edited in place, the sibling freshness parameters apply the same idea to when a document was last updated — covered at the end of this page.
How the score is computed
For each candidate hit, the server blends two scores in [0, 1]:
distance_score = cosine similarity of the matched passage, in [0, 1]
recency_score = exp(-age_days / half_life_days)
final_score = (1 - recency_weight) * distance_score
+ recency_weight * recency_score
recency_weightranges from0.0to1.0. At0.0(the default) ranking is pure similarity — identical to not passing the parameter at all. At1.0ranking is pure recency.0.3is a sensible starting point for memory use cases.recency_scoredecays exponentially with the document's age, measured from itscreated_attimestamp. Thehalf_life_daysparameter (default 30) sets how fast: at one half-life the recency contribution is halved, at two half-lives it is a quarter, and so on.
Recency score by age
How recency_score falls off for a few half-lives:
| Age of document | half_life_days = 7 | half_life_days = 30 | half_life_days = 90 |
|---|---|---|---|
| 0 days (just now) | 1.00 | 1.00 | 1.00 |
| 7 days | 0.50 | 0.85 | 0.95 |
| 30 days | 0.05 | 0.50 | 0.79 |
| 90 days | 0.00 | 0.13 | 0.50 |
| 365 days | 0.00 | 0.00 | 0.06 |
A short half-life (7 days) is aggressive — only the last week or two carries weight. A long half-life (90 days) decays gently across a quarter. Pick the window that matches how quickly your domain goes stale.
Worked example
Two documents match the query "what does the customer want for billing?" equally well on semantics, but one is from yesterday and one is from five months ago. With half_life_days = 30:
| Document | created_at | distance_score | recency_score | final_score (w = 0.0) | final_score (w = 0.3) |
|---|---|---|---|---|---|
| "Switch us to annual EUR billing" | 1 day ago | 0.82 | 0.98 | 0.82 (rank 1) | 0.87 (rank 1) |
| "We pay monthly in USD" | 150 days ago | 0.84 | 0.03 | 0.84 (rank 1) | 0.60 (rank 2) |
At recency_weight = 0.0 the older, very slightly closer match wins — and your agent tells the customer something that stopped being true months ago. At recency_weight = 0.3 the fresh document is promoted to the top, while a strong-enough old match would still hold its place. Recency nudges; it does not blindly sort by date.
Using it
recency_weight and half_life_days are accepted on the search endpoints. SDK users building an agent-memory layer get the same behavior through the Memory facade's recall(recency_weight=...), which is tuned for exactly this case.
# Server-side recency blend on the REST search endpoint
curl "https://api.aetherdb.ai/v1/search?q=billing%20preferences&k=5&recency_weight=0.3&half_life_days=30" \
-H "Authorization: Bearer $AETHER_API_KEY"
Recency re-ranks; time filters restrict
recency_weight re-orders the candidates that already matched your query and filters — it never drops a document. When you want to exclude anything older than a cutoff, combine it with a time filter: last_n_days=90 bounds the window (a hard pre-filter), and recency_weight=0.3 then biases what's left toward the most recent. The two are complementary.
Choosing a weight
| Goal | Suggested recency_weight | Notes |
|---|---|---|
| Pure semantic search (default) | 0.0 | Recency ignored; identical to omitting the parameter. |
| Agent memory / "what's true now" | 0.2 – 0.4 | Recent observations win ties and near-ties; strong old matches survive. |
| Activity feeds, "latest first" with relevance | 0.6 – 0.8 | Recency dominates, but the query still filters and orders within a period. |
| Strict newest-first | use list with a time filter | When you don't want similarity in the ranking at all, list by recency instead of searching. |
Out-of-range values are rejected: recency_weight must be in [0.0, 1.0] and half_life_days must be greater than 0, otherwise the request returns 400.
Freshness: boosting recently updated documents
recency_weight is keyed to created_at — when a document first entered the store. That is the right signal for append-only data such as notes, transcripts, and observations, where a document is written once and its creation time is the information's time. Living documents — runbooks, wiki pages, tickets, customer profiles — are different: they are edited in place, and a page created a year ago but revised yesterday is current, not stale. For those, blend in freshness_weight to boost by time since last update instead:
freshness_weightranges from0.0to1.0. At0.0(the default) freshness is off — identical to not passing the parameter at all.freshness_half_life_dayssets how fast the freshness boost decays, exactly likehalf_life_daysdoes for recency. It defaults to 14 and must be greater than0.
Freshness reads each hit's updated_at timestamp. A document that has never been updated has no updated_at, so freshness falls back to created_at — for never-updated documents, freshness and recency measure the same age.
Freshness ranking may require a Scale plan or higher.
Composing freshness with recency
The two weights share a single blend; whatever weight you don't assign stays on semantic similarity:
recency_score = exp(-days_since_created / half_life_days)
freshness_score = exp(-days_since_updated / freshness_half_life_days)
final_score = (1 - recency_weight - freshness_weight) * distance_score
+ recency_weight * recency_score
+ freshness_weight * freshness_score
Either weight can be used alone; together their sum must stay within 1.0. A request with recency_weight + freshness_weight > 1.0 returns 400, and — as with recency — an out-of-range weight or a non-positive half-life is also a 400.
Worked example
Two runbook pages match the query "database failover procedure" about equally well on semantics. With freshness_weight = 0.3 (no recency weight) and the default 14-day half-life:
| Document | created_at | Last updated | distance_score | freshness_score | final_score (fw = 0.0) | final_score (fw = 0.3) |
|---|---|---|---|---|---|---|
| "Failover runbook" | 300 days ago | 2 days ago | 0.80 | 0.87 | 0.80 (rank 2) | 0.82 (rank 1) |
| "Failover notes" | 10 days ago | never | 0.82 | 0.49 | 0.82 (rank 1) | 0.72 (rank 2) |
The freshly revised runbook wins even though it was created long ago — the case recency alone gets backwards: recency_weight would penalize the 300-day-old page despite yesterday's edit. The never-updated notes fall back to their creation age (10 days) for the freshness score.
Using freshness
freshness_weight and freshness_half_life_days are accepted everywhere the recency pair is — search, retrieve, vector search, and each batch-search query:
# Server-side freshness blend, keyed to updated_at
curl "https://api.aetherdb.ai/v1/search?q=failover%20procedure&k=5&freshness_weight=0.3&freshness_half_life_days=14" \
-H "Authorization: Bearer $AETHER_API_KEY"
Choosing a freshness_weight follows the same logic as choosing a recency weight: 0.2–0.4 promotes recently edited documents past ties and near-ties without letting a stale-but-relevant page vanish, while higher values make edit time dominate.
Next steps
- Filtering by time — restrict to a window with
since/until/last_n_days, which composes with recency weighting - Tuning retrieval — picking
k, score thresholds, entity and tag filters - Search & Retrieval API — full reference for every parameter and field