Skip to main content

blockmetric (Python)

Official Python client of the Blockmetric API for real-time crypto market data: liquidation maps and heatmaps from real Hyperliquid on-chain positions, whales, order flow, plus funding, open interest and liquidation totals from the exchanges whose data the API may redistribute. Standard library only, Python 3.9+, fully typed (TypedDicts generated from the OpenAPI document).

Not on PyPI yet. Until it is published, install it from this repository: pip install -e sdk/python (or put sdk/python on PYTHONPATH). Once published: pip install blockmetric.

60-second quickstart

  1. Create a key on /account/api (every account has a personal developer key; the full key is shown once).

  2. Set it (and, until the SDK ships with the production URL baked in, the base URL):

    export BLOCKMETRIC_API_KEY=bm_live_...
    export BLOCKMETRIC_BASE_URL=https://<domain>/api/v1
    
  3. First request:

    from blockmetric import Blockmetric
    
    client = Blockmetric()  # reads BLOCKMETRIC_API_KEY and BLOCKMETRIC_BASE_URL
    res = client.liquidation_summary(hours=24)
    s = res["data"]
    print(f"24h liquidations: ${s['totalUsd']:,.0f} (longs ${s['longUsd']:,.0f}, shorts ${s['shortUsd']:,.0f})")
    print(res.last_response.rate_limit.remaining, "requests left this minute")
    

Every method returns the API envelope {"data": ..., "meta": ...} as a plain dict (JSON-serialisable), typed as the matching TypedDict (blockmetric.types.LiquidationSummaryResponse, …). The HTTP details are attached as res.last_response: status, request_id, rate_limit (limit, remaining, reset per minute; limit_day, remaining_day, reset_day and overage when the server sends them), etag, not_modified, deprecation, sunset, api_version, key_expires_at, headers. client.last_response holds those of the most recent request.

Endpoints

Method API
markets(venue=, fields=, format=) GET /markets Market state of a venue
funding(coin=, limit=, fields=, format=) GET /funding Cross-venue funding board
coins(fields=, format=) GET /coins Hyperliquid coins
long_short(coin=, limit=, fields=, format=) GET /long-short Long/short positioning
liquidations(coin=, venue=, side=, min_usd=, since_minutes=, from_=, to=, after=, limit=, fields=, format=) GET /liquidations (paginated) Liquidations feed
liquidation_summary(coin=, hours=) GET /liquidations/summary Liquidation totals
liqmap(coin) GET /liqmap/{coin} Liquidation map (current)
candles(coin, tf=, from_=, to=, limit=, fields=, format=) GET /candles/{coin} Price candles (OHLC)
heatmap(coin, tf=, hours=, end=, columns=, clusters=, venue=, step_minutes=, layer=) GET /heatmap/{coin} Liquidation heatmap over time
heatmap_open_interest(coin, hours=, end=, venue=, band_pct=) GET /heatmap/{coin}/open-interest Open interest added per price band
heatmap_wallets(coin, lo=, hi=, price=, clusters=, side=, min_usd=, limit=) GET /heatmap/{coin}/wallets Wallets with their liquidation price in a band
vaults(sort=, min_tvl=, closed=, q=, limit=) GET /vaults Vault leaderboard
hyperliquid_protocol(days=, recent=) GET /hyperliquid/protocol Hyperliquid protocol state
correlation(coins=, limit=) GET /correlation Correlation and beta to BTC
scoreboard(coin=, limit=) GET /scoreboard Cluster hit rates (scoreboard)
top_positions(coin=, side=, min_usd=, limit=, venue=, fields=, format=) GET /positions/top (paginated) Largest positions
whales(sort=, coin=, labelled=, limit=, venue=, fields=, format=) GET /whales Whale leaderboard
whale_feed(coin=, min_usd=, since=, limit=, fields=, format=) GET /whales/feed (paginated) Whale position changes
wallet(address, limit=) GET /wallets/{address} Wallet detail
whale_stops(coin=) GET /whales/stops Whale stop / TP map
traders(sort=, search=, style=, min_account_value=, limit=, fields=, format=) GET /traders (paginated) Trader leaderboard
cohorts(coin=, dim=, cohort=, limit=) GET /cohorts Cohort positioning
smart_money(coin=, limit=) GET /smart-money Smart-money convergence
orderbook(coin, venue=, hours=) GET /orderbook/{coin} Order book liquidity and walls
flow(coin, venue=, hours=, step_minutes=) GET /flow/{coin} Taker flow (CVD) and volume profile
tape(coin=, venue=, side=, min_usd=, since_minutes=, from_=, to=, after=, limit=, fields=, format=) GET /tape (paginated) Large-trade tape
screener(coin=, sort=, dir=, min_oi_usd=, limit=, fields=, format=) GET /screener Screener (every coin)
predicted_funding(coin=, limit=, fields=, format=) GET /funding/predicted Predicted next funding
funding_spreads(coin=, basis=, min_oi_usd=, limit=, fields=, format=) GET /funding/spreads Cross-venue funding spreads
sentiment(days=) GET /sentiment Blockmetric Sentiment index
radar_events(kinds=, min_severity=, coin=, since=, limit=, fields=, format=) GET /radar/events (paginated) Risk radar events
security_incidents(origin=, type=, chain=, min_severity=, limit=, fields=, format=) GET /security/incidents (paginated) Security incidents
security_wallets(category=, network=, source=, q=, moved_days=, limit=, fields=, format=) GET /security/wallets (paginated) Listed wallets (OFAC, FBI, post-mortems)
security_wallet(address, limit=) GET /security/wallets/{address} Listed wallet detail
movements(type=, chain=, asset=, min_usd=, labelled=, address=, since=, limit=, fields=, format=) GET /movements (paginated) Whale movements (on-chain + Hyperliquid)
exchange_flows(hours=, exchange=, asset=) GET /exchange-flows Exchange on-chain flows
stats() GET /stats Coverage and freshness
me() GET /me The calling key: plan, limits, usage
status() GET /status (public, no key) API status (public)
stream(channel=, kinds=, min_severity=, venue=, min_usd=, coin=, last_event_id=) GET /stream (API Pro, Server-Sent Events) Live event stream (SSE)

Parameters are keyword-only; snake_case (since_minutes) and the API's camelCase (sinceMinutes) both work, an unknown one raises TypeError before any request. Lists become comma-separated values (kinds=["hack", "depeg"]), booleans true / false. Units: times ISO 8601 UTC, money in USD, funding rates, distances and changes as fractions (0.01 = 1 %).

Liquidation heatmap

from blockmetric import Blockmetric, heatmap_clusters

hm = client.heatmap("BTC", tf="15m", hours=24)["data"]
c = heatmap_clusters(hm, top=3)
for x in c["above"]:
    print(f"shorts liquidate at {x['price']:,.0f} (+{x['distance']:.1%}): ${x['usd']:,.0f}")
for x in c["below"]:
    print(f"longs liquidate at {x['price']:,.0f} ({x['distance']:.1%}): ${x['usd']:,.0f}")

tf is the column interval (1m · 5m · 15m · 1h · 4h · 12h · 1d, as your plan allows), end= pages back in time.

Pagination

for liq in client.paginate("liquidations", since_minutes=24 * 60, coin="BTC", limit=1000):
    print(liq["ts"], liq["venue"], liq["side"], liq["usd"])

paginate follows meta.nextCursor until the last page (every operation marked "paginated" above); max_pages= stops earlier. pages() yields the full envelope of each page instead of the items.

New items as they arrive (any key)

import threading

stop = threading.Event()  # stop.set() from another thread ends the loop
for liq in client.follow("liquidations", coin="BTC", limit=200, interval=15, stop_event=stop):
    if (liq["usd"] or 0) >= 100_000:
        print("big liquidation", liq["side"], liq["usd"], liq["price"])

follow polls a feed (liquidations, whale_feed, radar_events, tape, movements), reads back until the newest item it has already seen and yields every new item once, oldest first. Retryable errors go to on_error and the loop continues. Mind your per-minute limit when choosing interval.

Live stream (API Pro)

for event in client.stream(kinds=["big_liquidation", "liq_cascade"], min_severity=2, max_duration=3600):
    print(event["type"], event["data"]["severity"], event["data"]["title"])

Each item is the webhook envelope {"id": "evt_…", "type", "created", "data"}. The client reconnects on its own (honouring the server's retry:, also after idle_timeout seconds without the 15 s heartbeat) and resumes with Last-Event-ID, so nothing is lost inside the server's 15-minute resume window. It stops on break, after max_duration seconds or when stop_event (a threading.Event) is set (also from another thread); the open connection is closed right away. on_ready, on_gap (events outside the window: backfill with radar_events()) and on_reconnect are optional callbacks. Without API Pro, follow("radar_events") gives the same events by polling.

Webhooks (API Dev, API Pro)

Verify the raw body exactly as received, never re-serialised JSON:

import os
from http.server import BaseHTTPRequestHandler, HTTPServer

from blockmetric import construct_event, BlockmetricError

SECRET = os.environ["BLOCKMETRIC_WEBHOOK_SECRET"]  # whsec_..., shown once when you create the endpoint


class Hook(BaseHTTPRequestHandler):
    def do_POST(self):
        raw = self.rfile.read(int(self.headers.get("Content-Length", 0)))
        try:
            event = construct_event(raw, self.headers.get("Blockmetric-Signature"), SECRET)
        except BlockmetricError:
            self.send_response(400)
            self.end_headers()
            return
        self.send_response(204)  # answer within 10 s, then work
        self.end_headers()
        print(event["id"], event["type"], event["data"]["title"])  # deduplicate on event["id"] (retries reuse it)


HTTPServer(("", 8080), Hook).serve_forever()

verify_webhook(body, header, secret, tolerance=300) returns a bool instead (constant-time comparison, any v1 matches: during a secret rotation the old secret keeps working for 24 h). sign_webhook(body, secret) produces a header for your own tests.

Errors and retries

Every failure raises blockmetric.BlockmetricError with code (see the error catalogue at doc_url), status, param (the parameter it is about, when the API names one), request_id (quote it to support), retry_after, retryable and message.

from blockmetric import Blockmetric, BlockmetricError

try:
    Blockmetric().heatmap("SOL", tf="15m")
except BlockmetricError as e:
    if e.code == "plan_required":
        print("needs a higher plan:", e.message)
    elif e.code == "source_restricted":
        print("not redistributed through the API:", e.message)
    else:
        raise

429, 5xx, timeouts and network errors are retried (max_retries=3) with exponential backoff and jitter, honouring Retry-After (seconds or an HTTP date) up to max_retry_delay=60 seconds; a longer wait (the daily limit) raises immediately. etag_cache=True keeps the last body per URL and revalidates with If-None-Match (a 304 still counts against the limit but transfers no body). user_agent="my-app/1.0" names your app in front of the SDK's User-Agent, on_retry and on_warning (every meta.warnings entry) are hooks for logging.

Plans and limits

Access Requests History Licence Realtime
No key (X-Blockmetric-Access: public) 10 / min, 300 / day per IP address 24 h public (non-commercial) -
Free 30 / min, 1,000 / day 30 days personal use -
Pro 40 / min, 5,000 / day 1 year personal use -
Desk 60 / min, 10,000 / day 3 years personal use -
API Dev 60 / min, 50,000 / day 1 year internal use (1 organisation) webhooks
API Pro 300 / min, 400,000 / day 3 years display in your product live stream, webhooks

Limits apply per account across all its keys; every plan is real time. Every key gets the heatmap of every coin: free keys the last 24 hours, paid plans their plan history. Every response names its licence (meta.license) and the credit to show (meta.attribution). Current prices: /pricing on the site. Data from public sources, without guarantee.

Versioning

The package follows semantic versioning (blockmetric.__version__) and is generated against one API contract (blockmetric.API_VERSION, sent in the User-Agent). Within /api/v1 the API only adds fields and parameters, so an older SDK keeps working; last_response.api_version shows the contract that answered. Changes: sdk/CHANGELOG.md.

Licence

The package is licensed under the Apache License 2.0 (see also NOTICE). The licence covers the code, not the name: "Blockmetric" and the logo are not licensed, so forks must be renamed; "works with Blockmetric" is fine (TRADEMARKS.md). Data you receive from the API is governed by the API terms (/terms#api-license on the site; meta.license in every response); market data of third-party exchanges stays subject to that exchange's terms (/terms#third-party-data). The examples in examples/ and the code snippets in the API docs are MIT-0 (copy them into your own code without attribution). No warranty for the data.

Development

python -m unittest discover -s sdk/python/tests -v   # local fake API, no network
npx tsx scripts/devx-generate.ts                     # regenerates blockmetric/types.py, _config.py and this README's tables

Publishing is a manual operator step: set SITE_URL to the production https URL, run npx tsx scripts/devx-generate.ts --release, remove the Private :: Do Not Upload classifier from pyproject.toml, check the copyright holder in NOTICE (exact registered company name), then build (setuptools ≥ 77.0.3, PEP 639 licence metadata) and upload. Examples: examples/.

Metadata

Release files for blockmetric 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for blockmetric 0.3.0
File Size Uploaded Provenance Actions
blockmetric-0.3.0.tar.gz 72.3 kB Without provenance Details for blockmetric-0.3.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for blockmetric 0.3.0
File Interpreter ABI Platform Actions
blockmetric-0.3.0-py3-none-any.whl Python 3 none any Details for blockmetric-0.3.0-py3-none-any.whl

Total release size: 129.9 kB

Release files / blockmetric-0.3.0.tar.gz

Download URL blockmetric-0.3.0.tar.gz
Size 72.3 kB
Tags Source
SHA-256 checksum
How to use checksums
ef401668d54358a5f61d6555c3a93e76d1ff67b24ba8cb8aa8415dab7b19c6a3
BLAKE2b-256 checksum
How to use checksums
a0ea6c16f93afe5f5420cb76ca38a4673c7e96ae750e2799c43b75cc907c3756
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / blockmetric-0.3.0-py3-none-any.whl

Download URL blockmetric-0.3.0-py3-none-any.whl
Size 57.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
cf34ec0630afdf1abdb7a7073e66da96d0dbc591d2d4dc42f9b1fdee4ca7e4e3
BLAKE2b-256 checksum
How to use checksums
9537c79af5e72dbd5da499691e643b6cd6f41b781f282599c90356c291fcdb60
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Security for blockmetric 0.3.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page