> ## Documentation Index
> Fetch the complete documentation index at: https://docs.openreward.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Backdated Search & Fetch API

> A point-in-time web search and page-fetch API over a frozen news archive — query the web as it existed on or before any past date, reproducibly.

## Overview

The Backdated Search & Fetch API answers one question no live search API can:
**"what did the web know on date X?"**

Every request carries an `as_of` date. Search returns only documents crawled on
or before that date, and fetch returns the article's bytes **as they were
archived at that time** — not today's version of the page. The backing corpus is
a frozen archive, so the same `as_of` query returns the same results forever.

This is the same engine behind the [`BackSearchToolset`](/environments/backdated-web-tools)
you can declare in an environment — exposed here as a standalone HTTP API for use
from any language or agent loop.

<Info>
  **Why not a live search API with a date filter?** Live APIs (Brave, Exa,
  Perplexity, Anthropic, etc.) filter *today's* index by a publish-date field and
  then hand you *today's* bytes — so ranking is computed with hindsight and a page
  edited since the cutoff leaks its later content. This API never touches the live
  web: it searches an immutable crawl, so results are **reproducible** and carry
  **no post-cutoff lookahead**. That makes it suitable for leakage-free evals,
  forecasting benchmarks, and defensible point-in-time backtests.
</Info>

## Base URL & authentication

```
https://search.openreward.ai
```

Authenticate with your OpenReward API key in the `x-api-key` header — the same
`or_...` key the rest of the platform uses. No separate key is needed.

```bash theme={null}
export OPENREWARD_API_KEY="or_xxxxxxxx"
```

<Note>
  Search runs over a fixed historical corpus. Choose `as_of` dates **inside the
  covered window** — dates outside it return no results. Current coverage:
  **December 2025 → June 2026**, news sources, rolling forward roughly one month
  behind real time. Confirm the live window with the OpenReward team.
</Note>

## Pricing

Pay-as-you-go against your prepaid OpenReward balance — no subscription.

| Endpoint       | Price                                   |
| -------------- | --------------------------------------- |
| `POST /search` | \*\*$10 / 1,000 requests** ($0.01 each) |
| `POST /fetch`  | \*\*$2 / 1,000 requests** ($0.002 each) |

Only successful requests are billed. A search that errors, or a fetch that finds
no capture on or before `as_of` (a `404`), is **not** charged. When your balance
is exhausted the API returns [`402 Payment Required`](#budget--errors) — top up
to continue. Track usage with `orwd usage` or `GET /v1/billing/api-usage`.

***

## POST /search

Full-text + semantic search over the archive, cut off at `as_of`.

### Request body

| Field             | Type      | Required | Description                                                                                                                 |
| ----------------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------- |
| `query`           | string    | **yes**  | The search query.                                                                                                           |
| `as_of`           | string    | **yes**  | Point-in-time cutoff, ISO `YYYY-MM-DD`. Only documents crawled on or before this date (end-of-day, inclusive) are returned. |
| `k`               | integer   | no       | Number of hits to return. Default `10`.                                                                                     |
| `mode`            | string    | no       | `hybrid` (BM25 + E5 vector fusion, best quality — default), `bm25` (fast lexical), or `dense` (pure vector).                |
| `lang`            | string    | no       | ISO-639-1 language filter, e.g. `en`.                                                                                       |
| `site`            | string    | no       | Restrict to a single host, e.g. `reuters.com`.                                                                              |
| `allowed_domains` | string\[] | no       | Only return results from these hosts. Mutually exclusive with `blocked_domains`.                                            |
| `blocked_domains` | string\[] | no       | Exclude these hosts.                                                                                                        |
| `sort`            | string    | no       | `relevance` (default) or `newest`.                                                                                          |

### Response

```json theme={null}
{
  "mode": "hybrid",
  "candidates": 25,
  "hits": [
    {
      "url": "https://example.com/article",
      "title": "Article title",
      "snippet": "first ~200 chars of article text",
      "crawl_date": "2025-12-01T17:33:40Z",
      "publish_date": "2025-12-01T00:00:00Z",
      "host": "example.com",
      "score": 0.976
    }
  ],
  "timing": { "total_ms": 587.3 }
}
```

| Field                 | Description                                                                                   |
| --------------------- | --------------------------------------------------------------------------------------------- |
| `hits[].crawl_date`   | When the page was archived. **This is what `as_of` gates on** — never later than your cutoff. |
| `hits[].publish_date` | The article's own stated publish date (best-effort; may be null).                             |
| `hits[].score`        | Rank pseudo-score in `[0,1]` (`1.0` = top hit). In `hybrid` it's the fused score.             |
| `candidates`          | Unique-URL candidates considered before rerank (meaningful in `hybrid`).                      |

### Example

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    curl -X POST https://search.openreward.ai/search \
      -H "x-api-key: $OPENREWARD_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "query": "central bank statement on interest rates",
        "as_of": "2026-01-15",
        "k": 5,
        "mode": "hybrid"
      }'
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os, requests

    resp = requests.post(
        "https://search.openreward.ai/search",
        headers={"x-api-key": os.environ["OPENREWARD_API_KEY"]},
        json={
            "query": "central bank statement on interest rates",
            "as_of": "2026-01-15",
            "k": 5,
            "mode": "hybrid",
        },
    )
    for hit in resp.json()["hits"]:
        print(hit["crawl_date"], hit["title"], hit["url"])
    ```
  </Tab>
</Tabs>

<Tip>
  `hybrid` handles paraphrase that lexical search misses — a query for
  *"Eastern European territorial dispute escalation"* surfaces Russia/Ukraine
  coverage even when those keywords aren't present.
</Tip>

***

## POST /fetch

Retrieve a page's content **as it was archived on or before `as_of`** — the
point-in-time counterpart to search. Returns the extracted article text, not
today's version of the URL.

### Request body

| Field          | Type    | Required | Description                                                             |
| -------------- | ------- | -------- | ----------------------------------------------------------------------- |
| `url`          | string  | **yes**  | The page to fetch.                                                      |
| `as_of`        | string  | **yes**  | Cutoff `YYYY-MM-DD`. Returns the latest capture on or before this date. |
| `summarize`    | boolean | no       | If `true`, also return an LLM summary of the page.                      |
| `prompt`       | string  | no       | Guiding prompt for the summary (with `summarize: true`).                |
| `include_html` | boolean | no       | Include the raw archived HTML alongside extracted text.                 |

### Response

```json theme={null}
{
  "url": "https://example.com/article",
  "crawl_date": "2026-01-14T09:12:03Z",
  "publish_date": "2026-01-14T00:00:00Z",
  "title": "Article title",
  "text": "full extracted article text as archived…",
  "summary": null,
  "host": "example.com"
}
```

If there is no capture of the URL on or before `as_of`, the API returns `404`
(and does not bill the request).

### Example

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    curl -X POST https://search.openreward.ai/fetch \
      -H "x-api-key: $OPENREWARD_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "url": "https://example.com/article",
        "as_of": "2026-01-15"
      }'
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={null}
    import os, requests

    resp = requests.post(
        "https://search.openreward.ai/fetch",
        headers={"x-api-key": os.environ["OPENREWARD_API_KEY"]},
        json={"url": "https://example.com/article", "as_of": "2026-01-15"},
    )
    page = resp.json()
    print(page["crawl_date"], page["title"])
    print(page["text"][:500])
    ```
  </Tab>
</Tabs>

<Warning>
  `as_of` gates on **crawl date**, not the article's stated publish date. A page
  that was first archived after your cutoff will not be returned even if it claims
  an earlier publish date — this is deliberate, and it's what guarantees no
  post-cutoff content leaks in.
</Warning>

***

## Budget & errors

| Status | Meaning                                                                                             |
| ------ | --------------------------------------------------------------------------------------------------- |
| `200`  | Success (billed).                                                                                   |
| `400`  | Bad request — e.g. both `allowed_domains` and `blocked_domains` set, or an invalid URL. Not billed. |
| `401`  | Missing `x-api-key`.                                                                                |
| `403`  | Invalid or disabled API key.                                                                        |
| `402`  | **Balance exhausted.** Top up to continue. Not billed.                                              |
| `404`  | (fetch) No capture of the URL on or before `as_of`. Not billed.                                     |

## Checking your usage

`GET https://api.openreward.ai/v1/billing/api-usage` returns metered search and
fetch usage for the account behind your API key — requests, billable cost, and
anything metered-but-not-yet-priced (`pendingRequests`).

### Query parameters

| Field         | Type    | Description                                           |
| ------------- | ------- | ----------------------------------------------------- |
| `days`        | integer | Window to report, 1–90. Default `30`.                 |
| `service`     | string  | Filter to `search` or `fetch`. Omit for both.         |
| `granularity` | string  | `total` (default) or `daily` for a per-day breakdown. |

### Response

```json theme={null}
{
  "since": "2026-06-21T00:00:00.000Z",
  "until": "2026-07-21T08:11:29.856Z",
  "byService": [
    { "service": "search", "requests": 1200, "units": "1200.000", "cost": "12.000000", "pendingRequests": 0 },
    { "service": "fetch",  "requests": 300,  "units": "300.000",  "cost": "0.600000",  "pendingRequests": 0 }
  ],
  "totals": { "requests": 1500, "cost": "12.600000", "pendingRequests": 0 }
}
```

<Note>
  `cost` counts only usage that has already been priced. Requests from the last few
  minutes show under `pendingRequests` with `cost: 0` until the rater runs — so the
  request count is always live even when the charge hasn't landed yet.
</Note>

<Tabs>
  <Tab title="curl">
    ```bash theme={null}
    curl "https://api.openreward.ai/v1/billing/api-usage?days=30&granularity=daily" \
      -H "x-api-key: $OPENREWARD_API_KEY"
    ```
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    orwd usage                 # summary for the last 30 days
    orwd usage --daily         # broken down by day
    orwd usage --service search
    ```
  </Tab>
</Tabs>

## Adding credits

**Top up from the dashboard** — the simplest path. Open your organization's
billing page at `openreward.ai/<your-org>/billing` (for example,
[openreward.ai/GeneralReasoning/billing](https://openreward.ai/GeneralReasoning/billing)),
choose an amount, and pay by card. The credit is available immediately.

**Top up via the API** — create a Stripe checkout session and send the user to
the returned URL:

```bash theme={null}
curl -X POST https://api.openreward.ai/v1/billing/checkout \
  -H "x-api-key: $OPENREWARD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "amount": "25.00", "currency": "usd" }'
# -> { "checkoutUrl": "https://checkout.stripe.com/...", "sessionId": "cs_..." }
```

Check your remaining balance any time:

```bash theme={null}
curl https://api.openreward.ai/v1/billing/balance \
  -H "x-api-key: $OPENREWARD_API_KEY"
# -> { "balance": "24.94", "currency": "usd" }
```

<Note>
  An **org-scoped** API key draws down the organization's shared balance; a
  personal key draws down your own. Top up whichever the key belongs to.
</Note>

## See also

* [Backdated Web Tools](/environments/backdated-web-tools) — the same engine as
  an environment toolset (`BackSearchToolset`) or SDK classes (`Backsearch` /
  `Backfetch`).
