openapi: 3.1.0
info:
  title: SQD Portal API - Bitcoin Dataset Endpoints
  description: API endpoints for interacting with Bitcoin datasets under the SQD Portal, specifically for the bitcoin-mainnet dataset.
  version: 1.0.0
servers:
  - url: https://portal.sqd.dev/datasets/bitcoin-mainnet
    description: SQD Portal's endpoint for Bitcoin Mainnet
paths:
  /metadata:
    get:
      summary: Dataset Metadata
      operationId: getBitcoinDatasetMetadata
      x-mint:
        href: "/en/api/bitcoin/metadata"
        metadata:
          description: "Get Bitcoin dataset metadata from the SQD Portal API."
      x-codeSamples:
        - lang: shell
          source: |
            curl --compressed 'https://portal.sqd.dev/datasets/bitcoin-mainnet/metadata'
        - lang: python
          label: "Dataset Metadata"
          source: |
            import requests

            response = requests.get(
                "https://portal.sqd.dev/datasets/bitcoin-mainnet/metadata"
            )
            data = response.json()
        - lang: javascript
          label: "Dataset Metadata"
          source: |
            const response = await fetch("https://portal.sqd.dev/datasets/bitcoin-mainnet/metadata");
            const data = await response.json();
      description: "Retrieves metadata describing the dataset, including its name, aliases, start block, and real-time status."
      responses:
        "200":
          description: Dataset metadata response
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/DatasetMetadata"
        "404":
          description: Dataset not found (`error.code` is `unknown_dataset`)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: unknown_dataset
                  message: "Unknown dataset: no-such-chain"
  /stream:
    post:
      summary: Stream Blocks
      operationId: streamBitcoinBlocks
      x-mint:
        href: "/en/api/bitcoin/stream"
        metadata:
          description: "Stream Bitcoin blockchain data with the SQD Portal API."
      x-codeSamples:
        - lang: shell
          label: Query Taproot Inputs
          source: |
            curl --compressed -X POST 'https://portal.sqd.dev/datasets/bitcoin-mainnet/stream' \
              -H 'Content-Type: application/json' \
              -d '{
                "type": "bitcoin",
                "fromBlock": 880000,
                "toBlock": 880000,
                "fields": {
                  "block": {
                    "number": true,
                    "timestamp": true
                  },
                  "input": {
                    "transactionIndex": true,
                    "inputIndex": true,
                    "txid": true,
                    "vout": true,
                    "prevoutValue": true,
                    "prevoutScriptPubKeyType": true,
                    "prevoutScriptPubKeyAddress": true
                  }
                },
                "inputs": [{
                  "prevoutScriptPubKeyType": ["witness_v1_taproot"]
                }]
              }'
        - lang: shell
          label: Query Taproot Outputs
          source: |
            curl --compressed -X POST 'https://portal.sqd.dev/datasets/bitcoin-mainnet/stream' \
              -H 'Content-Type: application/json' \
              -d '{
                "type": "bitcoin",
                "fromBlock": 880000,
                "toBlock": 880000,
                "fields": {
                  "block": {
                    "number": true,
                    "timestamp": true
                  },
                  "output": {
                    "transactionIndex": true,
                    "outputIndex": true,
                    "value": true,
                    "scriptPubKeyAddress": true,
                    "scriptPubKeyType": true
                  }
                },
                "outputs": [{
                  "scriptPubKeyType": ["witness_v1_taproot"]
                }]
              }'
        - lang: python
          label: "Query Taproot Inputs"
          source: |
            import requests

            response = requests.post(
                "https://portal.sqd.dev/datasets/bitcoin-mainnet/stream",
                json={
                    "type": "bitcoin",
                    "fromBlock": 880000,
                    "toBlock": 880000,
                    "fields": {
                        "block": {
                            "number": True,
                            "timestamp": True
                        },
                        "input": {
                            "transactionIndex": True,
                            "inputIndex": True,
                            "txid": True,
                            "vout": True,
                            "prevoutValue": True,
                            "prevoutScriptPubKeyType": True,
                            "prevoutScriptPubKeyAddress": True
                        }
                    },
                    "inputs": [{
                        "prevoutScriptPubKeyType": ["witness_v1_taproot"]
                    }]
                }
            )
            data = response.json()
        - lang: javascript
          label: "Query Taproot Inputs"
          source: |
            const response = await fetch("https://portal.sqd.dev/datasets/bitcoin-mainnet/stream", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({
                    "type": "bitcoin",
                    "fromBlock": 880000,
                    "toBlock": 880000,
                    "fields": {
                        "block": {
                            "number": true,
                            "timestamp": true
                        },
                        "input": {
                            "transactionIndex": true,
                            "inputIndex": true,
                            "txid": true,
                            "vout": true,
                            "prevoutValue": true,
                            "prevoutScriptPubKeyType": true,
                            "prevoutScriptPubKeyAddress": true
                        }
                    },
                    "inputs": [{
                        "prevoutScriptPubKeyType": ["witness_v1_taproot"]
                    }]
                })
            });
            const data = await response.json();
      description: "Streams a list of blocks matching the provided data query, potentially including real-time data. Required request headers: `Content-Type: application/json`; optional request headers: `Accept-Encoding: gzip`, `Content-Encoding: gzip`."
      requestBody:
        description: "Data query to filter and retrieve blocks. Request body may be gzipped (Content-Encoding: gzip)."
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DataQuery"
            examples:
              taproot-inputs:
                summary: Query Taproot Inputs
                value:
                  type: bitcoin
                  fromBlock: 880000
                  toBlock: 880000
                  fields:
                    block:
                      number: true
                      timestamp: true
                    input:
                      transactionIndex: true
                      inputIndex: true
                      txid: true
                      vout: true
                      prevoutValue: true
                      prevoutScriptPubKeyType: true
                      prevoutScriptPubKeyAddress: true
                  inputs:
                    - prevoutScriptPubKeyType:
                        - "witness_v1_taproot"
              taproot-outputs:
                summary: Query Taproot Outputs
                value:
                  type: bitcoin
                  fromBlock: 880000
                  toBlock: 880000
                  fields:
                    block:
                      number: true
                      timestamp: true
                    output:
                      transactionIndex: true
                      outputIndex: true
                      value: true
                      scriptPubKeyAddress: true
                      scriptPubKeyType: true
                  outputs:
                    - scriptPubKeyType:
                        - "witness_v1_taproot"
      responses:
        "200":
          description: A stream of blocks in JSON lines format, optionally gzipped. Can be empty if the data query has a bounded range and all blocks in the range have been skipped. May include X-Sqd-Finalized-Head-Number and X-Sqd-Finalized-Head-Hash headers, indicating the latest finalized block that's on the same chain as the returned blocks.
          headers:
            X-Sqd-Finalized-Head-Number:
              schema:
                type: integer
                format: int64
              description: Block number of the latest finalized block. Returned blocks can be above, at, or below this block number.
              required: false
            X-Sqd-Finalized-Head-Hash:
              schema:
                type: string
              description: Hash of the latest finalized block. All returned blocks are guaranteed to belong to the same (not necessarily final) chain as this block.
              required: false
          content:
            application/jsonl:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Block"
              examples:
                taproot-inputs:
                  summary: Taproot inputs response
                  value:
                    - header:
                        number: 880000
                        hash: "00000000000000000000a3b049e5e4e16c539c9a0b3c7d8f1e2a4b6c8d0e1f2a"
                        parentHash: "00000000000000000000b4c150f6f5f27d640a0b1c4d8e9f2f3b5c7d9e1f0a3b"
                        timestamp: 1738000000
                      transactions: []
                      inputs:
                        - transactionIndex: 1
                          inputIndex: 0
                          txid: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
                          vout: 0
                          prevoutValue: 0.0005
                          prevoutScriptPubKeyType: "witness_v1_taproot"
                          prevoutScriptPubKeyAddress: "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297"
                        - transactionIndex: 2
                          inputIndex: 0
                          txid: "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3"
                          vout: 1
                          prevoutValue: 0.00125
                          prevoutScriptPubKeyType: "witness_v1_taproot"
                          prevoutScriptPubKeyAddress: "bc1pxyz0987654321abcdefghijklmnopqrstuvwxyz012345678901234"
                      outputs: []
        "204":
          description: No new blocks available in the requested range - the range is entirely above the current dataset head. Not an error and the response has no body; wait and retry to keep following the chain head. The portal may wait for up to 5s before returning this.
          headers:
            X-Sqd-Finalized-Head-Number:
              schema:
                type: integer
                format: int64
              description: Block number of the latest finalized block.
              required: false
            X-Sqd-Finalized-Head-Hash:
              schema:
                type: string
              description: Hash of the latest finalized block.
              required: false
        "400":
          description: >
            Invalid request or query (`error.code` is `malformed_request`; `error.param` names the request field at fault when one is). Possible causes: (1) request headers or body encoding are incorrect; (2) the query is invalid - `error.message` includes an explanation; (3) fromBlock is below the dataset's start_block (see /metadata).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: malformed_request
                  message: "Bad request: fromBlock must be a non-negative integer"
                  param: fromBlock
        "404":
          description: Dataset not found (`error.code` is `unknown_dataset`)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: unknown_dataset
                  message: "Unknown dataset: no-such-chain"
        "409":
          description: Parent block hash mismatch (`error.code` is `base_block_mismatch`) - the query's parentBlockHash does not match the canonical parent of the first requested block, typically after a chain reorganization. Walk the top-level previousBlocks list to find the most recent block you have already processed, roll back your data to it, then resume the stream from the next block, passing that block's hash as parentBlockHash. If nothing in the list matches your records, restart from an earlier fromBlock, again with parentBlockHash.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConflictResponse"
        "429":
          description: Rate limited (`error.type` is `rate_limit_error`). Retry after the interval in the Retry-After header; it is always present and counts seconds
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: rate_limit_error
                  code: overloaded
                  message: Service is overloaded, please try again later
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
        "500":
          description: Internal server error (`error.type` is `api_error`). Do not retry; report the error together with its request_id
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: api_error
                  code: internal_error
                  message: Internal server error
                  request_id: "0198c3f1-..."
        "502":
          description: A data source the Portal depends on is unavailable (`error.code` is `upstream_unavailable`) - retry later. A proxied upstream failure keeps the upstream's own 5xx status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: availability_error
                  code: upstream_unavailable
                  message: Upstream data source is unavailable, please try again later
                  request_id: "0198c3f1-..."
        "503":
          description: Service temporarily unavailable (`error.type` is `availability_error`, typically `no_workers` or `retries_exhausted`) - retry later. May carry Retry-After; honor it when present
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: availability_error
                  code: no_workers
                  message: No workers available for dataset
                  request_id: "0198c3f1-..."
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
        "529":
          description: Overloaded (`error.code` is `overloaded`) - the Portal or a data source is at capacity. Retry after the interval in the Retry-After header; it is always present and counts seconds
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: rate_limit_error
                  code: overloaded
                  message: Service is overloaded, please try again later
                  request_id: "0198c3f1-..."
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
  /finalized-stream:
    post:
      summary: Stream Finalized Blocks
      operationId: streamBitcoinFinalizedBlocks
      x-mint:
        href: "/en/api/bitcoin/finalized-stream"
        metadata:
          description: "Stream finalized Bitcoin blockchain data with the SQD Portal API."
      x-codeSamples:
        - lang: shell
          label: Query Taproot Inputs
          source: |
            curl --compressed -X POST 'https://portal.sqd.dev/datasets/bitcoin-mainnet/finalized-stream' \
              -H 'Content-Type: application/json' \
              -d '{
                "type": "bitcoin",
                "fromBlock": 880000,
                "toBlock": 880000,
                "fields": {
                  "block": {
                    "number": true,
                    "timestamp": true
                  },
                  "input": {
                    "transactionIndex": true,
                    "inputIndex": true,
                    "txid": true,
                    "vout": true,
                    "prevoutValue": true,
                    "prevoutScriptPubKeyType": true,
                    "prevoutScriptPubKeyAddress": true
                  }
                },
                "inputs": [{
                  "prevoutScriptPubKeyType": ["witness_v1_taproot"]
                }]
              }'
        - lang: shell
          label: Query Taproot Outputs
          source: |
            curl --compressed -X POST 'https://portal.sqd.dev/datasets/bitcoin-mainnet/finalized-stream' \
              -H 'Content-Type: application/json' \
              -d '{
                "type": "bitcoin",
                "fromBlock": 880000,
                "toBlock": 880000,
                "fields": {
                  "block": {
                    "number": true,
                    "timestamp": true
                  },
                  "output": {
                    "transactionIndex": true,
                    "outputIndex": true,
                    "value": true,
                    "scriptPubKeyAddress": true,
                    "scriptPubKeyType": true
                  }
                },
                "outputs": [{
                  "scriptPubKeyType": ["witness_v1_taproot"]
                }]
              }'
        - lang: python
          label: "Query Taproot Inputs"
          source: |
            import requests

            response = requests.post(
                "https://portal.sqd.dev/datasets/bitcoin-mainnet/finalized-stream",
                json={
                    "type": "bitcoin",
                    "fromBlock": 880000,
                    "toBlock": 880000,
                    "fields": {
                        "block": {
                            "number": True,
                            "timestamp": True
                        },
                        "input": {
                            "transactionIndex": True,
                            "inputIndex": True,
                            "txid": True,
                            "vout": True,
                            "prevoutValue": True,
                            "prevoutScriptPubKeyType": True,
                            "prevoutScriptPubKeyAddress": True
                        }
                    },
                    "inputs": [{
                        "prevoutScriptPubKeyType": ["witness_v1_taproot"]
                    }]
                }
            )
            data = response.json()
        - lang: javascript
          label: "Query Taproot Inputs"
          source: |
            const response = await fetch("https://portal.sqd.dev/datasets/bitcoin-mainnet/finalized-stream", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({
                    "type": "bitcoin",
                    "fromBlock": 880000,
                    "toBlock": 880000,
                    "fields": {
                        "block": {
                            "number": true,
                            "timestamp": true
                        },
                        "input": {
                            "transactionIndex": true,
                            "inputIndex": true,
                            "txid": true,
                            "vout": true,
                            "prevoutValue": true,
                            "prevoutScriptPubKeyType": true,
                            "prevoutScriptPubKeyAddress": true
                        }
                    },
                    "inputs": [{
                        "prevoutScriptPubKeyType": ["witness_v1_taproot"]
                    }]
                })
            });
            const data = await response.json();
      description: "Streams only finalized blocks matching the provided data query (finalized data does not reorganize; a 409 here only means the provided parentBlockHash is stale). Query structure is identical to that of the /stream endpoint. Required request headers: `Content-Type: application/json`; optional request headers: `Accept-Encoding: gzip`, `Content-Encoding: gzip`."
      requestBody:
        description: "Data query to filter and retrieve blocks. Request body may be gzipped (Content-Encoding: gzip)."
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/DataQuery"
            examples:
              taproot-inputs:
                summary: Query Taproot Inputs
                value:
                  type: bitcoin
                  fromBlock: 880000
                  toBlock: 880000
                  fields:
                    block:
                      number: true
                      timestamp: true
                    input:
                      transactionIndex: true
                      inputIndex: true
                      txid: true
                      vout: true
                      prevoutValue: true
                      prevoutScriptPubKeyType: true
                      prevoutScriptPubKeyAddress: true
                  inputs:
                    - prevoutScriptPubKeyType:
                        - "witness_v1_taproot"
              taproot-outputs:
                summary: Query Taproot Outputs
                value:
                  type: bitcoin
                  fromBlock: 880000
                  toBlock: 880000
                  fields:
                    block:
                      number: true
                      timestamp: true
                    output:
                      transactionIndex: true
                      outputIndex: true
                      value: true
                      scriptPubKeyAddress: true
                      scriptPubKeyType: true
                  outputs:
                    - scriptPubKeyType:
                        - "witness_v1_taproot"
      responses:
        "200":
          description: A stream of finalized blocks in JSON lines format, optionally gzipped. Never returns blocks above the finalized head.
          content:
            application/jsonl:
              schema:
                type: array
                items:
                  $ref: "#/components/schemas/Block"
              examples:
                taproot-inputs:
                  summary: Taproot inputs response
                  value:
                    - header:
                        number: 880000
                        hash: "00000000000000000000a3b049e5e4e16c539c9a0b3c7d8f1e2a4b6c8d0e1f2a"
                        parentHash: "00000000000000000000b4c150f6f5f27d640a0b1c4d8e9f2f3b5c7d9e1f0a3b"
                        timestamp: 1738000000
                      transactions: []
                      inputs:
                        - transactionIndex: 1
                          inputIndex: 0
                          txid: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2"
                          vout: 0
                          prevoutValue: 0.0005
                          prevoutScriptPubKeyType: "witness_v1_taproot"
                          prevoutScriptPubKeyAddress: "bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297"
                        - transactionIndex: 2
                          inputIndex: 0
                          txid: "b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3"
                          vout: 1
                          prevoutValue: 0.00125
                          prevoutScriptPubKeyType: "witness_v1_taproot"
                          prevoutScriptPubKeyAddress: "bc1pxyz0987654321abcdefghijklmnopqrstuvwxyz012345678901234"
                      outputs: []
        "204":
          description: No new blocks available in the requested range - the range is entirely above the current dataset head. Not an error and the response has no body; wait and retry to keep following the chain head. The portal may wait for up to 5s before returning this.
        "400":
          description: >
            Invalid request or query (`error.code` is `malformed_request`; `error.param` names the request field at fault when one is). Possible causes: (1) request headers or body encoding are incorrect; (2) the query is invalid - `error.message` includes an explanation; (3) fromBlock is below the dataset's start_block (see /metadata).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: malformed_request
                  message: "Bad request: fromBlock must be a non-negative integer"
                  param: fromBlock
        "404":
          description: Dataset not found (`error.code` is `unknown_dataset`)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: unknown_dataset
                  message: "Unknown dataset: no-such-chain"
        "409":
          description: Parent block hash mismatch (`error.code` is `base_block_mismatch`). Finalized data does not reorganize, so the provided parentBlockHash is stale or was carried over from an unfinalized block. Recover as for /stream by walking the top-level previousBlocks list.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ConflictResponse"
        "429":
          description: Rate limited (`error.type` is `rate_limit_error`). Retry after the interval in the Retry-After header; it is always present and counts seconds
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: rate_limit_error
                  code: overloaded
                  message: Service is overloaded, please try again later
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
        "500":
          description: Internal server error (`error.type` is `api_error`). Do not retry; report the error together with its request_id
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: api_error
                  code: internal_error
                  message: Internal server error
                  request_id: "0198c3f1-..."
        "502":
          description: A data source the Portal depends on is unavailable (`error.code` is `upstream_unavailable`) - retry later. A proxied upstream failure keeps the upstream's own 5xx status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: availability_error
                  code: upstream_unavailable
                  message: Upstream data source is unavailable, please try again later
                  request_id: "0198c3f1-..."
        "503":
          description: Service temporarily unavailable (`error.type` is `availability_error`, typically `no_workers` or `retries_exhausted`) - retry later. May carry Retry-After; honor it when present
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: availability_error
                  code: no_workers
                  message: No workers available for dataset
                  request_id: "0198c3f1-..."
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
        "529":
          description: Overloaded (`error.code` is `overloaded`) - the Portal or a data source is at capacity. Retry after the interval in the Retry-After header; it is always present and counts seconds
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: rate_limit_error
                  code: overloaded
                  message: Service is overloaded, please try again later
                  request_id: "0198c3f1-..."
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
  /head:
    get:
      summary: Dataset Head
      operationId: getBitcoinHead
      x-mint:
        href: "/en/api/bitcoin/head"
        metadata:
          description: "Get the latest Bitcoin block number and hash available from SQD Portal."
      x-codeSamples:
        - lang: shell
          source: |
            curl --compressed 'https://portal.sqd.dev/datasets/bitcoin-mainnet/head'
        - lang: python
          label: "Dataset Head"
          source: |
            import requests

            response = requests.get(
                "https://portal.sqd.dev/datasets/bitcoin-mainnet/head"
            )
            data = response.json()
        - lang: javascript
          label: "Dataset Head"
          source: |
            const response = await fetch("https://portal.sqd.dev/datasets/bitcoin-mainnet/head");
            const data = await response.json();
      description: "Returns the block number and hash of the highest block available in the dataset, or null if no blocks are available. Takes real-time data into account. Useful mostly for diagnostics, as clients using /stream can get this info from the X-Sqd-Finalized-Head-Number and X-Sqd-Finalized-Head-Hash response headers."
      responses:
        "200":
          description: Block head info
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BlockHead"
        "404":
          description: Dataset not found (`error.code` is `unknown_dataset`)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: unknown_dataset
                  message: "Unknown dataset: no-such-chain"
  /finalized-head:
    get:
      summary: Finalized Dataset Head
      operationId: getBitcoinFinalizedHead
      x-mint:
        href: "/en/api/bitcoin/finalized-head"
        metadata:
          description: "Get the latest finalized Bitcoin block number and hash from SQD Portal."
      x-codeSamples:
        - lang: shell
          source: |
            curl --compressed 'https://portal.sqd.dev/datasets/bitcoin-mainnet/finalized-head'
        - lang: python
          label: "Finalized Dataset Head"
          source: |
            import requests

            response = requests.get(
                "https://portal.sqd.dev/datasets/bitcoin-mainnet/finalized-head"
            )
            data = response.json()
        - lang: javascript
          label: "Finalized Dataset Head"
          source: |
            const response = await fetch("https://portal.sqd.dev/datasets/bitcoin-mainnet/finalized-head");
            const data = await response.json();
      description: "Returns the block number and hash of the highest finalized block available in the dataset, or null if no blocks are available. Useful mostly for diagnostics, as clients using /stream can get this info from the X-Sqd-Finalized-Head-Number and X-Sqd-Finalized-Head-Hash response headers."
      responses:
        "200":
          description: Finalized block head info
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BlockHead"
        "404":
          description: Dataset not found (`error.code` is `unknown_dataset`)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: unknown_dataset
                  message: "Unknown dataset: no-such-chain"
  /timestamps/{timestamp}/block:
    get:
      summary: Block at Timestamp
      operationId: resolveBitcoinTimestampToBlock
      x-mint:
        href: "/en/api/bitcoin/timestamp-block"
        metadata:
          description: "Resolve a Unix timestamp to a Bitcoin block with the SQD Portal API."
      x-codeSamples:
        - lang: shell
          label: Resolve timestamp to block
          source: |
            curl 'https://portal.sqd.dev/datasets/bitcoin-mainnet/timestamps/1700000000/block'
        - lang: python
          label: Resolve timestamp to block
          source: |
            import requests

            response = requests.get(
                "https://portal.sqd.dev/datasets/bitcoin-mainnet/timestamps/1700000000/block"
            )
            block_number = response.json()["block_number"]
        - lang: javascript
          label: Resolve timestamp to block
          source: |
            const response = await fetch("https://portal.sqd.dev/datasets/bitcoin-mainnet/timestamps/1700000000/block");
            const { block_number } = await response.json();
      description: >
        Resolves a Unix timestamp (in seconds) to a block number. Returns the first block whose
        timestamp is greater than or equal to `timestamp`. Resolution prefers archival data and
        falls back to the real-time source when available; the `x-sqd-data-source` response header
        reports which source served the result (`network` or `real_time`).
      parameters:
        - name: timestamp
          in: path
          required: true
          schema:
            type: integer
            format: int64
            default: 1700000000
            example: 1700000000
          description: Unix timestamp in seconds.
      responses:
        "200":
          description: Block number resolved.
          headers:
            x-sqd-data-source:
              schema:
                type: string
                enum: [network, real_time]
              description: Source that served the result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/BlockNumberResponse"
              examples:
                "bitcoin-mainnet":
                  summary: First block at or after the timestamp
                  value:
                    block_number: 816786
        "400":
          description: Unparseable `timestamp` path segment (`error.code` is `malformed_request`), or the real-time source refused the query the Portal generated on the client's behalf; read `error.message`
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: malformed_request
                  message: "Bad request: fromBlock must be a non-negative integer"
                  param: fromBlock
        "404":
          description: No block at or after the given timestamp yet (`error.code` is `not_found`)
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: invalid_request_error
                  code: not_found
                  message: No block at or after the given timestamp
        "429":
          description: Rate limited (`error.type` is `rate_limit_error`). Retry after the interval in the Retry-After header; it is always present and counts seconds
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: rate_limit_error
                  code: overloaded
                  message: Service is overloaded, please try again later
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
        "500":
          description: Internal server error (`error.type` is `api_error`). Do not retry; report the error together with its request_id
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: api_error
                  code: internal_error
                  message: Internal server error
                  request_id: "0198c3f1-..."
        "502":
          description: A data source the Portal depends on is unavailable (`error.code` is `upstream_unavailable`) - retry later. A proxied upstream failure keeps the upstream's own 5xx status
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: availability_error
                  code: upstream_unavailable
                  message: Upstream data source is unavailable, please try again later
                  request_id: "0198c3f1-..."
        "503":
          description: Service temporarily unavailable (`error.type` is `availability_error`, typically `no_workers` or `retries_exhausted`) - retry later. May carry Retry-After; honor it when present
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: availability_error
                  code: no_workers
                  message: No workers available for dataset
                  request_id: "0198c3f1-..."
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
        "529":
          description: Overloaded (`error.code` is `overloaded`) - the Portal or a data source is at capacity. Retry after the interval in the Retry-After header; it is always present and counts seconds
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ErrorResponse"
              example:
                error:
                  type: rate_limit_error
                  code: overloaded
                  message: Service is overloaded, please try again later
                  request_id: "0198c3f1-..."
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retrying (at least 1)
components:
  schemas:
    BlockNumberResponse:
      type: object
      properties:
        block_number:
          type: integer
          format: int64
          description: Number of the first block whose timestamp is greater than or equal to the requested timestamp.
          example: 18000000
      required:
        - block_number
    DatasetMetadata:
      type: object
      properties:
        dataset:
          type: string
          description: The default name used to reference this dataset
        aliases:
          type: array
          items:
            type: string
          description: Alternative names for the dataset
        real_time:
          type: boolean
          description: Indicates if the dataset has real-time data
        start_block:
          type: integer
          format: int64
          description: The block number of the first known block
      required:
        - dataset
        - aliases
        - real_time
    DataQuery:
      type: object
      properties:
        type:
          type: string
          enum: ["bitcoin"]
          default: "bitcoin"
        fromBlock:
          type: integer
          format: int64
          description: The number of the first block to fetch. If unsure how far into the past this can go consult /metadata
          default: 880000
        toBlock:
          type: integer
          format: int64
          description: "The block number to fetch up to (inclusive). Optional; if omitted, streams until dataset height or timeout."
          default: 880000
        parentBlockHash:
          type: string
          description: Expected hash of the parent of the first requested block
        includeAllBlocks:
          type: boolean
          description: If true, includes blocks with no matching data
          default: true
        fields:
          type: object
          description: Field selector. See the HTTP 200 response description for details on fields that aren't self-explanatory
          properties:
            block:
              type: object
              description: Field selector for block headers (the .header objects of JSON block records)
              properties:
                number:
                  type: boolean
                  description: Block height
                hash:
                  type: boolean
                  description: Block hash
                parentHash:
                  type: boolean
                  description: Hash of the previous block
                timestamp:
                  type: boolean
                  description: Block timestamp in seconds since Unix epoch
                medianTime:
                  type: boolean
                  description: Median time past (BIP 113)
                version:
                  type: boolean
                  description: Block version field
                merkleRoot:
                  type: boolean
                  description: Merkle root of all transactions in the block
                nonce:
                  type: boolean
                  description: Nonce used in proof-of-work
                target:
                  type: boolean
                  description: Proof-of-work target for this block
                bits:
                  type: boolean
                  description: Compact representation of the target
                difficulty:
                  type: boolean
                chainWork:
                  type: boolean
                  description: Total work in the chain up to and including this block
                strippedSize:
                  type: boolean
                  description: Block size without witness data
                size:
                  type: boolean
                  description: Total block size in bytes
                weight:
                  type: boolean
                  description: Block weight in weight units (BIP 141)
            transaction:
              type: object
              description: Field selector for transactions
              properties:
                transactionIndex:
                  type: boolean
                  description: Index of the transaction within the block
                hex:
                  type: boolean
                  description: Raw transaction hex
                txid:
                  type: boolean
                  description: Transaction ID (double SHA-256 of serialized transaction without witness)
                hash:
                  type: boolean
                  description: Transaction hash (includes witness data, differs from txid for SegWit transactions)
                size:
                  type: boolean
                  description: Transaction size in bytes
                vsize:
                  type: boolean
                  description: Virtual size (weight / 4)
                weight:
                  type: boolean
                  description: Transaction weight in weight units
                version:
                  type: boolean
                  description: Transaction version number
                locktime:
                  type: boolean
                  description: Block height or timestamp after which the transaction is final
            input:
              type: object
              description: Field selector for transaction inputs
              properties:
                transactionIndex:
                  type: boolean
                  description: Index of the parent transaction within the block
                inputIndex:
                  type: boolean
                  description: Index of this input within the transaction
                type:
                  type: boolean
                  description: Input type
                txid:
                  type: boolean
                  description: Transaction ID of the output being spent
                vout:
                  type: boolean
                  description: Index of the output being spent in the referenced transaction
                scriptSigHex:
                  type: boolean
                  description: ScriptSig in hex encoding
                scriptSigAsm:
                  type: boolean
                  description: ScriptSig in human-readable ASM format
                sequence:
                  type: boolean
                  description: Sequence number of the input
                coinbase:
                  type: boolean
                  description: Coinbase data (only present in coinbase transactions)
                txInWitness:
                  type: boolean
                  description: Witness data for the input (SegWit)
                prevoutGenerated:
                  type: boolean
                  description: Whether the spent output was a coinbase output
                prevoutHeight:
                  type: boolean
                  description: Block height of the spent output
                prevoutValue:
                  type: boolean
                  description: Value of the spent output in BTC (decimal float, not satoshis). null for coinbase inputs.
                prevoutScriptPubKeyHex:
                  type: boolean
                  description: ScriptPubKey of the spent output in hex encoding
                prevoutScriptPubKeyAsm:
                  type: boolean
                  description: ScriptPubKey of the spent output in ASM format
                prevoutScriptPubKeyDesc:
                  type: boolean
                  description: Output descriptor of the spent output script
                prevoutScriptPubKeyType:
                  type: boolean
                  description: Script type of the spent output (e.g. pubkeyhash, witness_v1_taproot)
                prevoutScriptPubKeyAddress:
                  type: boolean
                  description: Address of the spent output
            output:
              type: object
              description: Field selector for transaction outputs
              properties:
                transactionIndex:
                  type: boolean
                  description: Index of the parent transaction within the block
                outputIndex:
                  type: boolean
                  description: Index of this output within the transaction
                value:
                  type: boolean
                  description: Output value in BTC (decimal float, not satoshis)
                scriptPubKeyHex:
                  type: boolean
                  description: ScriptPubKey in hex encoding
                scriptPubKeyAsm:
                  type: boolean
                  description: ScriptPubKey in human-readable ASM format
                scriptPubKeyDesc:
                  type: boolean
                  description: Output descriptor of the script
                scriptPubKeyType:
                  type: boolean
                  description: "Output type (e.g. pubkeyhash, scripthash, witness_v0_keyhash, witness_v1_taproot, nulldata)"
                scriptPubKeyAddress:
                  type: boolean
                  description: Output address
          default:
            block:
              number: true
              hash: true
              timestamp: true
            transaction:
              transactionIndex: true
              txid: true
              size: true
              weight: true
            input:
              transactionIndex: true
              inputIndex: true
              prevoutValue: true
              prevoutScriptPubKeyAddress: true
            output:
              transactionIndex: true
              outputIndex: true
              value: true
              scriptPubKeyAddress: true
        transactions:
          type: array
          description: Transaction data requests. An empty object matches all transactions
          default:
            - inputs: true
              outputs: true
          items:
            type: object
            properties:
              inputs:
                description: Fetch all inputs for matching transactions
                type: boolean
              outputs:
                description: Fetch all outputs for matching transactions
                type: boolean
        inputs:
          type: array
          description: Input data requests. Filter by prevout address, type, or coinbase status
          items:
            type: object
            properties:
              type:
                description: Filter by input type
                type: array
                items:
                  type: string
              prevoutScriptPubKeyAddress:
                description: Filter by the address of the spent output
                type: array
                items:
                  type: string
              prevoutScriptPubKeyType:
                description: Filter by the type of the spent output script
                type: array
                items:
                  type: string
              prevoutGenerated:
                description: Filter by whether the spent output was a coinbase output
                type: boolean
              transaction:
                description: Fetch parent transactions for all matching inputs
                type: boolean
              transactionInputs:
                description: Fetch all sibling inputs of matching inputs' parent transactions
                type: boolean
              transactionOutputs:
                description: Fetch all outputs of matching inputs' parent transactions
                type: boolean
        outputs:
          type: array
          description: Output data requests. Filter by address or script type
          items:
            type: object
            properties:
              scriptPubKeyAddress:
                description: Filter by output address
                type: array
                items:
                  type: string
              scriptPubKeyType:
                description: Filter by output script type
                type: array
                items:
                  type: string
              transaction:
                description: Fetch parent transactions for all matching outputs
                type: boolean
              transactionInputs:
                description: Fetch all inputs of matching outputs' parent transactions
                type: boolean
              transactionOutputs:
                description: Fetch all sibling outputs of matching outputs' parent transactions
                type: boolean
      required:
        - fromBlock
    Block:
      type: object
      properties:
        header:
          type: object
          properties:
            number:
              type: integer
              format: uint64
            hash:
              type: string
            parentHash:
              type: string
            timestamp:
              type: integer
              format: uint64
              description: Block timestamp in seconds since Unix epoch
            medianTime:
              type: integer
              format: uint64
              description: Median time past (BIP 113)
            version:
              type: integer
            merkleRoot:
              type: string
            nonce:
              type: integer
              format: uint64
            target:
              type: string
            bits:
              type: string
            difficulty:
              type: number
              format: double
            chainWork:
              type: string
            strippedSize:
              type: integer
            size:
              type: integer
            weight:
              type: integer
        transactions:
          type: array
          items:
            type: object
            properties:
              transactionIndex:
                type: integer
              hex:
                type: string
                description: Raw transaction hex
              txid:
                type: string
                description: Transaction ID
              hash:
                type: string
                description: Transaction hash (includes witness data)
              size:
                type: integer
              vsize:
                type: integer
                description: Virtual size (weight / 4)
              weight:
                type: integer
              version:
                type: integer
              locktime:
                type: integer
        inputs:
          type: array
          items:
            type: object
            properties:
              transactionIndex:
                type: integer
              inputIndex:
                type: integer
              type:
                type: string
              txid:
                type: string
                description: Transaction ID of the output being spent
              vout:
                type: integer
                description: Index of the output being spent
              scriptSigHex:
                type: string
              scriptSigAsm:
                type: string
              sequence:
                type: integer
                format: uint64
              coinbase:
                type: string
                description: Coinbase data (only present in coinbase transactions)
              txInWitness:
                type: array
                items:
                  type: string
              prevoutGenerated:
                type: boolean
                description: Whether the spent output was a coinbase output
              prevoutHeight:
                type: integer
              prevoutValue:
                type: [number, "null"]
                description: Value of the spent output in BTC (decimal float, not satoshis). null for coinbase inputs.
              prevoutScriptPubKeyHex:
                type: string
              prevoutScriptPubKeyAsm:
                type: string
              prevoutScriptPubKeyDesc:
                type: string
              prevoutScriptPubKeyType:
                type: string
              prevoutScriptPubKeyAddress:
                type: [string, "null"]
                description: Address of the spent output. null for coinbase inputs.
        outputs:
          type: array
          items:
            type: object
            properties:
              transactionIndex:
                type: integer
              outputIndex:
                type: integer
              value:
                type: number
                description: Output value in BTC (decimal float, not satoshis)
              scriptPubKeyHex:
                type: string
              scriptPubKeyAsm:
                type: string
              scriptPubKeyDesc:
                type: string
              scriptPubKeyType:
                type: string
                description: "Output type (e.g. pubkeyhash, scripthash, witness_v0_keyhash, witness_v1_taproot, nulldata)"
              scriptPubKeyAddress:
                type: string
                description: Output address
      required:
        - header
    BlockHead:
      type: [object, "null"]
      properties:
        number:
          type: integer
          format: int64
          description: Block number of the highest available block
        hash:
          type: string
    ErrorDetail:
      type: object
      description: Machine-readable error detail. Branch on `type`; match on `code` for one specific case.
      properties:
        type:
          type: string
          description: Coarse category to branch on. One of `invalid_request_error`, `authentication_error`, `permission_error`, `rate_limit_error`, `availability_error`, or `api_error`. The two credential types answer 403 and appear only on a Portal that requires an API key. Treat an unknown `code` according to its `type`.
          example: invalid_request_error
        code:
          type: string
          description: Specific cause, stable across releases. Match on this rather than on `message`.
          example: base_block_mismatch
        message:
          type: string
          description: Human-readable detail. Prose, not stable; do not parse or match on it.
        param:
          type: string
          description: The request parameter at fault, when the error is about one.
        request_id:
          type: string
          description: Echo of the `x-request-id` response header, included in the body on 5xx errors. Quote it when reporting a problem.
      required:
        - type
        - code
        - message
    ErrorResponse:
      type: object
      description: Error body shared by every failing response.
      properties:
        error:
          $ref: "#/components/schemas/ErrorDetail"
      required:
        - error
      example:
        error:
          type: invalid_request_error
          code: unknown_dataset
          message: "Unknown dataset: no-such-chain"
    ConflictResponse:
      type: object
      description: The 409 body carries the standard error envelope plus a top-level `previousBlocks` list - a slice of the current canonical chain at and below the conflict point, in ascending block-number order.
      properties:
        error:
          $ref: "#/components/schemas/ErrorDetail"
        previousBlocks:
          type: array
          description: "`{ number, hash }` pairs from the current canonical chain in ascending order; the last entry is the most recent block at or below the conflict point. Guaranteed to contain at least the parent of the requested fromBlock."
          items:
            type: object
            properties:
              number:
                type: integer
                format: int64
              hash:
                type: string
            required:
              - number
              - hash
      required:
        - error
        - previousBlocks
      example:
        error:
          type: invalid_request_error
          code: base_block_mismatch
          message: Base block mismatch
        previousBlocks:
          - number: 21780871
            hash: "0xab12cd..."
          - number: 21780872
            hash: "0xf6a96a29..."
