# Tool endpoints

> Search, check and call paid data tools over plain HTTP from your own agent.

Building your own agent? Frames' tool search and pay-per-call data are also plain HTTP endpoints. They return the same results as the [MCP tools](https://frames.ag/docs/mcp-tools.md), so an agent can switch between the two without changes. There's no Frames model in the loop: your agent decides what to call.

## Endpoints

| Endpoint | What it does | Cost | Body |
| --- | --- | --- | --- |
| `POST /v1/tools/search` | Finds tools for what you describe. Top matches can come back ready to call, with a live check, a price and the input schema. | Free | `queries` (up to 4) or `query`; optional `capability` |
| `POST /v1/tools/probe` | Checks up to 5 tools: whether they're up, their price and their exact inputs | Free, within an hourly allowance shared with MCP | `ids` |
| `POST /v1/tools/invoke` | Calls up to 10 tools in parallel. Each call is checked, and calls that don't deliver are refunded. | Data that arrived, plus 15% | `calls` (each an `id` and `args`); optional `max_usd`, `objective`, `search_ids`, `idempotency_key` |
| `GET /v1/tools/spec` | Tool definitions to import into any function-calling framework | Free, no key needed | None |

All of them take the same `Authorization: Bearer <your key>` header as the rest of the [API](https://frames.ag/docs/api.md), except `/v1/tools/spec`.

## A typical flow

### Search the catalog

Free. Describe what your agent needs, in up to four phrasings.

```bash
curl -X POST https://api.frames.ag/v1/tools/search \
  -H "Authorization: Bearer $FRAMES_API_KEY" -H "Content-Type: application/json" \
  -d '{"queries": ["twitter user profile", "recent tweets by user"]}'
```

### Invoke the tools your agent picked

Paid. Pass the ids from the search as calls, with the `search_id` it returned and an `idempotency_key` so a retry can't pay twice.

```bash
curl -X POST https://api.frames.ag/v1/tools/invoke \
  -H "Authorization: Bearer $FRAMES_API_KEY" -H "Content-Type: application/json" \
  -d '{
    "calls": [{ "id": "<tool id from search>", "args": { "<input>": "<value>" } }],
    "search_ids": ["<search_id from step 1>"],
    "idempotency_key": "profiles-batch-001"
  }'
```

## What comes back

- **Search hits:** each has an `id`, `title`, `description` and payment details. Hits marked `invoke_ready` also carry a live check, `price_usd` and the tool's real `input_schema`, so they don't need a probe.
- **Invoke results:** one row per call with the response as the provider sent it, whether it delivered, what it cost and its receipt. Very large responses come back as a reference to read with `GET /v1/runs/{id}/tool-results`.
- **Billing:** a `billing` block with the credits charged, never more than the request's cap.

## Errors and retries

- **No automatic fallback:** if a provider rejects a call, the error comes back for your agent to handle. Frames doesn't swap in another tool.
- **Retries:** reuse the same `idempotency_key` and the batch is never paid twice.

[Troubleshooting](https://frames.ag/docs/troubleshooting.md#errors) lists the error codes and what to do about each.
