// // https://docs.rs/schemars/latest/schemars/derive.JsonSchema.html // // Scalar: // - Documentation: https://guides.scalar.com/scalar/scalar-api-references // - Configuration: https://guides.scalar.com/scalar/scalar-api-references/configuration // - Examples: // - https://docs.machines.dev/ // - https://tailscale.com/api // - https://api.supabase.com/api/v1 // mod compact; mod full; pub use compact::ApiJson; pub use full::OpenApiJson; use aide::openapi::{Contact, Info, License, OpenApi, Tag}; use crate::VERSION; pub fn create_openapi() -> OpenApi { let info = Info { title: "Bitcoin Research Kit".to_string(), description: Some( r#"API for querying Bitcoin blockchain data, mempool state, and on-chain series. ### Features - **[Mempool.space](https://mempool.space/docs/api/rest) compatible**: Blocks, transactions, addresses, mining, fees, and mempool endpoints match the mempool.space REST API - **Series**: Thousands of on-chain time-series across multiple indexes (date, block height, etc.) - **Multiple formats**: JSON and CSV output - **LLM-optimized**: [`/llms.txt`](/llms.txt) for discovery, [`/api.json`](/api.json) compact OpenAPI spec for tool use (full spec at [`/openapi.json`](/openapi.json)) ### Quick start ```bash curl -s https://bitview.space/api/blocks/tip/height curl -s https://bitview.space/api/v1/fees/recommended curl -s https://bitview.space/api/mempool curl -s https://bitview.space/api/series/search?q=price ``` ### Errors All errors return structured JSON with a consistent format: ```json { "error": { "type": "not_found", "code": "series_not_found", "message": "'foo' not found, did you mean 'bar'?", "doc_url": "/api" } } ``` - **`type`**: Error category — `invalid_request` (400), `forbidden` (403), `not_found` (404), `unavailable` (503), or `internal` (500) - **`code`**: Machine-readable error code (e.g. `invalid_address`, `series_not_found`, `weight_exceeded`) - **`message`**: Human-readable description - **`doc_url`**: Link to API documentation ### Client Libraries - [JavaScript](https://www.npmjs.com/package/brk-client) - [Python](https://pypi.org/project/brk-client/) - [Rust](https://crates.io/crates/brk_client) ### Links - [GitHub](https://github.com/bitcoinresearchkit/brk) - [Bitview](https://bitview.space) - Web app built on this API"# .to_string(), ), version: format!("v{VERSION}"), contact: Some(Contact { name: Some("Bitcoin Research Kit".to_string()), url: Some("https://github.com/bitcoinresearchkit/brk".to_string()), email: Some("hello@bitcoinresearchkit.org".to_string()), ..Contact::default() }), license: Some(License { name: "MIT".to_string(), url: Some( "https://github.com/bitcoinresearchkit/brk/blob/main/docs/LICENSE.md".to_string(), ), ..License::default() }), ..Info::default() }; let tags = vec![ Tag { name: "Server".to_string(), description: Some( "API metadata, health monitoring, and OpenAPI specifications.".to_string(), ), ..Default::default() }, Tag { name: "Series".to_string(), description: Some( "Access thousands of Bitcoin network time-series data. Query historical statistics \ across various indexes (date, week, month, block height) with JSON or CSV output.\n\n\ **Note:** Series names are subject to change while the project is in active development." .to_string(), ), ..Default::default() }, Tag { name: "General".to_string(), description: Some( "General Bitcoin network information including difficulty adjustments and price data.\n\n\ *[Mempool.space](https://mempool.space/docs/api/rest) compatible.*" .to_string(), ), ..Default::default() }, Tag { name: "Addresses".to_string(), description: Some( "Query Bitcoin address data including balances, transaction history, and UTXOs. \ Supports all address types: P2PKH, P2SH, P2WPKH, P2WSH, and P2TR.\n\n\ *[Mempool.space](https://mempool.space/docs/api/rest) compatible.*" .to_string(), ), ..Default::default() }, Tag { name: "Blocks".to_string(), description: Some( "Retrieve block data by hash or height. Access block headers, transaction lists, \ and raw block bytes.\n\n\ *[Mempool.space](https://mempool.space/docs/api/rest) compatible.*" .to_string(), ), ..Default::default() }, Tag { name: "Mining".to_string(), description: Some( "Mining statistics including pool distribution, hashrate, difficulty adjustments, \ block rewards, and fee rates across configurable time periods.\n\n\ *[Mempool.space](https://mempool.space/docs/api/rest) compatible.*" .to_string(), ), ..Default::default() }, Tag { name: "Fees".to_string(), description: Some( "Fee estimation and projected mempool blocks.\n\n\ *[Mempool.space](https://mempool.space/docs/api/rest) compatible.*" .to_string(), ), ..Default::default() }, Tag { name: "Mempool".to_string(), description: Some( "Monitor unconfirmed transactions. Get mempool statistics, \ transaction IDs, fee histogram, and recent transactions.\n\n\ *[Mempool.space](https://mempool.space/docs/api/rest) compatible.*" .to_string(), ), ..Default::default() }, Tag { name: "Transactions".to_string(), description: Some( "Retrieve transaction data by txid. Access full transaction details, confirmation \ status, raw hex, merkle proofs, and output spend information.\n\n\ *[Mempool.space](https://mempool.space/docs/api/rest) compatible.*" .to_string(), ), ..Default::default() }, Tag { name: "Oracle".to_string(), description: Some( "On-chain BTC/USD price derived purely from round-dollar payment patterns in \ transaction outputs, with no external price feed. Payment activity is binned on a \ log scale, and a smoothed EMA over recent blocks locates the price.\n\n\ Histograms come in two flavors, each available at the live tip (mempool-blended) \ or at any confirmed height: `raw` bins every output by value with no filtering, \ while `ema` is the smoothed round-dollar window the price is read from. The live \ price is also at `/api/mempool/price`. Confirmed per-height price history is at \ `/api/vecs/height-to-price`." .to_string(), ), ..Default::default() }, Tag { name: "URPD".to_string(), description: Some( "UTXO Realized Price Distribution. For each (cohort, date) pair, supply is \ grouped by the close price at which each UTXO was last moved. One snapshot is \ emitted per UTC day.\n\n\ Each bucket carries `supply` (BTC), `realized_cap` (USD, = `price_floor * supply`), \ and `unrealized_pnl` (USD, = `(close - price_floor) * supply`, can be negative).\n\n\ Aggregate with the `agg` query parameter (alias `bucket`):\n\ - `raw`: one bucket per rounded price (default).\n\ - `lin200` / `lin500` / `lin1000`: linear buckets, $200 / $500 / $1000 wide.\n\ - `log10` / `log50` / `log100` / `log200`: logarithmic buckets, N bins per price decade.\n\n\ Discovery flow: `GET /api/urpd` (cohorts), `GET /api/urpd/{cohort}` (latest), \ `GET /api/urpd/{cohort}/dates` (history), `GET /api/urpd/{cohort}/{date}` (specific)." .to_string(), ), ..Default::default() }, Tag { name: "Metrics".to_string(), description: Some("Deprecated - use Series".to_string()), extensions: [("deprecated".to_string(), serde_json::Value::Bool(true))].into(), ..Default::default() }, ]; OpenApi { info, tags, ..OpenApi::default() } }