# Microquery API Reference

Base URL: `https://microquery.dev`

All amounts are in **micro-USDC** (1 USDC = 1,000,000 micro-USDC).

______________________________________________________________________

## Getting Started

```bash
# 1. Register — get an API key and $0.10 free trial credit
curl -X POST https://microquery.dev/v1/register \
  -H "Content-Type: application/json" \
  -d '{"name": "my-agent"}'
# → { "id": "...", "api_key": "a3f1b2c9...", "balance": 100000 }

# 2. Query
curl -G https://microquery.dev/query \
  -H "Authorization: Bearer YOUR_API_KEY" \
  --data-urlencode "database=nvd" \
  --data-urlencode "query=SELECT id, description FROM cve WHERE cvss_score > 9 LIMIT 5"
# → ndjson rows + X-Microquery-Cost-MicroUSDC and X-Microquery-Balance-MicroUSDC headers
```

No wallet required. The trial credit covers thousands of small queries. When the
trial runs out, [add a wallet and deposit USDC](#deposits).

______________________________________________________________________

## Registration

`POST /v1/register` — public, no authentication required.

| Field         | Type   | Required | Description                                                                                                                                 |
| ------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`        | string | yes      | Display name, max 64 characters                                                                                                             |
| `wallet_addr` | string | no       | Ethereum wallet address (`0x` + 40 hex chars). Optional at registration — enables account recovery and deposits. See [Deposits](#deposits). |

**Response `201 Created`:**

```json
{
  "id": "acct_...",
  "api_key": "...",
  "name": "my-agent",
  "balance": 100000,
  "wallet_addr": "0x...",
  "first_query": {
    "database": "nvd",
    "sql": "SELECT id, description FROM cve WHERE cvss_score > 9 LIMIT 3",
    "note": "Trial balance covers all example scripts. Execute via GET /query?database=nvd&query=..."
  },
  "deposit_instructions": {
    "chain": "Base",
    "token": "USDC",
    "minimum_micro_usdc": 250000,
    "endpoint": "POST /v1/deposit",
    "method": "EIP-2612 permit: sign permit off-chain, send amount+deadline+v+r+s with Bearer auth",
    "docs_url": "https://microquery.dev/docs.md#deposits"
  },
  "quickstart_url": "https://microquery.dev/v1/agent-quickstart"
}
```

- Every new account starts with **100,000 micro-USDC ($0.10)** free trial
  credit.
- Rate limited to **3 accounts per IP per 24 hours**.
- Store the `api_key` securely — it is shown only once. Agents with a linked
  wallet can recover their account ID via `GET /v1/wallets/{addr}`.

### Agent bootstrap endpoint

`GET /v1/agent-quickstart` — public, no authentication required.

Returns a self-contained JSON document designed to be injected directly into an
agent's context. The response describes every step needed for an autonomous
agent to register, run its first query, and deposit funds — without consulting
external documentation.

```bash
curl https://microquery.dev/v1/agent-quickstart
```

The response includes `service`, `registration`, `authentication`, `query`,
`first_query`, `pricing`, `deposit`, `datasets`, and a `discovery` section
documenting `GET /v1/databases`. This endpoint is the canonical MCP tool
manifest entry point.

______________________________________________________________________

## Authentication

### Bearer token

```
Authorization: Bearer YOUR_API_KEY
```

The standard method. Use this for all queries once you have an API key from
`POST /v1/register`. The Bearer token is a stable credential — no per-query
signing required.

### EIP-712 signed authorization (advanced — per-query spending caps)

```
Authorization: EIP712 BASE64_ENCODED_PAYLOAD
```

For agents that want a cryptographic per-query spending cap. Each request
carries a signed authorization binding the query to a maximum cost — the server
rejects if the actual scan would exceed `max_cost`. Requires a linked wallet.
Most agents do not need this; the deposit balance is the effective spending cap.

The payload is a base64-encoded JSON object:

```json
{
  "consumer":   "0xYOUR_WALLET",
  "max_cost":   "5000",
  "database":   "nvd",
  "query_hash": "0x...",
  "nonce":      1,
  "deadline":   1234567890,
  "signature":  "0x..."
}
```

______________________________________________________________________

## Querying

`GET /query` or `POST /query`

| Parameter  | Description                           |
| ---------- | ------------------------------------- |
| `database` | Database name (e.g., `nvd`, `pubmed`) |
| `query`    | SQL query string                      |

**Example:**

```bash
curl -G https://microquery.dev/query \
  -H "Authorization: Bearer YOUR_API_KEY" \
  --data-urlencode "database=pubmed" \
  --data-urlencode "query=SELECT title FROM baseline LIMIT 10"
```

Response is **newline-delimited JSON** (`application/x-ndjson`), one object per
row.

For agents without a pre-funded account that want to pay per query without
registration, see [Advanced: x402](#advanced-x402).

### Response headers

| Header                           | Description                                                                                                           |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
| `X-Microquery-Cost-MicroUSDC`    | Actual cost of this query in micro-USDC                                                                               |
| `X-Microquery-Balance-MicroUSDC` | Remaining balance after this query in micro-USDC                                                                      |
| `X-Microquery-Bytes-Scanned`     | Decompressed bytes scanned                                                                                            |
| `X-Microquery-Query-ID`          | Opaque query identifier                                                                                               |
| `X-Microquery-Auto-Limit`        | Set to `1000` when a LIMIT clause was automatically injected — add an explicit `LIMIT` to your query to suppress this |
| `X-Microquery-Hard-Limit`        | Set to `10000` when your explicit LIMIT was clamped to the server maximum                                             |
| `X-Payment-Transaction`          | On-chain settlement tx hash (x402 per-query only)                                                                     |
| `X-Payment-Account-Key`          | `api_key` for this wallet — returned on first x402 per-query payment only                                             |

### HTTP status codes

| Code  | Meaning                                                                                                                    |
| ----- | -------------------------------------------------------------------------------------------------------------------------- |
| `200` | Query executed; results follow                                                                                             |
| `402` | Either insufficient balance (`{"error":"insufficient balance",...}`) or x402 challenge (`{"x402Version":1,"accepts":[…]}`) |
| `401` | Missing authorization                                                                                                      |
| `403` | Invalid token or signature                                                                                                 |

______________________________________________________________________

## Pricing

**150 micro-USDC per GiB** of decompressed bytes scanned (~$0.15 / TB).

- Trial credit: **100,000 micro-USDC ($0.10)** on every new account (no deposit
  needed)
- Minimum deposit: **250,000 micro-USDC (0.25 USDC)**
- `GET /v1/pricing` returns the current rate as JSON

```json
{ "micro_usdc_per_gib": 150, "description": "..." }
```

### Cost estimation

`GET /v1/estimate` or `POST /v1/estimate` — authenticated, **free** (no charge
deducted).

Returns the estimated maximum bytes that would be scanned and the corresponding
cost for a query, without executing it. Useful for checking affordability or
comparing query variants before committing.

```bash
curl -G https://microquery.dev/v1/estimate \
  -H "Authorization: Bearer YOUR_API_KEY" \
  --data-urlencode "database=pubmed" \
  --data-urlencode "query=SELECT * FROM baseline WHERE MedlineCitation.Article.ArticleTitle ~ 'CRISPR'"
```

```json
{
  "database": "pubmed",
  "estimated_bytes_scanned": 53687091200,
  "estimated_cost_micro_usdc": 7500
}
```

`estimated_bytes_scanned` is a conservative upper bound — actual scan size at
execution time will be equal or lower.

______________________________________________________________________

## Databases

`GET /v1/databases` — public, no authentication required.

Returns an object with a `databases` array (each entry has `name`, `tables`, and
per-table `fields` and `partitioning` summary) and a `_hint` field describing
available query parameters.

**Query parameters**

| Parameter    | Description                                                                                                                                                                                     |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `partitions` | Set to `true` to include a per-partition byte breakdown for each table. Default: omitted. Response grows to ~500 KB uncompressed; use only when reasoning about specific date or year coverage. |

**Default response shape**

```json
{
  "databases": [
    {
      "name": "pubmed",
      "tables": [
        {
          "name": "baseline",
          "fields": ["MedlineCitation.PMID", "..."],
          "partitioning": {
            "key": "pub_year",
            "type": "int",
            "total_bytes": 137737797632,
            "updated_at": "2026-04-15T18:17:35Z"
          }
        }
      ]
    }
  ],
  "_hint": "Add ?partitions=true to include per-partition byte breakdown for each table."
}
```

With `?partitions=true` each `partitioning` object also includes a `partitions`
array of `{"value": "...", "bytes": N}` entries sorted by partition value.

**Available databases**

| Database         | Tables                                                                                  | Total size |
| ---------------- | --------------------------------------------------------------------------------------- | ---------- |
| `nvd`            | `cve`                                                                                   | ~0.7 GB    |
| `osv`            | `advisories`                                                                            | ~0.4 GB    |
| `sec`            | `edgar`                                                                                 | ~6 GB      |
| `clinicaltrials` | `studies`                                                                               | ~0.7 GB    |
| `fda`            | `faers`                                                                                 | ~6.5 GB    |
| `pubmed`         | `baseline`                                                                              | ~138 GB    |
| `btc`            | `blocks`, `outputs`                                                                     | ~121 GB    |
| `eth`            | `blocks`, `contracts`, `dex_swaps`, `lending`, `lp_events`, `transactions`, `transfers` | ~234 GB    |

______________________________________________________________________

## Deposits

**You do not need a wallet to start.** The $0.10 trial credit requires only
`POST /v1/register`. A wallet is only needed when you want to top up.

### Wallet setup

Generate a dedicated wallet for the agent in code, then send USDC to it from
your personal wallet. Do not share your personal wallet's private key with an
agent.

```javascript
import { ethers } from "ethers";

// Generate once, store the private key securely
const wallet = ethers.Wallet.createRandom();
console.log("address:     ", wallet.address);
console.log("private_key: ", wallet.privateKey);

// Load at startup
const wallet = new ethers.Wallet(process.env.AGENT_PRIVATE_KEY);
```

Send USDC (on Base) to the agent address from Coinbase, MetaMask, Rabby, or any
Base-compatible wallet. Minimum **$0.25 USDC**; $1–5 covers thousands of
queries. To get USDC on Base: withdraw from Coinbase exchange directly to Base,
bridge via [bridge.base.org](https://bridge.base.org), or use any CEX that
supports Base withdrawals.

In production, give each agent instance its own wallet — independent balances,
per-agent spend visibility, and isolation if one is compromised.

### Gasless permit deposit (EIP-2612)

Top up your account balance with USDC on Base. The operator sponsors gas — no
ETH required.

### Gasless permit deposit (EIP-2612)

Sign a permit off-chain, send it to `POST /v1/deposit` with Bearer auth. The
server submits `depositWithPermit()` on-chain using the operator key.

**Step 1** — sign an EIP-2612 permit off-chain:

```python
domain = {
    "name": "USD Coin", "version": "2",
    "chainId": 8453,
    "verifyingContract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
types = {"Permit": [
    {"name": "owner",   "type": "address"},
    {"name": "spender", "type": "address"},
    {"name": "value",   "type": "uint256"},
    {"name": "nonce",   "type": "uint256"},
    {"name": "deadline","type": "uint256"}
]}
message = {
    "owner":    YOUR_WALLET,
    "spender":  ESCROW_ADDRESS,
    "value":    amount,        # micro-USDC (1 USDC = 1_000_000)
    "nonce":    usdc.nonces(YOUR_WALLET),   # call USDC contract
    "deadline": int(time.time()) + 600
}
v, r, s = sign_typed_data(domain, types, message, private_key)
```

**Step 2** — POST to the server (Bearer auth required):

`POST /v1/deposit`

```json
{
  "amount":   1000000,
  "deadline": 1734567890,
  "v": 28,
  "r": "0x...",
  "s": "0x..."
}
```

Response:

```json
{
  "tx_hash":        "0x...",
  "amount":         1000000,
  "balance":        1005000,
  "transaction_id": "..."
}
```

The account is credited once the transaction is mined (~2 s on Base).

Escrow contract: `0xb1f8eE89bc8E51558a3C2A216620aBa1b7B2d01A` (Base mainnet)

______________________________________________________________________

## Advanced: x402

The [x402 protocol](https://x402.org) is an alternative payment path for agents
that are already crypto-native and prefer on-chain authorization without
pre-registration. Most agents should use the Bearer + deposit path above.

### x402 deposit — auto-register on first payment

An x402 deposit auto-creates an account on first use. No prior registration
required. Sign an EIP-3009 `transferWithAuthorization` and POST to `/v1/deposit`
with an `X-PAYMENT` header.

**Step 1** — sign EIP-3009 `transferWithAuthorization`:

```python
from eth_account import Account
from eth_account.messages import encode_typed_data
import time, secrets

domain = {
    "name": "USD Coin", "version": "2",
    "chainId": 8453,
    "verifyingContract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
}
types = {
    "EIP712Domain": [
        {"name": "name",              "type": "string"},
        {"name": "version",           "type": "string"},
        {"name": "chainId",           "type": "uint256"},
        {"name": "verifyingContract", "type": "address"},
    ],
    "TransferWithAuthorization": [
        {"name": "from",        "type": "address"},
        {"name": "to",          "type": "address"},
        {"name": "value",       "type": "uint256"},
        {"name": "validAfter",  "type": "uint256"},
        {"name": "validBefore", "type": "uint256"},
        {"name": "nonce",       "type": "bytes32"},
    ],
}
message = {
    "from":        YOUR_WALLET,
    "to":          "0x5Da586b29c3bb0f86cf820b9ac4331B289a6e50B",  # operator
    "value":       250000,          # minimum deposit: 250,000 micro-USDC
    "validAfter":  0,
    "validBefore": int(time.time()) + 300,
    "nonce":       "0x" + secrets.token_hex(32),
}
acct = Account.from_key(YOUR_PRIVATE_KEY)
signed = acct.sign_message(encode_typed_data(full_message={
    "types": types, "domain": domain,
    "primaryType": "TransferWithAuthorization", "message": message,
}))
signature = "0x" + signed.signature.hex()
```

**Step 2** — build the x402 payment payload and POST:

```python
import base64, json, urllib.request

payload = {
    "x402Version": 1, "scheme": "exact", "network": "base",
    "payload": {
        "signature": signature,
        "authorization": {
            "from":        message["from"],
            "to":          message["to"],
            "value":       str(message["value"]),
            "validAfter":  str(message["validAfter"]),
            "validBefore": str(message["validBefore"]),
            "nonce":       message["nonce"],
        },
    },
}
x_payment = base64.urlsafe_b64encode(
    json.dumps(payload).encode()
).decode().rstrip("=")

req = urllib.request.Request(
    "https://microquery.dev/v1/deposit",
    method="POST",
    headers={"X-PAYMENT": x_payment, "Content-Type": "application/json"},
    data=b"{}",
)
with urllib.request.urlopen(req) as resp:
    print(json.loads(resp.read()))
```

**First-deposit response `201 Created`:**

```json
{
  "api_key":            "...",
  "balance_micro_usdc": 250000,
  "transaction":        "0x...",
  "network":            "base"
}
```

Save the returned `api_key` — use it as a Bearer token for subsequent queries.

### x402 per-query — no registration, no pre-deposit

Each query is a self-contained payment. The server returns a 402 challenge with
the exact cost; the agent signs EIP-3009 for that amount and retries. **Minimum:
10,000 micro-USDC ($0.01) per query.**

Note: at microquery's typical query costs ($0.0001–$0.012), the x402 facilitator
fee ($0.001/tx) represents significant overhead. This path is best suited for
higher-value queries or agents already integrated with the x402 protocol.

**Step 1 — send the query without auth to get the cost:**

```bash
curl -s -o challenge.json -w "%{http_code}" \
  -X POST "https://microquery.dev/query?database=defi" \
  -H "Content-Type: text/plain" \
  --data "SELECT name, tvl FROM protocols ORDER BY tvl DESC LIMIT 5"
# prints: 402
```

```json
{
  "x402Version": 1,
  "accepts": [{
    "scheme": "exact", "network": "base",
    "maxAmountRequired": "10000",
    "payTo": "0x5da586b29c3bb0f86cf820b9ac4331b289a6e50b",
    "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
    "maxTimeoutSeconds": 60
  }]
}
```

**Step 2 — sign EIP-3009 and retry:**

```python
import base64, json, secrets, time
from eth_account import Account
from eth_account.messages import encode_typed_data

amount    = int(challenge["accepts"][0]["maxAmountRequired"])
pay_to    = challenge["accepts"][0]["payTo"]
usdc_addr = challenge["accepts"][0]["asset"]

acct  = Account.from_key(PRIVATE_KEY)
nonce = "0x" + secrets.token_hex(32)

signed = acct.sign_message(encode_typed_data(full_message={
    "types": {
        "EIP712Domain": [
            {"name": "name",              "type": "string"},
            {"name": "version",           "type": "string"},
            {"name": "chainId",           "type": "uint256"},
            {"name": "verifyingContract", "type": "address"},
        ],
        "TransferWithAuthorization": [
            {"name": "from",        "type": "address"},
            {"name": "to",          "type": "address"},
            {"name": "value",       "type": "uint256"},
            {"name": "validAfter",  "type": "uint256"},
            {"name": "validBefore", "type": "uint256"},
            {"name": "nonce",       "type": "bytes32"},
        ],
    },
    "domain": {
        "name": "USD Coin", "version": "2",
        "chainId": 8453, "verifyingContract": usdc_addr,
    },
    "primaryType": "TransferWithAuthorization",
    "message": {
        "from": acct.address, "to": pay_to, "value": amount,
        "validAfter": 0, "validBefore": int(time.time()) + 300, "nonce": nonce,
    },
}))

payload = {
    "x402Version": 1, "scheme": "exact", "network": "base",
    "payload": {
        "signature": "0x" + signed.signature.hex(),
        "authorization": {
            "from": acct.address, "to": pay_to, "value": str(amount),
            "validAfter": "0", "validBefore": str(int(time.time()) + 300),
            "nonce": nonce,
        },
    },
}
x_payment = base64.urlsafe_b64encode(
    json.dumps(payload).encode()
).decode().rstrip("=")

import requests
resp = requests.post(
    "https://microquery.dev/query?database=defi",
    headers={"Content-Type": "text/plain", "X-PAYMENT": x_payment},
    data="SELECT name, tvl FROM protocols ORDER BY tvl DESC LIMIT 5",
)
# resp.headers["X-Payment-Transaction"] — on-chain settlement tx hash
# resp.headers.get("X-Payment-Account-Key") — api_key (first payment only)
```

On first payment from a wallet the server auto-creates an account and returns
the `api_key` in `X-Payment-Account-Key`. Save it — use it as a Bearer token for
subsequent queries.

______________________________________________________________________

## Account Management

### Get account details

`GET /v1/accounts/{id}` — requires Bearer auth (your API key).

Returns `id`, `name`, `balance`, `wallet_addr`, `created_at`.

### List transactions

`GET /v1/accounts/{id}/transactions?limit=50&offset=0` — requires Bearer auth.

### Recover account by wallet

`GET /v1/wallets/{addr}` — public.

Returns account `id`, `name`, `balance` for a linked wallet address. Does not
return the API key. Use this if an autonomous agent needs to rediscover its
account ID using only its wallet key.

### Link a wallet after registration

Linking a wallet proves ownership via a challenge/response — the server recovers
the signer from the signature and rejects if it doesn't match the claimed
address.

**Step 1** — request a challenge nonce (public, no auth):

`POST /v1/wallet-challenge`

```json
{ "wallet_addr": "0xYOUR_WALLET" }
```

Response:

```json
{
  "nonce":      "a3f1c8...",
  "message":    "microquery wallet verification\nnonce: a3f1c8...",
  "expires_in": 300
}
```

**Step 2** — sign the `message` field with `eth_personalSign` (MetaMask,
ethers.js `signer.signMessage()`, web3.py `eth.sign()`), then submit:

`POST /v1/accounts/{id}/link-wallet` — requires Bearer auth.

```json
{
  "wallet_addr": "0xYOUR_WALLET",
  "nonce":       "a3f1c8...",
  "signature":   "0x..."
}
```

The nonce expires after **5 minutes** and is consumed on first use.

### Rate limits

| Endpoint            | Limit                          |
| ------------------- | ------------------------------ |
| `POST /v1/register` | 3 accounts per IP per 24 hours |
| `GET/POST /query`   | None — balance is the throttle |

______________________________________________________________________

## Further Reading

- [Dataset schemas and example queries](https://microquery.dev/datasets.md)
- [Runnable cross-dataset examples](https://microquery.dev/examples.md)
- [FAQ](https://microquery.dev/faq.md)
- [Sample autonomous agent](https://github.com/microqueryhq/microquery-agent)
