openapi: 3.0.3

info:
  title: Startmining API
  version: 1.0.0
  description: |
    Bitcoin mining live data sourced from a self-hosted Bitcoin Core full node.
    Free public REST API powering pro.startmining.io. All blockchain state comes
    from a self-hosted Bitcoin Core node — no third-party blockchain provider
    in the chain.

    **Authentication.** Free tier is anonymous — no API key required.
    Per-IP rate limit applies (see Rate limits below). Enterprise tier (custom
    rate limit, SLA, support) is available on request: contact@startmining.io.

    **Rate limits.** 120 requests / minute / IP on Free tier, as a SINGLE budget
    shared across the rate-limited data endpoints — not one budget per endpoint.
    Budget 120 req/min in total, whichever endpoints you call. A 429 carries a
    JSON body and a `Retry-After` header. The widget embed surface (`/embed/*`,
    NOT documented here as an API) is rate-limited at 60 req/min per
    (IP, embedding domain) pair.

    **Versioning.** Semver. Breaking changes bump the major version, get a
    6-month deprecation window, and are announced on https://pro.startmining.io/developers.

    **Data licence.** CC BY 4.0 — attribution required:
    `Startmining (https://pro.startmining.io)`.
  contact:
    email: contact@startmining.io
  license:
    name: CC BY 4.0
    url: https://creativecommons.org/licenses/by/4.0/

servers:
  - url: https://pro.startmining.io
    description: Production — Next.js app routes (this spec)
  - url: https://mining-api.startmining.io
    description: Internal upstream node — not part of this spec, key required

tags:
  - name: system
    description: Healthcheck and operational endpoints
  - name: market
    description: BTC price, hashprice, difficulty — aggregated views
  - name: network
    description: Bitcoin network state from a self-hosted Bitcoin Core node
  - name: mining
    description: Halving countdown, mempool, mining pools
  - name: pools
    description: >-
      Pool Economics Index — per-pool economics (blocks found, luck, realized
      rewards, fee capture premium) derived exclusively from the self-hosted
      Bitcoin Core node, never from pool-reported figures
  - name: asics
    description: ASIC hardware market prices

paths:
  /api/health:
    get:
      tags: [system]
      summary: Healthcheck
      description: Returns 200 with a static status payload when the simulator app is up.
      operationId: getHealth
      responses:
        '200':
          description: Service is up.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Health'
              example:
                status: ok
                service: simulator-pro
                timestamp: '2026-04-30T20:38:28.882Z'

  /api/market-summary:
    get:
      tags: [market]
      summary: Aggregated market snapshot
      description: |
        The single endpoint most consumers want: BTC price, current hashprice,
        difficulty, network hashrate, last block height — everything for a profitability
        widget in one round-trip. Cached for 60 seconds (`s-maxage=60`).
      operationId: getMarketSummary
      responses:
        '200':
          description: Aggregated snapshot.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketSummary'
              example:
                btcPrice: 76439
                hashpriceUsd: 37.55
                difficulty: 135594876535256.6
                difficultyT: '135.59T'
                networkHashrateEH: 938
                blockReward: 3.125
                txFeesPercent: 2.4
                breakevenEfficiency75c: 0
                blockHeight: 947321
                updated: '2026-04-30T20:33:37.240Z'
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/market/price:
    get:
      tags: [market]
      summary: BTC price + hashprice (detailed)
      description: |
        Full market snapshot with provenance fields (which upstream provided each
        value). Includes 24h price change, hashprice in USD and BTC, transaction-fee
        share of the block reward, and effective reward.
      operationId: getMarketPrice
      responses:
        '200':
          description: Detailed market data.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/MarketPrice'
              example:
                price: 76466
                change24h: 1.08
                hashpriceChange24h: 1.08
                difficulty: 135594876535256.6
                networkHashrateEH: 919.95
                hashpriceUsd: 35.75733823168562
                hashpriceBtc: 0.00046762401893240944
                blockReward: 3.125
                effectiveReward: 3.152
                txFeesPercent: 0.9
                avgTxFees: 0.027
                blockHeight: 947321
                source:
                  price: mining-api
                  difficulty: mining-api
                  txFees: mining-api
                  hashprice: calculated
                timestamp: '2026-04-30T20:37:26.445Z'
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/market/difficulty:
    get:
      tags: [market]
      summary: Current network difficulty
      description: Minimal endpoint returning only the live difficulty.
      operationId: getMarketDifficulty
      responses:
        '200':
          description: Current difficulty.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Difficulty'
              example:
                difficulty: 135594876535257
                source: blockchain.info
                timestamp: '2026-04-30T20:38:07.073Z'
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/network/stats:
    get:
      tags: [network]
      summary: Bitcoin Core full node stats
      description: |
        Chain state (height, size, verification progress), hashrate, difficulty epoch
        progress, transaction throughput, mempool size, recent block-time average.
        Direct passthrough of the upstream Bitcoin Core RPC, normalized.
      operationId: getNetworkStats
      responses:
        '200':
          description: Full node snapshot.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NetworkStats'
              example:
                timestamp: '2026-04-30T20:34:33.617Z'
                blockchain:
                  height: 947321
                  size_gb: 839.9
                  chain: main
                  pruned: false
                  verification_progress: 0.9999949582502852
                hashrate:
                  current_eh: 855.2
                  current_h: 8.552005482771601e+20
                difficulty:
                  current: 135594876535256.6
                  current_formatted: '135.59 T'
                  blocks_until_adjustment: 199
                  blocks_into_epoch: 1817
                transactions:
                  total: 1348575698
                  rate_per_second: 6.578
                  mempool_size: 12910
                blocks:
                  avg_time_seconds: 619
                  avg_time_7d_seconds: 619
                  avg_time_7d_formatted: '10m 19s'
                  target_time_seconds: 600
                  vs_target_pct: 3.21
                source: bitcoin_node
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/network/blocks:
    get:
      tags: [network]
      summary: Recent blocks with miner attribution
      description: |
        Returns the last 10 blocks with height, hash, timestamp, transaction count,
        size, weight, fees, and the identified mining pool when known.
      operationId: getNetworkBlocks
      responses:
        '200':
          description: Recent blocks.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecentBlocks'
              example:
                timestamp: '2026-04-30T20:34:33.770Z'
                count: 10
                current_height: 947321
                blocks:
                  - height: 947321
                    hash: '0000000000000000000098f186675233ca1aecffd387ed190ac5886c084cb1de'
                    timestamp: '2026-04-30T20:17:18.000Z'
                    time_ago_seconds: 1035
                    transactions: 3887
                    size_bytes: 1603372
                    size_mb: 1.603
                    weight: 3993406
                    difficulty: 135594876535256.6
                    subsidy_btc: 3.125
                    fees_btc: 0.068
                    total_btc: 3.193
                    avg_fee_sat: 1740
                    avg_fee_rate: 6
                    pool:
                      name: Foundry USA
                      slug: foundry-usa
                      identified: true
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/network/mempool:
    get:
      tags: [mining]
      summary: Mempool size and fee buckets
      description: Current pending transactions, total fees, and recommended fee rates per priority bucket.
      operationId: getMempool
      responses:
        '200':
          description: Mempool snapshot.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Mempool'
              example:
                timestamp: '2026-04-30T20:34:33.662Z'
                mempool:
                  transactions: 12910
                  size_bytes: 9010630
                  size_mb: 9.01
                  total_fees_btc: 0.23515617
                  memory_usage_mb: 38.18
                fees:
                  fastest:
                    sat_per_vb: 7
                    target_blocks: 2
                    description: Next block
                  medium:
                    sat_per_vb: 5
                    target_blocks: 6
                    description: '~1 hour'
                  slow:
                    sat_per_vb: 1
                    target_blocks: 144
                    description: '~1 day'
                  minimum:
                    sat_per_vb: 1
                    description: Minimum relay fee
                source: bitcoin_node
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/network/pools:
    get:
      tags: [mining]
      summary: Mining pool distribution
      description: |
        Pool share over the last 144 blocks (~24h), with estimated hashrate per pool.
        Useful for centralization metrics and miner concentration tracking.
      operationId: getNetworkPools
      responses:
        '200':
          description: Pool distribution.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PoolDistribution'
              example:
                timestamp: '2026-04-30T20:34:34.380Z'
                blocks_analyzed: 144
                period_description: 'Last 144 blocks (~24 hours)'
                network_hashrate_eh: 855.2
                pools:
                  - name: Foundry USA
                    slug: foundry-usa
                    blocks: 44
                    percentage: 30.56
                    estimated_hashrate_eh: 261.35
                  - name: AntPool
                    slug: antpool
                    blocks: 19
                    percentage: 13.19
                    estimated_hashrate_eh: 112.8
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/network/halving:
    get:
      tags: [mining]
      summary: Halving countdown and history
      description: |
        Current halving era, next halving (target block, estimated date, blocks remaining,
        days remaining), total mined supply, and the full historical timeline since genesis.
      operationId: getHalving
      responses:
        '200':
          description: Halving state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Halving'
              example:
                current:
                  era: 5
                  block_reward: 3.125
                  started_block: 840000
                  started_date: '2024-04-20'
                next:
                  era: 6
                  block_reward: 1.5625
                  target_block: 1050000
                  blocks_remaining: 102679
                  days_remaining: 713
                  estimated_date: '2028-04-12'
                supply:
                  total_mined: 20022878.13
                  max_supply: 21000000
                  percent_mined: 95.35
                  remaining: 977121.88
                history:
                  - era: 1
                    block: 0
                    date: '2009-01-03'
                    reward: 50
                  - era: 2
                    block: 210000
                    date: '2012-11-28'
                    reward: 25
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/network/market:
    get:
      tags: [network]
      summary: Network + market merged snapshot
      description: |
        Combines BTC market data (price, volume, market cap, 24h change) with the live
        network state (difficulty, hashrate, block height, mempool) and mining payouts
        (hashprice in USD and BTC, effective block reward, fee share).
      operationId: getNetworkMarket
      responses:
        '200':
          description: Network + market merged.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NetworkMarket'
              example:
                timestamp: '2026-04-30T20:34:33.689Z'
                btc:
                  price_usd: 76442
                  change_24h_pct: 1.09
                  market_cap_usd: 1530543074499.99
                  volume_24h_usd: 32859408466.31
                network:
                  difficulty: 135594876535256.6
                  difficulty_formatted: '135.59 T'
                  hashrate_eh: 919.95
                  block_height: 947321
                  blocks_until_adjustment: 199
                  block_time_sma_seconds: 633
                  block_time_sma_formatted: '10m 33s'
                mining:
                  hashprice_usd_ph_day: 35.74
                  hashprice_btc_ph_day: 0.000467607607543174
                  hashprice_change_24h_pct: 1.09
                  block_subsidy: 3.125
                  effective_reward: 3.152
                  tx_fees_avg_btc: 0.027
                  tx_fees_pct: 0.9
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/network/difficulty-estimate:
    get:
      tags: [network]
      summary: Next difficulty retarget estimate
      description: |
        Projects the difficulty value at the upcoming retarget (every 2016 blocks)
        based on the current epoch's average block time. Includes the previous adjustment
        for context and the estimated retarget date.
      operationId: getDifficultyEstimate
      responses:
        '200':
          description: Retarget projection.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DifficultyEstimate'
              example:
                timestamp: '2026-04-30T20:34:25.242Z'
                current:
                  difficulty: 135594876535256.6
                  difficulty_formatted: '135.59 T'
                  height: 947321
                previous_adjustment:
                  change_percent: -2.43
                  date: '2026-04-17'
                  block_height: 945504
                  from_difficulty_formatted: '138.97 T'
                  to_difficulty_formatted: '135.59 T'
                next_adjustment:
                  estimated_difficulty: 130622429774946.53
                  estimated_difficulty_formatted: '130.62 T'
                  change_percent: -3.67
                  blocks_remaining: 199
                  estimated_date: '2026-05-02T07:00:10.455Z'
                  epoch_progress_percent: 90.13
                  epoch_avg_block_time_seconds: 623
                hashrate_eh: 855.2
                source: bitcoin_node
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/pools:
    get:
      tags: [pools]
      summary: Pool Economics Index (ranked, windowed)
      description: |
        Per-pool mining economics over a selectable window, derived exclusively
        from our self-hosted Bitcoin Core full node — never from pool-reported
        figures. Per pool: blocks found, block share (share of blocks FOUND,
        not hashrate share), luck vs a 90-day baseline (null when no baseline
        covers the window, e.g. `1y`), realized rewards, fee capture premium
        (a Tier 1 proxy confounded by mining time — read the `caveats` object
        shipped with every response before quoting it), average fee rate, and
        empty blocks. Network aggregates include top-3 share and the Nakamoto
        coefficient. Responses are dated: `asOf` is the timestamp of the latest
        ingested block, not the render time. CORS `*`, cached 5 minutes.
        Rate limit: 120 req/min/IP, SHARED with the other rate-limited data
        endpoints — it is one budget per IP, not one per endpoint.
      operationId: getPoolsIndex
      parameters:
        - name: window
          in: query
          schema: { type: string, enum: [24h, 7d, 30d, 1y], default: 24h }
          description: Aggregation window.
      responses:
        '200':
          description: Ranked pool economics index. Example is a real production capture (30d window), truncated to the top 3 of 20 pools.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PoolsIndex'
              example:
                asOf: '2026-07-16T13:31:23.000Z'
                window: 30d
                blocks_analyzed: 4334
                baseline_window: 90d
                latest_block_height: 958289
                latest_block_time: '2026-07-16T13:31:23.000Z'
                network:
                  top3_share: 0.5784
                  nakamoto_coefficient: 3
                  mean_fees_btc: 0.02127
                  median_fees_btc: 0.01758
                  unattributed_blocks: 52
                  unattributed_share: 0.012
                pools:
                  - slug: foundry-usa
                    name: Foundry USA
                    blocks: 1109
                    block_share: 0.2559
                    luck: 0.856
                    avg_block_reward_btc: 3.14616
                    total_reward_btc: 3489.08776
                    fee_capture_premium: -0.0053
                    avg_fee_rate_sat_vb: 2.12
                    empty_blocks: 0
                  - slug: antpool
                    name: AntPool
                    blocks: 790
                    block_share: 0.1823
                    luck: 1.021
                    avg_block_reward_btc: 3.14668
                    total_reward_btc: 2485.8799
                    fee_capture_premium: 0.0195
                    avg_fee_rate_sat_vb: 2.18
                    empty_blocks: 1
                  - slug: f2pool
                    name: F2Pool
                    blocks: 608
                    block_share: 0.1403
                    luck: 1.258
                    avg_block_reward_btc: 3.14566
                    total_reward_btc: 1912.56334
                    fee_capture_premium: -0.0285
                    avg_fee_rate_sat_vb: 2.08
                    empty_blocks: 3
                caveats:
                  fee_capture_premium: 'Tier 1 proxy: pool mean block fees vs network mean over the same window. Confounded by mining time (fee levels vary with congestion, so a pool mining during busy hours scores higher). Cross-check against avg_fee_rate_sat_vb. A rigorous template-diff version is planned.'
                  luck: 'Blocks found vs blocks expected from the pool share over the 90 days PRECEDING the window. Mean-reverting and transient: it is not an edge. null when the pool has no baseline (new pool) or when the window is longer than the baseline (1y).'
                  block_share: 'Share of blocks found, NOT hashrate share. Over short windows this is noisy: that is precisely what luck measures.'
                  avg_fee_rate_sat_vb: 'Mean of per-block fee rates (fees / non-coinbase vsize), unweighted by block size. null for a block with no fee-paying transaction (empty block).'
                  mined_at: 'Windows are defined on the miner-declared block header timestamp, which can drift ~2h and is not strictly monotonic.'
                source: self-hosted Bitcoin Core
        '400':
          description: Invalid window parameter.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
              example:
                error: 'Invalid window. Use one of: 24h, 7d, 30d, 1y'
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }
        '503':
          description: >-
            Stale data — the index has not ingested a recent block, so serving
            it would misrepresent the network. Deliberate fail-honest behaviour;
            retry later rather than caching this response.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /api/pools/{slug}:
    get:
      tags: [pools]
      summary: Single pool detail with daily history
      description: |
        The same economics as the index entry for one pool, plus a daily
        history (blocks found and share of blocks found per day) over the
        window, the network aggregates for context, and `coverage_since`
        (node-derived block coverage starts 2013). There is no HTML page
        behind this endpoint — it exists for machines. Same dating, `caveats`,
        CORS and rate limit as the index.
      operationId: getPoolDetail
      parameters:
        - name: slug
          in: path
          required: true
          schema: { type: string }
          description: Pool slug as returned by `/api/pools`, e.g. `foundry-usa`, `antpool`, `f2pool`.
        - name: window
          in: query
          schema: { type: string, enum: [24h, 7d, 30d, 1y], default: 24h }
          description: Aggregation window.
      responses:
        '200':
          description: Pool detail with daily history. Example is a real production capture (7d window, caveats identical to the index and omitted here for brevity).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PoolDetail'
              example:
                asOf: '2026-07-16T23:21:10.000Z'
                window: 7d
                baseline_window: 90d
                blocks_analyzed: 977
                latest_block_height: 958336
                latest_block_time: '2026-07-16T23:21:10.000Z'
                pool:
                  slug: antpool
                  name: AntPool
                  blocks: 164
                  block_share: 0.1679
                  luck: 0.922
                  avg_block_reward_btc: 3.1466
                  total_reward_btc: 516.04301
                  fee_capture_premium: 0.0557
                  avg_fee_rate_sat_vb: 2.16
                  empty_blocks: 0
                history:
                  - date: '2026-07-10'
                    blocks: 29
                    share: 0.2230769230769231
                  - date: '2026-07-11'
                    blocks: 25
                    share: 0.17985611510791366
                  - date: '2026-07-12'
                    blocks: 17
                    share: 0.11805555555555555
                  - date: '2026-07-13'
                    blocks: 33
                    share: 0.22448979591836735
                  - date: '2026-07-14'
                    blocks: 27
                    share: 0.17532467532467533
                  - date: '2026-07-15'
                    blocks: 16
                    share: 0.11428571428571428
                  - date: '2026-07-16'
                    blocks: 17
                    share: 0.14049586776859505
                network:
                  top3_share: 0.6162
                  nakamoto_coefficient: 3
                  mean_fees_btc: 0.02046
                  median_fees_btc: 0.0166
                  unattributed_blocks: 7
                  unattributed_share: 0.0072
                coverage_since: '2012-12-31T21:45:40.000Z'
                source: self-hosted Bitcoin Core
        '400':
          description: Invalid window parameter.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
              example:
                error: 'Invalid window. Use one of: 24h, 7d, 30d, 1y'
        '404':
          description: Unknown pool slug.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }
              example:
                error: 'Unknown pool slug: nonexistent-pool'
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }
        '503':
          description: >-
            Stale data — the index has not ingested a recent block. Deliberate
            fail-honest behaviour; retry later.
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Error' }

  /api/pools/history:
    get:
      tags: [pools]
      summary: Weekly mining-pool composition since 2022
      description: >
        Weekly block counts per pool (ratio-of-sums shares, not averaged ratios),
        plus the Nakamoto coefficient and top-3 share per week since 2022-01-01,
        derived exclusively from our self-hosted Bitcoin Core full node. `since`
        is FLOORED to 2022-01-01: earlier attribution is under 95% (15% in 2014,
        pools did not sign their coinbase) and would misrepresent centralization —
        a clamped request is served from 2022 with `clamped_since: true`.
        `unattributed_blocks` counts toward `total_blocks` but is excluded from the
        Nakamoto coefficient and top-3 share (it is not a pool). Weeks start on
        Monday (ISO), so the first bucket can be a partial week. Per week,
        `sum(pools) + unattributed_blocks == total_blocks`. CORS `*`, cached 5
        minutes. Rate limit: 120 req/min/IP, SHARED with the other rate-limited
        data endpoints.
      operationId: getPoolsHistory
      parameters:
        - name: since
          in: query
          schema: { type: string, format: date, default: '2022-01-01' }
          description: Inclusive lower bound, floored to 2022-01-01.
      responses:
        '200':
          description: Weekly composition series. Example is a real production capture, truncated to a single week.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PoolsHistory'
              example:
                since: '2022-01-01'
                clamped_since: false
                bucket: week
                weeks:
                  - week: '2022-01-31'
                    total_blocks: 1002
                    pools:
                      foundry-usa: 174
                      f2pool: 153
                      antpool: 144
                      binance: 118
                      poolin: 116
                      viabtc: 101
                      braiins: 58
                      sbi-crypto: 23
                      mara: 16
                      luxor: 14
                      ckpool: 2
                      sigmapool: 2
                    unattributed_blocks: 81
                    nakamoto_coefficient: 4
                    top3_share: 0.4701
                pool_names:
                  foundry-usa: Foundry USA
                  f2pool: F2Pool
                  antpool: AntPool
                  binance: Binance Pool
                  poolin: Poolin
                  viabtc: ViaBTC
                  braiins: Braiins Pool
                  sbi-crypto: SBI Crypto
                  mara: MARA Pool
                  luxor: Luxor
                  ckpool: CKPool
                  sigmapool: SigmaPool
                source: self-hosted Bitcoin Core
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/asic-prices:
    get:
      tags: [asics]
      summary: ASIC market prices (latest snapshot)
      description: |
        Current secondary-market prices for SHA-256 ASICs, indexed by `$/TH`.
        Updated periodically from manufacturer datasheets and aggregator scrapes.
        See `/asics` for the full catalogue with hashrate / efficiency / cooling.
      operationId: getAsicPrices
      responses:
        '200':
          description: Latest ASIC prices.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/AsicPrices'
              example:
                generated_at: '2026-01-22T16:35:33.700778'
                total_models: 169
                latest_prices:
                  - model: S21 XP
                    hashrate: 270
                    price_per_th: 15
                    unit_price: 4050
                    efficiency_jt: null
                  - model: S23 Hydro
                    hashrate: 580
                    price_per_th: 17.2
                    unit_price: 9976
                    efficiency_jt: 9.5
        '429': { $ref: '#/components/responses/RateLimited' }
        '500': { $ref: '#/components/responses/InternalError' }

  /api/calculate:
    get:
      tags: [market]
      summary: Self-explaining mining profitability calculation
      description: |
        Full Bitcoin mining profitability calculation at live market conditions,
        with **step-by-step formulas and numeric applications** (`steps[]`) so the
        result can be quoted and audited as-is — designed for AI agents. Pick an
        ASIC from the catalogue (`asic=`) or pass custom specs (`hashrate=` + `power=`).
        Monthly figures use 730 h/month (30.4167 days). Cached 5 minutes.
      operationId: getCalculation
      parameters:
        - name: asic
          in: query
          schema: { type: string }
          description: 'Catalogue model_id, e.g. `bitmain-antminer-s21-xp`. Full list: /data/asic_specs.json'
        - name: hashrate
          in: query
          schema: { type: number }
          description: Custom hashrate per unit (TH/s), with `power=` instead of `asic=`
        - name: power
          in: query
          schema: { type: number }
          description: Custom power per unit (W), with `hashrate=`
        - name: kwh
          in: query
          schema: { type: number, default: 0.075 }
          description: Electricity price (USD/kWh)
        - name: units
          in: query
          schema: { type: integer, default: 1, maximum: 100000 }
          description: Number of ASIC
        - name: pool_fee
          in: query
          schema: { type: number, default: 0.02, maximum: 0.2 }
          description: Pool fee rate (fraction)
      responses:
        '200':
          description: Calculation with inputs, dated market data, steps and results.
          content:
            application/json:
              schema:
                type: object
                description: >-
                  `{ as_of, inputs, market, steps[] (formula + computation per step),
                  results { efficiency_j_th, daily, monthly, breakeven_electricity_usd_kwh,
                  margin_pct, profitable }, caveats[], links }`
              example:
                as_of: '2026-07-15T10:00:00.000Z'
                inputs:
                  asic: Antminer S21 XP
                  units: 1
                  hashrate_th_per_unit: 270
                  power_w_per_unit: 3645
                  electricity_usd_kwh: 0.06
                  pool_fee_rate: 0.02
                market:
                  btc_price_usd: 60000
                  hashprice_usd_ph_day: 60
                  difficulty: 127170000000000
                  source: Self-hosted Bitcoin Core full node (Startmining)
                steps:
                  - step: 4
                    name: Daily gross revenue
                    formula: revenue ($/day) = fleet hashrate (PH/s) x hashprice ($/PH/day)
                    computation: 0.27 x 60 = $16.2/day
                    result: 16.2
                    unit: USD/day
                results:
                  efficiency_j_th: 13.5
                  daily: { revenue_usd: 16.2, pool_fee_usd: 0.324, electricity_cost_usd: 5.2488, profit_usd: 10.6272, btc_mined: 0.00027 }
                  monthly: { revenue_usd: 492.75, electricity_cost_usd: 159.65, profit_usd: 323.24, btc_mined: 0.00821250 }
                  breakeven_electricity_usd_kwh: 0.1815
                  margin_pct: 65.6
                  profitable: true
        '400': { description: 'Invalid or missing parameters (response includes a `usage` object).' }
        '404': { description: 'Unknown ASIC model_id (response includes `suggestions`).' }
        '429': { $ref: '#/components/responses/RateLimited' }
        '503': { description: Live market data temporarily unavailable. }

components:
  schemas:
    Health:
      type: object
      required: [status, service, timestamp]
      properties:
        status: { type: string, example: ok }
        service: { type: string, example: simulator-pro }
        timestamp: { type: string, format: date-time }

    MarketSummary:
      type: object
      required: [btcPrice, hashpriceUsd, difficulty, networkHashrateEH, blockHeight, updated]
      properties:
        btcPrice: { type: number, description: BTC price in USD }
        hashpriceUsd: { type: number, description: Daily revenue per PH/s in USD }
        difficulty: { type: number, description: Current network difficulty }
        difficultyT: { type: string, description: Difficulty formatted (e.g. "135.59T") }
        networkHashrateEH: { type: number, description: Network hashrate in EH/s }
        blockReward: { type: number, description: Current block subsidy in BTC }
        txFeesPercent: { type: number, description: Transaction fees as % of block reward }
        breakevenEfficiency75c: { type: number, description: Break-even J/TH at $0.075/kWh }
        blockHeight: { type: integer }
        updated: { type: string, format: date-time }

    MarketPrice:
      type: object
      required: [price, hashpriceUsd, difficulty, blockHeight, timestamp]
      properties:
        price: { type: number }
        change24h: { type: number, description: BTC price change over 24h, in percent }
        hashpriceChange24h: { type: number }
        difficulty: { type: number }
        networkHashrateEH: { type: number }
        hashpriceUsd: { type: number }
        hashpriceBtc: { type: number }
        blockReward: { type: number }
        effectiveReward: { type: number, description: Block reward + average fees }
        txFeesPercent: { type: number }
        avgTxFees: { type: number, description: Average fees per block in BTC }
        blockHeight: { type: integer }
        source:
          type: object
          additionalProperties: { type: string }
          example:
            price: mining-api
            difficulty: mining-api
            txFees: mining-api
            hashprice: calculated
        timestamp: { type: string, format: date-time }

    Difficulty:
      type: object
      required: [difficulty, source, timestamp]
      properties:
        difficulty: { type: integer, format: int64 }
        source: { type: string, example: blockchain.info }
        timestamp: { type: string, format: date-time }

    NetworkStats:
      type: object
      required: [timestamp, blockchain, hashrate, difficulty, transactions, blocks, source]
      properties:
        timestamp: { type: string, format: date-time }
        blockchain:
          type: object
          properties:
            height: { type: integer }
            size_gb: { type: number }
            chain: { type: string, example: main }
            pruned: { type: boolean }
            verification_progress: { type: number }
        hashrate:
          type: object
          properties:
            current_eh: { type: number }
            current_h: { type: number }
        difficulty:
          type: object
          properties:
            current: { type: number }
            current_formatted: { type: string }
            blocks_until_adjustment: { type: integer }
            blocks_into_epoch: { type: integer }
        transactions:
          type: object
          properties:
            total: { type: integer, format: int64 }
            rate_per_second: { type: number }
            mempool_size: { type: integer }
        blocks:
          type: object
          properties:
            avg_time_seconds: { type: integer }
            avg_time_7d_seconds: { type: integer }
            avg_time_7d_formatted: { type: string }
            target_time_seconds: { type: integer }
            vs_target_pct: { type: number }
        source: { type: string, example: bitcoin_node }

    Block:
      type: object
      required: [height, hash, timestamp, transactions, size_bytes]
      properties:
        height: { type: integer }
        hash: { type: string }
        timestamp: { type: string, format: date-time }
        time_ago_seconds: { type: integer }
        transactions: { type: integer }
        size_bytes: { type: integer }
        size_mb: { type: number }
        weight: { type: integer }
        difficulty: { type: number }
        subsidy_btc: { type: number }
        fees_btc: { type: number }
        total_btc: { type: number }
        avg_fee_sat: { type: integer }
        avg_fee_rate: { type: integer }
        pool:
          type: object
          properties:
            name: { type: string }
            slug: { type: string }
            identified: { type: boolean }

    RecentBlocks:
      type: object
      required: [timestamp, count, blocks]
      properties:
        timestamp: { type: string, format: date-time }
        count: { type: integer }
        current_height: { type: integer }
        blocks:
          type: array
          items: { $ref: '#/components/schemas/Block' }

    Mempool:
      type: object
      required: [timestamp, mempool, fees, source]
      properties:
        timestamp: { type: string, format: date-time }
        mempool:
          type: object
          properties:
            transactions: { type: integer }
            size_bytes: { type: integer }
            size_mb: { type: number }
            total_fees_btc: { type: number }
            memory_usage_mb: { type: number }
        fees:
          type: object
          properties:
            fastest: { $ref: '#/components/schemas/FeeBucket' }
            medium: { $ref: '#/components/schemas/FeeBucket' }
            slow: { $ref: '#/components/schemas/FeeBucket' }
            minimum: { $ref: '#/components/schemas/FeeBucket' }
        source: { type: string, example: bitcoin_node }

    FeeBucket:
      type: object
      required: [sat_per_vb]
      properties:
        sat_per_vb: { type: integer }
        target_blocks: { type: integer }
        description: { type: string }

    Pool:
      type: object
      required: [name, slug, blocks, percentage]
      properties:
        name: { type: string }
        slug: { type: string }
        blocks: { type: integer }
        percentage: { type: number }
        estimated_hashrate_eh: { type: number }

    PoolDistribution:
      type: object
      required: [timestamp, blocks_analyzed, network_hashrate_eh, pools]
      properties:
        timestamp: { type: string, format: date-time }
        blocks_analyzed: { type: integer }
        period_description: { type: string }
        network_hashrate_eh: { type: number }
        pools:
          type: array
          items: { $ref: '#/components/schemas/Pool' }

    PoolEconomics:
      type: object
      required: [slug, name, blocks, block_share, avg_block_reward_btc, total_reward_btc, empty_blocks]
      properties:
        slug: { type: string }
        name: { type: string }
        blocks: { type: integer, description: Blocks found in the window }
        block_share:
          type: number
          description: Share of blocks FOUND in the window — not hashrate share. Noisy over short windows; that noise is what luck measures.
        luck:
          type: number
          nullable: true
          description: Blocks found vs blocks expected from the pool share over the 90 days preceding the window. Mean-reverting and transient, not an edge. null when the pool has no baseline (new pool) or the window exceeds the baseline (1y).
        avg_block_reward_btc: { type: number, description: Mean total reward (subsidy + fees) per block, in BTC }
        total_reward_btc: { type: number, description: Total realized reward over the window, in BTC }
        fee_capture_premium:
          type: number
          nullable: true
          description: Tier 1 proxy — pool mean block fees vs network mean over the same window, minus 1. Confounded by mining time (congestion varies); a rigorous template-diff version is planned.
        avg_fee_rate_sat_vb:
          type: number
          nullable: true
          description: Mean of per-block fee rates (sat/vB), unweighted by block size. null for a block with no fee-paying transaction.
        empty_blocks: { type: integer }

    PoolsNetworkAggregates:
      type: object
      properties:
        top3_share: { type: number, description: Combined block share of the top 3 pools over the window }
        nakamoto_coefficient: { type: integer, description: Minimum number of pools whose combined blocks found exceed 50% of the window }
        mean_fees_btc: { type: number, description: Network mean fees per block (BTC) over the window }
        median_fees_btc: { type: number, description: Network median fees per block (BTC) over the window }
        unattributed_blocks: { type: integer, description: Blocks whose mining pool could not be identified }
        unattributed_share: { type: number }

    PoolsIndex:
      type: object
      required: [asOf, window, blocks_analyzed, baseline_window, latest_block_height, network, pools, caveats, source]
      properties:
        asOf:
          type: string
          format: date-time
          description: Timestamp of the latest ingested block — the data date, not the render date
        window: { type: string, enum: [24h, 7d, 30d, 1y] }
        blocks_analyzed: { type: integer }
        baseline_window: { type: string, example: 90d }
        latest_block_height: { type: integer }
        latest_block_time: { type: string, format: date-time }
        network: { $ref: '#/components/schemas/PoolsNetworkAggregates' }
        pools:
          type: array
          items: { $ref: '#/components/schemas/PoolEconomics' }
        caveats:
          type: object
          additionalProperties: { type: string }
          description: Honest limitations of each metric, shipped with every response. Read them before quoting numbers.
        source: { type: string, example: self-hosted Bitcoin Core }

    PoolHistoryPoint:
      type: object
      required: [date, blocks, share]
      properties:
        date: { type: string, format: date }
        blocks: { type: integer, description: Blocks found by the pool that day }
        share: { type: number, description: Share of blocks found by the pool that day }

    PoolDetail:
      type: object
      required: [asOf, window, baseline_window, blocks_analyzed, latest_block_height, pool, history, network, caveats, source]
      properties:
        asOf: { type: string, format: date-time }
        window: { type: string, enum: [24h, 7d, 30d, 1y] }
        baseline_window: { type: string, example: 90d }
        blocks_analyzed: { type: integer, description: Total network blocks in the window (all pools) }
        latest_block_height: { type: integer }
        latest_block_time: { type: string, format: date-time }
        pool: { $ref: '#/components/schemas/PoolEconomics' }
        history:
          type: array
          items: { $ref: '#/components/schemas/PoolHistoryPoint' }
        network: { $ref: '#/components/schemas/PoolsNetworkAggregates' }
        coverage_since:
          type: string
          format: date-time
          description: Start of node-derived block coverage (2012-12-31, block height 214543)
        caveats:
          type: object
          additionalProperties: { type: string }
        source: { type: string, example: self-hosted Bitcoin Core }

    Halving:
      type: object
      required: [current, next, supply, history]
      properties:
        current:
          type: object
          properties:
            era: { type: integer }
            block_reward: { type: number }
            started_block: { type: integer }
            started_date: { type: string, format: date }
        next:
          type: object
          properties:
            era: { type: integer }
            block_reward: { type: number }
            target_block: { type: integer }
            blocks_remaining: { type: integer }
            days_remaining: { type: integer }
            estimated_date: { type: string, format: date }
        supply:
          type: object
          properties:
            total_mined: { type: number }
            max_supply: { type: integer }
            percent_mined: { type: number }
            remaining: { type: number }
        history:
          type: array
          items:
            type: object
            properties:
              era: { type: integer }
              block: { type: integer }
              date: { type: string, format: date }
              reward: { type: number }
        block_height: { type: integer }

    NetworkMarket:
      type: object
      required: [timestamp, btc, network, mining]
      properties:
        timestamp: { type: string, format: date-time }
        btc:
          type: object
          properties:
            price_usd: { type: number }
            change_24h_pct: { type: number }
            market_cap_usd: { type: number }
            volume_24h_usd: { type: number }
        network:
          type: object
          properties:
            difficulty: { type: number }
            difficulty_formatted: { type: string }
            hashrate_eh: { type: number }
            block_height: { type: integer }
            blocks_until_adjustment: { type: integer }
            block_time_sma_seconds: { type: integer }
            block_time_sma_formatted: { type: string }
        mining:
          type: object
          properties:
            hashprice_usd_ph_day: { type: number }
            hashprice_btc_ph_day: { type: number }
            hashprice_change_24h_pct: { type: number }
            block_subsidy: { type: number }
            effective_reward: { type: number }
            tx_fees_avg_btc: { type: number }
            tx_fees_pct: { type: number }
        sources:
          type: object
          additionalProperties: { type: string }

    DifficultyEstimate:
      type: object
      required: [timestamp, current, next_adjustment]
      properties:
        timestamp: { type: string, format: date-time }
        current:
          type: object
          properties:
            difficulty: { type: number }
            difficulty_formatted: { type: string }
            height: { type: integer }
        previous_adjustment:
          type: object
          properties:
            change_percent: { type: number }
            date: { type: string, format: date }
            block_height: { type: integer }
            from_difficulty_formatted: { type: string }
            to_difficulty_formatted: { type: string }
        next_adjustment:
          type: object
          properties:
            estimated_difficulty: { type: number }
            estimated_difficulty_formatted: { type: string }
            change_percent: { type: number }
            blocks_remaining: { type: integer }
            estimated_date: { type: string, format: date-time }
            epoch_progress_percent: { type: number }
            epoch_avg_block_time_seconds: { type: integer }
        hashrate_eh: { type: number }
        source: { type: string, example: bitcoin_node }

    AsicPrice:
      type: object
      required: [model, hashrate, price_per_th, unit_price]
      properties:
        model: { type: string, description: ASIC model short name (e.g. "S21 XP") }
        hashrate: { type: number, description: Hashrate in TH/s }
        price_per_th: { type: number, description: Market price normalized in $/TH }
        unit_price: { type: number, description: Market unit price in USD }
        efficiency_jt:
          type: number
          nullable: true
          description: Efficiency in J/TH if known

    PoolsHistory:
      type: object
      required: [since, clamped_since, bucket, weeks, pool_names, source]
      properties:
        since: { type: string, format: date, description: Effective lower bound (floored to 2022-01-01) }
        clamped_since: { type: boolean, description: 'true if the request was floored to 2022-01-01' }
        bucket: { type: string, enum: [week] }
        weeks:
          type: array
          items:
            type: object
            required: [week, total_blocks, pools, unattributed_blocks]
            properties:
              week: { type: string, format: date, description: ISO week start (Monday) }
              total_blocks: { type: integer }
              pools:
                type: object
                additionalProperties: { type: integer }
                description: Blocks found per identified pool slug this week
              unattributed_blocks: { type: integer, description: Blocks whose coinbase signature was not recognized (not a pool) }
              nakamoto_coefficient: { type: integer, nullable: true, description: Smallest number of pools exceeding 50% (excludes unattributed); null on an empty week }
              top3_share: { type: number, nullable: true, description: Summed share of the top 3 identified pools (0-1); null on an empty week }
        pool_names:
          type: object
          additionalProperties: { type: string }
          description: Slug to display name for identified pools
        source: { type: string }

    AsicPrices:
      type: object
      required: [generated_at, total_models, latest_prices]
      properties:
        generated_at: { type: string, format: date-time }
        total_models: { type: integer }
        latest_prices:
          type: array
          items: { $ref: '#/components/schemas/AsicPrice' }

    Error:
      type: object
      required: [error]
      properties:
        error: { type: string, example: rate_limited }
        message: { type: string }

  responses:
    RateLimited:
      description: Too many requests — slow down.
      headers:
        Retry-After:
          schema: { type: integer }
          description: Seconds to wait before retrying.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
          example:
            error: rate_limited
            message: Free tier — slow down or contact contact@startmining.io for Enterprise.

    InternalError:
      description: Upstream Bitcoin Core node or aggregator is unreachable.
      content:
        application/json:
          schema: { $ref: '#/components/schemas/Error' }
          example:
            error: upstream_unavailable
            message: Bitcoin Core node temporarily unreachable. Retry shortly.

  securitySchemes:
    EnterpriseApiKey:
      type: apiKey
      in: header
      name: x-api-key
      description: |
        **Free tier accepts anonymous requests** — no API key required.
        The `x-api-key` header is reserved for Enterprise tier (custom rate
        limits, SLA, support, private deployment). Request access at
        contact@startmining.io.

security:
  - {} # Anonymous access permitted on Free tier
  - EnterpriseApiKey: []
