# Keenable quickstart: web search and page fetch API for AI agents

Keenable runs its own crawler, index and ranking. Web Search answers a query with ranked results that carry publish and index dates, and Page Fetch returns any page as Markdown. Each call is one request against prepaid Credits: $4 per thousand requests for a search, $4 per thousand requests for a page.

## At a glance

- **Search:** `POST https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/v1/zeroclick/search` with a JSON body such as `{"query": "...", "max_results": 5}`
- **Fetch:** `GET https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/v1/zeroclick/fetch?url=<the page, URL-encoded>`
- **Price:** Web Search $4 per thousand requests, Page Fetch $4 per thousand requests. A call that fails is not billed.
- **Access:** `Authorization: Bearer <token>` for an agent credential that holds Credits
- **Payment:** Credits from $0.50, paid over x402 or MPP (the USDC wallet rails) or by card
- **Response:** search returns `results` with `title`, `url`, `snippet`, `published_at`, `acquired_at`. Fetch returns `url`, `title`, `content` (Markdown), `author`, `published_at`.
- **Billing:** the `zc-billing` response header carries `chargedUsd` and `remainingCreditUsd`
- **Full purchase flow:** [the buyer skill](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/SKILL.md)

## Run a first search and a first page fetch

1. Get an agent credential and an access token. If you already hold one from any ZeroClick storefront, reuse it. Otherwise register a free one: [auth.md](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/auth.md) has the commands.

2. Buy Credits. Find the Credits plan and its `purchase.url` in [manifest.json](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/manifest.json). Then send the purchase with your bearer:

   ```bash
   curl -s -X POST "https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/plans/{planId}/purchase" \
     -H "Authorization: Bearer $ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"amountUsd": "<amount>"}'
   ```

   `amountUsd` is a whole-cent decimal string of at least $0.50, and at least $0.50 by card. The storefront answers with a 402 price quote. Nothing is bought until you pay it, so you can stop safely at the quote. [The buyer skill](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/SKILL.md) covers paying it.

3. Search the web:

   ```bash
   curl -s -X POST "https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/v1/zeroclick/search" \
     -H "Authorization: Bearer $ACCESS_TOKEN" \
     -H "Content-Type: application/json" \
     -d '{"query": "HTTP 402 Payment Required status code", "max_results": 5}'
   ```

   This example returned five results in 0.8 seconds, and the first is shown below.

   ```json
   {
     "query": "HTTP 402 Payment Required status code",
     "mode": "pro",
     "results": [
       {
         "title": "402 Payment Required",
         "url": "https://developer.mozilla.org/…",
         "description": "",
         "snippet": "# 402 Payment Required\nThe HTTP **`402 Payment Required`** [client error respons…",
         "published_at": "2026-07-15T11:43:47Z",
         "acquired_at": "2026-10-11T12:45:02Z"
       }
     ]
   }
   ```

   `snippet` holds query-relevant highlights. `published_at` is the publish date and `acquired_at` is when the page was indexed, both as ISO 8601 strings. `description` is deprecated and always empty. The example search drew $0.004 from credit.

4. Fetch a result as Markdown. URL-encode the page address once and pass it as `url`:

   ```bash
   curl -s "https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/v1/zeroclick/fetch?url=https%3A%2F%2Fkeenable.ai%2Fblog%2Fselect-the-tail-from-the-web&max_chars=3000" \
     -H "Authorization: Bearer $ACCESS_TOKEN"
   ```

   The response is a JSON object with the page as Markdown in `content`.

   ```json
   {
     "url": "https://keenable.ai/…",
     "title": "SELECT the tail FROM the web",
     "content": "## The model knows the best answer but gives the typical ones\n\nWhy doesn't the l…",
     "description": "",
     "author": "<author name>",
     "published_at": 1788393600
   }
   ```

   In Page Fetch, `published_at` is in Unix seconds. `url` can differ from the one you sent after redirects. `max_chars` truncates `content`, and its default is 50000. The example fetch drew $0.004 from credit.

5. Read the charge. Add `-i` to either call to print headers. A paid response carries:

   ```
   zc-billing: {"status":"settled","fundingSource":"credit","chargedUsd":"<debit>","remainingCreditUsd":"<balance>"}
   ```

   `chargedUsd` is what this call took from your Credits, and `remainingCreditUsd` is the balance after it. A delivered response with no `zc-billing` header billed nothing. `GET https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/agent/entitlements` with your bearer also lists your remaining credit.

## Search filters, live fetches, prompts and SELECT

| Need | Set | Documented in |
| --- | --- | --- |
| Results from one domain | `site` | [Web Search reference](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/services/search/openapi.json) |
| Recent results only | `published_after` (and `published_before`) | Web Search reference |
| Pages indexed in a window | `acquired_after`, `acquired_before` | Web Search reference |
| The web as it stood at a past moment | `query_time` (an upper bound, not a recency filter) | Web Search reference |
| Longer snippets | `snippet_max_length` (180 to 10000) | Web Search reference |
| The current page, not the indexed copy | `live=true` | [Page Fetch reference](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/services/fetch/openapi.json) |
| Only the answer to an instruction | `prompt` (up to 2000 characters) | Page Fetch reference |
| A table built from many pages | `POST https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/v1/zeroclick/select` with one read-only SQL `query`, at $0.10 per query that answers 200 | [SELECT query guide](https://select.keenable.ai/guide.md) |

## Costs and limits

- Every call draws on Credits held by your credential. Calls cannot be paid one at a time. A call without a bearer answers `401 bearer_required`, and a call without Credits answers `403 plan_required`.
- Each search and each fetch is one request. A call that fails with any non-2xx status is not billed.
- `max_results` runs from 1 to 50, and its default is 10. A search returns results, not page text: fetch each page you need to read.
- A search whose `query` is a URL is answered by fetching that page, and the response carries a `warning`.
- Claiming the credential to a person's verified email makes the Credits bought with it recoverable. [auth.md](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/auth.md) has the steps.

## Errors

| Status or code | Meaning | Action |
| --- | --- | --- |
| `401 bearer_required` | No agent credential on the call | Register, then retry with the bearer |
| `401 invalid_token` | The agent credential did not verify (`reason` is `token_expired` or `token_invalid`) | Exchange the stored assertion for a fresh token |
| `403 plan_required` | The credential holds no Credits (`reason`: `access_not_found`) | Buy Credits, then retry |
| `403 plan_required`, `reason`: `usage_exhausted` | The Credits balance is used up | Top up with `POST https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/extend` and `{"amountUsd": "<amount>"}`, or buy again |
| `400` | Invalid parameters | Fix the body or query string |
| `422` (fetch) | The page has no extractable content, or the target answered an HTTP error | Read `message` and try another page |
| `429` | Rate limited | Slow down, and honour `Retry-After` where given |
| `502`, `503`, `529` | The target was unreachable, the allowance could not be confirmed (nothing charged), or search is overloaded | Retry after `Retry-After` |

## Frequently asked questions

### Does the Keenable storefront need an API key?

No. Calls use the bearer token from the free agent credential that holds your Credits. The storefront forwards each call to Keenable, so no upstream key is involved.

### Can I pay for a single search without buying Credits?

No. Every search and fetch draws on Credits. The smallest purchase is $0.50, or $0.50 by card, and each search then draws $4 per thousand requests from it.

### What format are the dates in?

Web Search returns `published_at` and `acquired_at` as ISO 8601 strings. Page Fetch returns `published_at` in Unix seconds.

### How do I get only recent web results?

Set `published_after` to a date, and results are limited to pages published after it. `query_time` does not filter by recency. It searches the web as it stood at that moment and excludes newer pages.

### How much do a thousand searches and fetches cost?

A thousand searches cost $4, and a thousand searches plus a thousand fetches cost $8. Failed calls are not billed.

## Keep reading

- [Buyer skill](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/SKILL.md): the full purchase flow
- [Identity](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/auth.md): register, exchange and claim a credential
- [How to pay](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/payment.md): Credits, x402, MPP, card and top-ups
- [Error reference](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/errors.md): every storefront error code
- [Web Search reference](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/services/search/openapi.json): every search parameter
- [Page Fetch reference](https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/services/fetch/openapi.json): every fetch parameter

---

Updated 2026-11-04. All Keenable guides: https://zeroclick.keenable.ai/guides.md. Storefront: https://zeroclick.keenable.ai/zcj/nsu45kj0bmtb/llms.txt.
