Introducing BackSearch | General Reasoning
Release24th July 2026<br>Introducing BackSearch<br>Letting agents search the web as it was
Language models are increasingly asked to predict the future. The obvious way to check whether they are any good at it is to ask them about a future that has already happened. But you can only do that if you can hold the world still.<br>Quantitative finance solved a version of this problem with backtesting: replay a strategy against history and see how it would have done. That works because the input to a trading strategy is a price series, and price series can be truncated at a date. An agent's input is the internet. It learns about the world by making web searches and synthesising various sources of information. To backtest an agent you need the state of the web at a point in time.<br>Live search APIs with a date filter are insufficient for this purpose. They rank today's index with hindsight, and hand you today's bytes for a page that may have been rewritten since the event resolved. This means there is a risk of leakage.<br>BackSearch is our answer: two endpoints for searching and fetching the web over a frozen news archive. Every request carries an as_of date. Search returns only documents crawled on or before it, and fetch returns the article's text as it was archived at that time. The corpus never moves, so the same query with the same as_of returns the same results forever.<br>We're releasing BackSearch today as a narrow preview : news domains only, covering December 2025 to July 2026. We plan to widen the domain coverage and expand the backdated period following initial feedback.
Quickstart<br>The base URL is https://search.openreward.ai. Authenticate with your existing OpenReward or_... key in the x-api-key header. There is no separate credential to obtain.
curl -X POST https://search.openreward.ai/search \<br>-H "x-api-key: $OPENREWARD_API_KEY" \<br>-H "Content-Type: application/json" \<br>-d '{<br>"query": "central bank statement on interest rates",<br>"as_of": "2026-01-15",<br>"k": 5<br>}'
Each hit comes back with the archived URL, title, snippet, host, and two dates:
"mode": "hybrid",<br>"candidates": 25,<br>"hits": [<br>"url": "https://www.thenationalnews.com/business/economy/...",<br>"title": "UAE Central Bank cuts interest rates after US Fed move",<br>"snippet": "The UAE Central Bank on Wednesday lowered its benchmark...",<br>"crawl_date": "2025-12-10T20:24:28Z",<br>"publish_date": "2025-12-10T00:00:00Z",<br>"host": "www.thenationalnews.com"
The one thing worth internalising: as_of gates on crawl_date, not on the article's own stated publish date. A page first archived after your cutoff will not be returned even if it claims to have been published before it. That is deliberate: a self-reported date is exactly the field a backdated crawl can't trust, and gating on the crawl is what guarantees nothing post-cutoff leaks in.<br>Fetch is the point-in-time counterpart. Give it a URL and a cutoff, and you get the extracted article text from the latest capture on or before that date: not today's version of the page.
curl -X POST https://search.openreward.ai/fetch \<br>-H "x-api-key: $OPENREWARD_API_KEY" \<br>-H "Content-Type: application/json" \<br>-d '{<br>"url": "https://example.com/article",<br>"as_of": "2026-01-15"<br>}'
Give it to a model<br>Here it is wired to GPT-5.6 Sol through the OpenAI Responses API:
import json, os, requests<br>from openai import OpenAI
AS_OF = "2026-01-15"<br>KEY = os.environ["OPENREWARD_API_KEY"]
def call(path, **body):<br>body["as_of"] = AS_OF<br>return requests.post(f"https://search.openreward.ai/{path}",<br>headers={"x-api-key": KEY},<br>json={k: v for k, v in body.items() if v}).json()
def web_search(query, allowed_domains=None, blocked_domains=None):<br>return call("search", query=query, k=5, allowed_domains=allowed_domains,<br>blocked_domains=blocked_domains)["hits"]
def web_fetch(url, prompt=None):<br>page = call("fetch", url=url, prompt=prompt, summarize=bool(prompt))<br># No capture on or before AS_OF: hand the model a soft error it can recover from<br>return page.get("text", "This page could not be retrieved.")[:4000]
TOOLS = [<br>{"type": "function", "name": "web_search",<br>"description":<br>"- Searches the web and returns a list of results to cite\n"<br>"- Pass allowed_domains to restrict the search, or blocked_domains to "<br>"exclude sites, never both\n"<br>"- Cite every URL you rely on as a markdown link",<br>"parameters": {"type": "object", "required": ["query"], "properties": {<br>"query": {"type": "string"},<br>"allowed_domains": {"type": "array", "items": {"type": "string"}},<br>"blocked_domains": {"type": "array", "items": {"type": "string"}}}}},<br>{"type": "function", "name": "web_fetch",<br>"description":<br>"- Fetches the content of a web page\n"<br>"- Takes a URL and a prompt describing what to extract from it\n"<br>"- Use it to read a page returned by web_search",<br>"parameters": {"type": "object", "required": ["url"], "properties": {<br>"url": {"type": "string"},<br>"prompt": {"type": "string"}}}},
TOOLBOX = {"web_search": web_search, "web_fetch": web_fetch}<br>client...