CryptoKeyhole Charts API

Chart data, the catalogue and downloads

GitHub OpenAPI

Operations 16

GET /assets Assets #
GET /catalog Metric catalogue #
GET /series Chart series #
GET /formula Saved formula as a series #
GET /export Chart download (Plus and higher) #
GET /sql SQL over every chart's daily values #
POST /sql SQL over every chart's daily values (long queries) #
GET /sql/schema SQL tables, columns and examples #
GET /bulk/metric One metric for every coin #
GET /bulk/files The nightly files #
GET /bulk/file/{name} One nightly file (a signed link) #
GET /vintages First-published values #
GET /vintages/charts The charts with first-published values #
GET /data-webhooks Data webhooks and destinations #
POST /data-webhooks Add a data webhook #
POST /data-webhooks/delete Delete a data webhook #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/cryptokeyhole:cryptokeyhole-charts-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

cryptokeyhole-charts-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Cryptokeyhole Charts API
  version: 1.0.0
  contact:
    name: CryptoKeyhole
    url: https://cryptokeyhole.com/data-access/
  license:
    name: CC BY 4.0 (free data; paid-plan data under the terms of service)
    url: https://cryptokeyhole.com/methodology/#licence-title
  termsOfService: https://cryptokeyhole.com/terms/
  x-logo:
    url: https://cryptokeyhole.com/favicon.svg
  description: 'Operations tagged Charts across 2 of this provider''s published API definitions: cryptokeyhole-openapi.json, cryptokeyhole-openapi.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://cryptokeyhole.com/api/v1
tags:
- name: Charts
  description: Chart data, the catalogue and downloads
paths:
  /assets:
    get:
      tags:
      - Charts
      operationId: listAssets
      summary: Assets
      description: The public assets, Bitcoin first. Every other asset's chart ids start with its prefix (for example eth.).
      security:
      - {}
      responses:
        '200':
          description: The assets
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Assets'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /catalog:
    get:
      tags:
      - Charts
      operationId: getCatalog
      summary: Metric catalogue
      description: Every chart of one asset with its id, name, unit, description, method, intervals, coverage and status. A planned chart that has not published carries status held and its reason.
      security:
      - {}
      - bearerAuth: []
      parameters:
      - name: asset
        in: query
        description: An asset code from /assets. Default BTC, or the asset of the first of ids.
        schema:
          type: string
          example: ETH
      - name: ids
        in: query
        description: 'Comma-separated chart ids of one asset: only these charts.'
        schema:
          type: string
          example: eth.PriceUSD
      - name: view
        in: query
        description: 'directory: the chart directory''s tree instead of the flat list (not with ids).'
        schema:
          type: string
          enum:
          - directory
      responses:
        '200':
          description: The catalogue
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Catalog'
        '400':
          $ref: '#/components/responses/BadRequest'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /series:
    get:
      tags:
      - Charts
      operationId: getSeries
      summary: Chart series
      description: 'One chart''s observations, one point per period, and for charts with several lines their components. Free charts at 24h, 1w, 1M and 1Y need no key. A chart or interval above the plan answers 200 with state locked and the plan it needs, or a fixed historical sample named by the X-CKH-Preview header. Price-distribution charts (catalogue distribution.type price_histogram) take one UTC day: start at midnight and end exactly one day later.'
      security:
      - {}
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/metric'
      - $ref: '#/components/parameters/interval'
      - $ref: '#/components/parameters/start'
      - $ref: '#/components/parameters/end'
      - name: limit
        in: query
        description: Most points returned (12 to 20,000, default 3,000); a longer range is reduced to it.
        schema:
          type: integer
          minimum: 12
          maximum: 20000
          default: 3000
      - $ref: '#/components/parameters/sma'
      - $ref: '#/components/parameters/smoothing'
      - $ref: '#/components/parameters/publication'
      responses:
        '200':
          description: The series, or its plan lock
          headers:
            X-CKH-Preview:
              description: 'Set when the answer is a fixed sample of a chart above the plan: plan, name, from, through (UTC seconds) and sample=1.'
              schema:
                type: string
            Link:
              description: 'Set on a free chart''s answer at a free resolution when the data is ours to license: <https://creativecommons.org/licenses/by/4.0/>; rel="license".'
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Series'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /formula:
    get:
      tags:
      - Charts
      operationId: getFormula
      summary: Saved formula as a series
      description: A saved formula read like a chart. Anyone can read a formula by the share id of its public link; with your key you can also read your own saved formulas by their id. Every input chart and the interval must be in the requester's plan. Without interval, start and end the formula's saved interval and range apply.
      security:
      - {}
      - bearerAuth: []
      parameters:
      - name: id
        in: query
        required: true
        description: A share id, or the id of one of your own saved formulas.
        schema:
          type: string
      - name: interval
        in: query
        schema:
          type: string
          enum:
          - 10m
          - 1h
          - 24h
          - 1w
          - 1M
      - $ref: '#/components/parameters/start'
      - $ref: '#/components/parameters/end'
      - name: limit
        in: query
        schema:
          type: integer
          minimum: 12
          maximum: 20000
      responses:
        '200':
          description: The formula's series
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Series'
        '400':
          $ref: '#/components/responses/BadRequest'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '404':
          $ref: '#/components/responses/NotFound'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /export:
    get:
      tags:
      - Charts
      operationId: exportSeries
      summary: Chart download (Plus and higher)
      description: The chart pages' CSV or JSON download of one chart, with every provenance column, the source, licence and attribution of each series. Included with Plus and higher plans.
      security:
      - bearerAuth: []
      parameters:
      - $ref: '#/components/parameters/metric'
      - $ref: '#/components/parameters/interval'
      - $ref: '#/components/parameters/start'
      - $ref: '#/components/parameters/end'
      - $ref: '#/components/parameters/sma'
      - $ref: '#/components/parameters/smoothing'
      - name: format
        in: query
        schema:
          type: string
          enum:
          - csv
          - json
          default: csv
      - $ref: '#/components/parameters/publication'
      responses:
        '200':
          description: The download, as an attachment
          content:
            text/csv:
              schema:
                type: string
            application/json:
              schema:
                $ref: '#/components/schemas/Series'
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /sql:
    get:
      tags:
      - Charts
      operationId: querySql
      summary: SQL over every chart's daily values
      description: 'One read-only statement (SELECT, or WITH ... SELECT; SQLite''s dialect) over every public chart''s daily values: tables series (asset, metric, chart, component, interval, day, t, value, finality), latest, metrics and assets; GET /sql/schema lists them with examples. Pro and API & Team: a key''s query counts one API call. At most 100,000 rows (truncated), 30 seconds per statement. Values are 64-bit floats; the series route keeps exact decimals. The database is read again every hour.'
      security:
      - bearerAuth: []
      parameters:
      - name: q
        in: query
        required: true
        description: The SQL statement.
        schema:
          type: string
          example: SELECT a.asset, l.value FROM assets a JOIN latest l ON l.metric = a.price ORDER BY l.value DESC
      - name: format
        in: query
        schema:
          type: string
          enum:
          - json
          - csv
          default: json
      - name: maxRows
        in: query
        schema:
          type: integer
          minimum: 1
          maximum: 100000
      responses:
        '200':
          description: columns, rows, rowCount, truncated and ms (JSON), or CSV
          content:
            application/json:
              schema:
                type: object
                properties:
                  columns:
                    type: array
                    items:
                      type: string
                  rows:
                    type: array
                    items:
                      type: array
                      items: {}
                  rowCount:
                    type: integer
                  truncated:
                    type: boolean
                  ms:
                    type: integer
            text/csv:
              schema:
                type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          description: The database is being built or the query service is busy
    post:
      tags:
      - Charts
      operationId: querySqlPost
      summary: SQL over every chart's daily values (long queries)
      description: As GET /sql, with the statement in a JSON body. A signed-in browser session sends its X-CSRF-Token.
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - query
              properties:
                query:
                  type: string
                format:
                  type: string
                  enum:
                  - json
                  - csv
                maxRows:
                  type: integer
      responses:
        '200':
          description: As GET /sql
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /sql/schema:
    get:
      tags:
      - Charts
      operationId: getSqlSchema
      summary: SQL tables, columns and examples
      description: The tables and views of the SQL database with their columns, the finality words, example queries and when the data was read. No key needed.
      security:
      - {}
      responses:
        '200':
          description: The schema
          content:
            application/json:
              schema:
                type: object
        '503':
          description: The database is being built
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /bulk/metric:
    get:
      tags:
      - Charts
      operationId: getEveryCoin
      summary: One metric for every coin
      description: 'One chart for every coin that publishes it, in one call: each coin''s . chart (Bitcoin''s ckh.) with its components, as daily values with their finality, from the SQL database. Plus and higher; a coin''s chart above the plan is listed under locked. One API call. At most 500,000 rows (truncated).'
      security:
      - bearerAuth: []
      parameters:
      - name: metric
        in: query
        required: true
        description: A chart name without its coin prefix, or any coin's full chart id.
        schema:
          type: string
          example: PriceUSD
      - name: assets
        in: query
        description: Comma-separated coins (all when absent).
        schema:
          type: string
          example: BTC,ETH
      - name: since
        in: query
        description: First UTC day, YYYY-MM-DD or Unix seconds.
        schema:
          type: string
      - name: until
        in: query
        description: Last UTC day, YYYY-MM-DD or Unix seconds.
        schema:
          type: string
      - name: format
        in: query
        schema:
          type: string
          enum:
          - json
          - csv
          default: json
      responses:
        '200':
          description: metric, database (builtAt, kind, fullAt), columns (t, value, finality), series (asset, metric, chart, component, name, unit, interval, plan, url, points), locked, rows, truncated, licence; or CSV (asset, metric, chart, component, day, t, value, finality)
          content:
            application/json:
              schema:
                type: object
            text/csv:
              schema:
                type: string
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '503':
          description: The database is being built
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /bulk/files:
    get:
      tags:
      - Charts
      operationId: listBulkFiles
      summary: The nightly files
      description: 'The latest nightly files the plan includes: one gzip CSV per coin and the series list (Pro and API & Team) and the whole SQLite database (API & Team), each with bytes, rows, SHA-256 and a download link that works for an hour without a key. One API call.'
      security:
      - bearerAuth: []
      responses:
        '200':
          description: builtAt, database, linksExpire, files (name, kind, asset, plan, bytes, rows, sha256, url), locked
          content:
            application/json:
              schema:
                type: object
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '503':
          description: The files are being built for the first time
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /bulk/file/{name}:
    get:
      tags:
      - Charts
      operationId: downloadBulkFile
      summary: One nightly file (a signed link)
      description: A file from GET /bulk/files, by the link it signed (e and s); no key needed. Byte ranges are supported for resumed downloads.
      security:
      - {}
      parameters:
      - name: name
        in: path
        required: true
        schema:
          type: string
      - name: e
        in: query
        required: true
        description: The link's expiry (Unix seconds)
        schema:
          type: integer
      - name: s
        in: query
        required: true
        description: The link's signature
        schema:
          type: string
      responses:
        '200':
          description: The file
          content:
            application/gzip:
              schema:
                type: string
                format: binary
        '206':
          description: The requested byte range
        '403':
          description: The link is not valid or has expired
        '404':
          description: The file was replaced by a newer set
        '429':
          $ref: '#/components/responses/TooManyRequests'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /vintages:
    get:
      tags:
      - Charts
      operationId: getVintages
      summary: First-published values
      description: Each period's first published value, the reading a backtest would have seen at the time, or with asOf each period's value as it stood then. Every point carries seen, when CryptoKeyhole's recorder first saw that value. History starts when the recorder started (recordedSince) and cannot be recreated for earlier times. API & Team. One API call.
      security:
      - bearerAuth: []
      parameters:
      - name: metric
        in: query
        required: true
        description: A recorded chart id; /vintages/charts lists them.
        schema:
          type: string
          example: eth.ExchangeNetflow
      - name: interval
        in: query
        schema:
          type: string
          enum:
          - 1h
          - 24h
          default: 24h
      - name: start
        in: query
        description: 'First period: YYYY-MM-DD, ISO time or Unix seconds.'
        schema:
          type: string
      - name: end
        in: query
        description: 'End of the window, exclusive: YYYY-MM-DD, ISO time or Unix seconds.'
        schema:
          type: string
      - name: asOf
        in: query
        description: Read each period as it stood at this time; without it, each period's first published value.
        schema:
          type: string
      - name: format
        in: query
        schema:
          type: string
          enum:
          - json
          - csv
          default: json
      responses:
        '200':
          description: metric, interval, mode (first or asOf), asOf, recordedSince, truncated, points (t, v, finality, seen), about, docs; or CSV
          content:
            application/json:
              schema:
                type: object
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '404':
          description: No vintages are recorded for this chart and interval
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /vintages/charts:
    get:
      tags:
      - Charts
      operationId: listVintageCharts
      summary: The charts with first-published values
      description: The recorded charts and intervals, and since when each has been recorded. No key needed.
      security:
      - {}
      responses:
        '200':
          description: charts (metric, interval, recordedSince, vintages), listed, firstRun, lastRun, docs
          content:
            application/json:
              schema:
                type: object
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /data-webhooks:
    get:
      tags:
      - Charts
      operationId: listDataWebhooks
      summary: Data webhooks and destinations
      description: The account's data webhooks (a chart's new values, or a screen's changes, pushed to one of its webhooks) and its webhook destinations. Pro and API & Team.
      security:
      - bearerAuth: []
      responses:
        '200':
          description: items, destinations, intervals
          content:
            application/json:
              schema:
                type: object
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
    post:
      tags:
      - Charts
      operationId: addDataWebhook
      summary: Add a data webhook
      description: 'A chart ({destinationId, metric, interval}) or a screen ({destinationId, screen: {where}, interval}; the screener''s conditions). Each new value is sent once as provisional, then final (event data.value); a screen''s change in matching coins is one event (data.screen). It starts from the latest value. Checks run every five minutes. Pro holds 50, API & Team 200.'
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - destinationId
              - interval
              properties:
                destinationId:
                  type: string
                metric:
                  type: string
                screen:
                  type: object
                  properties:
                    where:
                      type: string
                interval:
                  type: string
                  enum:
                  - 10m
                  - 1h
                  - 24h
      responses:
        '201':
          description: The new item
          content:
            application/json:
              schema:
                type: object
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '402':
          $ref: '#/components/responses/PlanRequired'
        '404':
          $ref: '#/components/responses/NotFound'
    servers:
    - url: https://cryptokeyhole.com/api/v1
  /data-webhooks/delete:
    post:
      tags:
      - Charts
      operationId: deleteDataWebhook
      summary: Delete a data webhook
      security:
      - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required:
              - id
              properties:
                id:
                  type: string
      responses:
        '200':
          description: Deleted
        '401':
          $ref: '#/components/responses/Unauthorized'
        '404':
          $ref: '#/components/responses/NotFound'
    servers:
    - url: https://cryptokeyhole.com/api/v1
components:
  responses:
    TooManyRequests:
      description: Request limit reached; try again in a minute
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    BadRequest:
      description: The request's parameters are invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    PlanRequired:
      description: Included with a higher plan; locked names it
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    NotFound:
      description: Unknown chart, asset, id or API route
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
    Unauthorized:
      description: The API key is invalid or revoked, or was sent to a route keys do not read
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
  parameters:
    publication:
      name: publication
      in: query
      description: A retained publication id, for the charts that keep them; omit for current data.
      schema:
        type: string
    end:
      name: end
      in: query
      description: UTC Unix seconds, not included. Without it the data runs to the latest value.
      schema:
        type: integer
    interval:
      name: interval
      in: query
      description: 'Resolution, as the chart offers it: Free 24h, 1w, 1M and 1Y; Plus also 1h; Pro also 10m.'
      schema:
        type: string
        enum:
        - 10m
        - 1h
        - 24h
        - 1w
        - 1M
        - 1Y
        default: 24h
    start:
      name: start
      in: query
      description: UTC Unix seconds, included. Without it the chart's default window applies.
      schema:
        type: integer
    smoothing:
      name: smoothing
      in: query
      description: Smoothing method, as on the charts.
      schema:
        type: string
        enum:
        - sma
        - ema
        - median
        default: sma
    metric:
      name: metric
      in: query
      required: true
      description: The chart id, as in its address (/charts/eth/?metric=eth.PriceUSD) or the catalogue.
      schema:
        type: string
        example: eth.PriceUSD
    sma:
      name: sma
      in: query
      description: Smoothing window in observations (0 for none).
      schema:
        type: integer
        minimum: 0
        default: 0
  schemas:
    Point:
      type: object
      required:
      - t
      - v
      - finality
      properties:
        t:
          type: integer
          description: The period's start, UTC seconds.
        v:
          type:
          - string
          - 'null'
          description: Exact decimal; null where there is no observation.
        finality:
          type: string
          enum:
          - final
          - provisional
          - partial
    Locked:
      type: object
      description: What a higher plan would include.
      properties:
        feature:
          type: string
          example: interval
        requiredPlan:
          type: string
          enum:
          - plus
          - pro
          - team
        requiredPlanName:
          type: string
          example: Plus
        currentPlan:
          type: string
          example: free
        currentPlanName:
          type: string
          example: Free
        upgradePath:
          type: string
          example: /pricing/
        allowedIntervals:
          type: array
          items:
            type: string
    Assets:
      type: object
      properties:
        assets:
          type: array
          items:
            type: object
            properties:
              code:
                type: string
                example: ETH
              name:
                type: string
                example: Ethereum
              route:
                type: string
                example: eth
              prefix:
                type: string
                example: eth.
                description: The asset's chart ids start with it (not for BTC).
    Error:
      type: object
      required:
      - error
      properties:
        error:
          type: string
        locked:
          $ref: '#/components/schemas/Locked'
    Catalog:
      type: object
      properties:
        asset:
          type: string
        metrics:
          type: array
          items:
            $ref: '#/components/schemas/Metric'
    Series:
      type: object
      properties:
        metric:
          $ref: '#/components/schemas/Metric'
        interval:
          type: string
        state:
          type: string
          description: current; delayed (the source is late, with its delay); held (not published); locked (above the plan, see locked).
        reason:
          type:
          - string
          - 'null'
        points:
          type: array
          items:
            $ref: '#/components/schemas/Point'
        components:
          type: array
          description: The lines of a chart with several, each with its points.
          items:
            type: object
            properties:
              id:
                type: string
              name:
                type: string
              unit:
                type: string
              points:
                type: array
                items:
                  $ref: '#/components/schemas/Point'
        historyStart:
          type:
          - integer
          - 'null'
        historyEnd:
          type:
          - integer
          - 'null'
        generatedAt:
          type: integer
        sourceObservedAt:
          type:
          - integer
          - 'null'
        locked:
          $ref: '#/components/schemas/Locked'
    Metric:
      type: object
      properties:
        id:
          type: string
          example: eth.PriceUSD
        name:
          type: string
        family:
          type: string
        unit:
          type: string
          example: USD/ETH
        description:
          type: string
        method:
          type: string
        intervals:
          type: array
          items:
            type: string
        status:
          type: string
          description: 'held: planned but not published (see reason).'
        reason:
          type:
          - string
          - 'null'
        historyStart:
          type:
          - integer
          - 'null'
          description: UTC seconds, the first period.
        historyEnd:
          type:
          - integer
          - 'null'
          description: UTC seconds, the end of the last period (not included).
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: 'An API key from your account page (Plus and higher plans): Authorization: Bearer <key>. Keys are read-only.'
externalDocs:
  description: API keys, plans, Google Sheets, Excel and Python
  url: https://cryptokeyhole.com/data-access/
x-refined-from:
- cryptokeyhole-openapi.json
- cryptokeyhole-openapi.json
x-provenance:
  method: harvested
  publisher: CryptoKeyhole
  source: https://cryptokeyhole.com/api/v1/openapi.json
  note: split by refine-openapis from the provider's own published document, verified by fetch (openapi/_original/*.fetch.json);
    stamped by stamp-openapi-provenance.py (roadmap#793)