> For the complete documentation index, see [llms.txt](/llms.txt).

Hardware Hunter exposes a JSON API for agents and automation. This page documents every endpoint in the curated public spec, which is served machine-readable at [`/openapi.json`](/openapi.json) (and [`/api/public/openapi.json`](/api/public/openapi.json)), with authentication policies documented at [`/auth.md`](/auth.md).

## Base URL

```text
https://hardwarehunter.io
```

## Authentication

Authenticated endpoints use an API key sent as a Bearer token:

```http
Authorization: Bearer <api_key>
```

- You receive your API key at registration, and you can view or regenerate it anytime under **API access** in your [account settings](/account). Regenerating a key immediately invalidates the old one.
- Endpoints marked **Public** need no auth. Endpoints marked **Requires: Bearer API key** are scoped to your account and respect your tier.

## Rate limits

The effective application limit is **30 requests/minute/IP**.

Paid price-intelligence endpoints support dual-rail machine payments over [x402](/docs/paying-with-x402) and [MPP](/docs/paying-with-mpp): trusted benchmarks return HTTP 402 with self-describing payment instructions (~$0.01/query; inspect the live response for network and asset). The catalog and insufficient components are free.

## Paid price intelligence (x402 & MPP)

Per-query payment for trusted price data — no API key on the paid path. Supports both [x402](/docs/paying-with-x402) (Coinbase CDP/Base) and [MPP](/docs/paying-with-mpp) (Machine Payments Protocol by Tempo/Stripe via USDC).

- `GET /api/x402/pricing/catalog` — **free**: trusted components (each with `name` + `capacity`), strata, payment rails (`x402`, `mpp_tempo`), live per-query price, the data contract
- `GET /api/x402/pricing/component/{component_id}` — the paid query (~$0.01): cohort median/p25/p75, window, sample size, `benchmark_trust` metadata, `capacity`, and condition-stratified rows where published. Benchmarks are per exact SKU (model + capacity)

Payment flow: a trusted benchmark returns 402 with dual-rail challenges:

- **x402**: `PAYMENT-REQUIRED` header (Base64 JSON: scheme, network, asset, amount, pay-to); retry with `PAYMENT-SIGNATURE` carrying signed EIP-3009 transfer; server settles via facilitator and returns 200 with `PAYMENT-RESPONSE` receipt.
- **MPP**: `WWW-Authenticate: Payment ...` challenge header and RFC 9457 problem details; retry with `Authorization: Payment ...` header; server settles on Tempo and returns 200 with `Payment-Receipt` receipt.

## Public read-only endpoints

These are served outside the OpenAPI schema (they're `include_in_schema=False`), but they're stable and agent-friendly:

- `GET /api/public/prices` — hardware component categories with counts
- `GET /api/public/prices/{category}` — components + market summaries (p25/median/p75)
- `GET /api/public/prices/{category}/{component_id}` — specs, stats, observations
- `GET /api/public/deals` — current scored listings (ALERT/WATCH), filterable
- `GET /.well-known/webmcp` — WebMCP tool catalog
- `GET /tools.json` — machine-readable tool schema

## The endpoints

Below is the full curated spec, rendered live from `/api/public/openapi.json` — it cannot go stale.


## Hunts

### GET /hunts
_List all hunts for the current user_
- **Auth:** Requires: Bearer API key
- **Params:** `limit`, `offset`

### POST /hunts
_Create a new hunt_
- **Auth:** Requires: Bearer API key

### GET /hunts/results
_Paginated results feed across all hunts for the current user_
- **Auth:** Requires: Bearer API key
- **Params:** `filter`, `limit`, `offset`, `hunt_id`

Returns all scan results across all of the user's hunts, newest first.
Each row joins hunt_scan_results → listings → hunts, with alert_channel from alerts.

filter:
  all       — everything
  alerted   — action=ALERT
  near_miss — action=WATCH
  skipped   — action=SKIP

### DELETE /hunts/{hunt_id}
_Delete a hunt (hard delete)_
- **Auth:** Requires: Bearer API key
- **Params:** `hunt_id`

### GET /hunts/{hunt_id}
_Get a specific hunt_
- **Auth:** Requires: Bearer API key
- **Params:** `hunt_id`

### PATCH /hunts/{hunt_id}
_Update a hunt_
- **Auth:** Requires: Bearer API key
- **Params:** `hunt_id`

### GET /hunts/{hunt_id}/alerts
_List recent alerts fired for a hunt_
- **Auth:** Requires: Bearer API key
- **Params:** `hunt_id`, `limit`, `offset`

### GET /hunts/{hunt_id}/listings
_List listings evaluated for a hunt (recent alerts)_
- **Auth:** Requires: Bearer API key
- **Params:** `hunt_id`, `limit`, `offset`, `action`

Returns listings that were evaluated for this hunt.
Filter by action=ALERT|WATCH|SKIP or leave blank for all.

### POST /hunts/{hunt_id}/scan
_Trigger a manual scan for this hunt_
- **Auth:** Requires: Bearer API key
- **Params:** `hunt_id`

## Components & evaluation

### GET /api/v2/components/search
_Search Components_
- **Auth:** Requires: Bearer API key
- **Params:** `q`, `category`, `limit`, `cursor`

### GET /api/v2/components/{component_id}
_Get Component_
- **Auth:** Requires: Bearer API key
- **Params:** `component_id`

### GET /api/v2/components/{component_id}/history
_Get Price History_
- **Auth:** Requires: Bearer API key
- **Params:** `component_id`, `period`

### POST /api/v2/evaluate
_Evaluate_
- **Auth:** Requires: Bearer API key

Evaluate a listing: component-match diagnostics plus direct listing
facts. Market benchmarks are quarantined (2026-08-14), so the verdict is
``INCONCLUSIVE`` and all benchmark-derived fields are null until a trusted
comparable-listing cohort exists.

## Notification channels

### GET /users/me/channels
_List Channels_
- **Auth:** Requires: Bearer API key

### POST /users/me/channels
_Create Channel_
- **Auth:** Requires: Bearer API key

### DELETE /users/me/channels/{channel_id}
_Delete Channel_
- **Auth:** Requires: Bearer API key
- **Params:** `channel_id`

### PATCH /users/me/channels/{channel_id}
_Update Channel_
- **Auth:** Requires: Bearer API key
- **Params:** `channel_id`

### POST /users/me/channels/{channel_id}/test
_Test Channel_
- **Auth:** Requires: Bearer API key
- **Params:** `channel_id`

## Account & billing

### GET /account/me
_Get account details_
- **Auth:** Requires: Bearer API key

### GET /billing/status
_Current billing tier and subscription info_
- **Auth:** Requires: Bearer API key

Returns tier, Stripe IDs, and cached (≤60s) subscription status.

### GET /users/me/entitlements
_Get effective tier entitlements for the current user_
- **Auth:** Requires: Bearer API key

Effective cadence/cap contract for the authenticated user.

The dashboard uses this instead of reconstructing tier rules in
JavaScript. active_hunt_count includes hunts with a future paused_until
(a timed pause does not free the slot); remaining is clamped at zero so
grandfathered/over-cap data cannot produce a negative UI value.
