← Back to Skills Marketplace
hvasconcelos

Luzia Crypto API

by hvasconcelos · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ⚠ suspicious
87
Downloads
0
Stars
0
Active Installs
1
Versions
Install in OpenClaw
/install luzia-crypto-api
Description
Use this skill whenever the user wants to fetch cryptocurrency prices, stream real-time market data, list exchanges or markets, or retrieve historical OHLCV...
README (SKILL.md)

Luzia API Integration Skill

Overview

Luzia (luzia.dev) provides real-time cryptocurrency pricing from multiple exchanges through a unified REST and WebSocket API.

  • Base REST URL: https://api.luzia.dev
  • WebSocket URL: wss://api.luzia.dev/v1/ws
  • Swagger UI: https://api.luzia.dev/docs
  • Docs: https://luzia.dev/docs

Core Principles

  1. Always authenticate — every endpoint requires Authorization: Bearer lz_\x3Ckey>.
  2. Choose REST vs WebSocket correctly — REST for on-demand lookups; WebSocket for streaming updates (Pro plan required).
  3. Respect rate limits — Free: 100 req/min, 5 000 req/day. Pro: 1 000 req/min, unlimited/day.
  4. Use the right symbol format — REST paths use BTC-USDT (hyphen); channel subscriptions and response payloads use BTC/USDT (slash).
  5. Handle errors explicitly — inspect HTTP status codes for REST; handle error message type for WebSocket.

Authentication

All requests require an API key in the Authorization header:

Authorization: Bearer lz_your_api_key

API keys follow the format lz_ + 32 random characters. Get one at: https://luzia.dev/signup


Tier Summary

Feature Free Pro ($29.99/mo) Enterprise
REST requests/min 100 1 000 Custom
REST requests/day 5 000 Unlimited Unlimited
WebSocket access ✅ (5 conns, 50 sub) ✅ (25, 500 sub)
History lookback 30 days 90 days Unlimited

REST API Reference

1. List Exchanges

GET /v1/exchanges

Returns all supported exchanges with their status.

Example response:

{
  "exchanges": [
    { "id": "binance", "name": "Binance", "status": "active" },
    { "id": "coinbase", "name": "Coinbase", "status": "active" }
  ]
}

2. List Markets for an Exchange

GET /v1/markets/:exchange

Returns all trading pairs available on the given exchange.

Path params: exchange — e.g. binance, coinbase, kraken

Example response:

{
  "exchange": "binance",
  "markets": [
    { "symbol": "BTC-USDT", "base": "BTC", "quote": "USDT" }
  ]
}

3. Get Ticker (single pair)

GET /v1/ticker/:exchange/:symbol

Fetches the latest price for one trading pair.

Path params:

  • exchange — e.g. binance
  • symbol — hyphen-separated, e.g. BTC-USDT

Query params:

  • maxAge (optional, ms) — max acceptable data age. Default: 120000 (2 min).

Example response:

{
  "symbol": "BTC/USDT",
  "exchange": "binance",
  "last": 67432.50,
  "bid": 67430.00,
  "ask": 67435.00,
  "high": 68500.00,
  "low": 66800.00,
  "open": 67000.00,
  "close": 67432.50,
  "volume": 12345.678,
  "quoteVolume": 832456789.50,
  "change": 432.50,
  "changePercent": 0.65,
  "timestamp": "2024-01-20T12:00:00.000Z"
}

4. Get All Tickers for an Exchange

GET /v1/tickers/:exchange

Fetches prices for all pairs on an exchange. Supports pagination.

Query params:

  • maxAge (optional, ms) — Default: 120000
  • limit (optional) — Default: 20, max: 50
  • offset (optional) — Default: 0

Example response:

{
  "tickers": [ { /* same shape as single ticker */ } ],
  "total": 150,
  "limit": 20,
  "offset": 0
}

5. Historical OHLCV Data

GET /v1/history/:exchange/:symbol

Returns candlestick data for a trading pair.

Path params: exchange, symbol (hyphen format, e.g. BTC-USDT)

Query params:

  • interval1m | 5m | 15m | 1h | 1d
  • limit — default 300, max 500
  • start — Unix ms timestamp (default: 24h ago)
  • end — Unix ms timestamp (default: now)

Example response:

{
  "exchange": "binance",
  "symbol": "BTC-USDT",
  "interval": "1h",
  "candles": [
    {
      "open": 67000.00,
      "high": 67450.00,
      "low": 66950.00,
      "close": 67432.50,
      "volume": 1234.56,
      "timestamp": "2024-01-20T12:00:00.000Z"
    }
  ],
  "count": 24,
  "start": "2024-01-20T00:00:00.000Z",
  "end": "2024-01-21T00:00:00.000Z"
}

REST: Code Patterns

TypeScript / fetch

const API_KEY = "lz_your_api_key";
const BASE = "https://api.luzia.dev";

async function getTicker(exchange: string, symbol: string) {
  const res = await fetch(`${BASE}/v1/ticker/${exchange}/${symbol}`, {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  return res.json();
}

async function getHistory(exchange: string, symbol: string, interval = "1h", limit = 24) {
  const url = new URL(`${BASE}/v1/history/${exchange}/${symbol}`);
  url.searchParams.set("interval", interval);
  url.searchParams.set("limit", String(limit));
  const res = await fetch(url.toString(), {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });
  if (!res.ok) throw new Error(`HTTP ${res.status}: ${await res.text()}`);
  return res.json();
}

Python / httpx

import httpx

API_KEY = "lz_your_api_key"
BASE = "https://api.luzia.dev"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}

def get_ticker(exchange: str, symbol: str) -> dict:
    r = httpx.get(f"{BASE}/v1/ticker/{exchange}/{symbol}", headers=HEADERS)
    r.raise_for_status()
    return r.json()

def get_history(exchange: str, symbol: str, interval: str = "1h", limit: int = 24) -> dict:
    r = httpx.get(
        f"{BASE}/v1/history/{exchange}/{symbol}",
        headers=HEADERS,
        params={"interval": interval, "limit": limit}
    )
    r.raise_for_status()
    return r.json()

WebSocket API Reference

Requires Pro plan or above.

Connection

wss://api.luzia.dev/v1/ws
Header: Authorization: Bearer lz_your_api_key

Channel Format

Channel Description
ticker:binance:BTC/USDT Single pair from one exchange
ticker:coinbase:ETH/USDT Single pair from another exchange
ticker:binance All tickers from Binance (1 sub slot)

⚠️ Symbol format in channels is slash (BTC/USDT), not hyphen.

Client → Server Messages

// Subscribe
{ "type": "subscribe", "channels": ["ticker:binance:BTC/USDT"] }

// Unsubscribe
{ "type": "unsubscribe", "channels": ["ticker:binance:BTC/USDT"] }

// Heartbeat (send every 30s)
{ "type": "ping" }

Server → Client Messages

// After connect
{ "type": "connected", "tier": "pro", "limits": { "maxSubscriptions": 50 } }

// Subscription confirmed
{ "type": "subscribed", "channel": "ticker:binance:BTC/USDT" }

// Heartbeat response
{ "type": "pong", "timestamp": "2024-01-23T10:13:20.000Z" }

// Price update
{
  "type": "ticker",
  "exchange": "binance",
  "symbol": "BTC/USDT",
  "data": { /* same fields as REST ticker response */ },
  "timestamp": "2024-01-23T10:13:20.050Z"
}

// Error
{ "type": "error", "code": "SUBSCRIPTION_LIMIT", "message": "..." }

WebSocket Error Codes

Code Meaning
CONNECTION_REJECTED Wrong tier or connection limit exceeded
SUBSCRIPTION_LIMIT Max subscriptions reached for your tier
INVALID_CHANNEL Bad channel format
INVALID_JSON Message is not valid JSON
INVALID_REQUEST Missing required fields
UNKNOWN_TYPE Unrecognized message type
SERVER_SHUTDOWN Server shutting down — reconnect shortly

WebSocket: Code Patterns

Node.js / Native WebSocket (no SDK)

import WebSocket from "ws"; // npm install ws

const API_KEY = "lz_your_api_key";

function createLuziaStream(channels: string[]) {
  const ws = new WebSocket("wss://api.luzia.dev/v1/ws", {
    headers: { Authorization: `Bearer ${API_KEY}` },
  });

  // Heartbeat every 30s
  const heartbeat = setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
      ws.send(JSON.stringify({ type: "ping" }));
    }
  }, 30_000);

  ws.on("open", () => console.log("WebSocket open"));

  ws.on("message", (raw: string) => {
    const msg = JSON.parse(raw);
    switch (msg.type) {
      case "connected":
        console.log("Connected — tier:", msg.tier);
        ws.send(JSON.stringify({ type: "subscribe", channels }));
        break;
      case "subscribed":
        console.log("Subscribed to:", msg.channel);
        break;
      case "ticker":
        console.log(`[${msg.exchange}] ${msg.symbol}: $${msg.data.last}`);
        break;
      case "pong":
        // heartbeat acknowledged
        break;
      case "error":
        console.error(`WS error [${msg.code}]:`, msg.message);
        break;
    }
  });

  ws.on("close", (code, reason) => {
    clearInterval(heartbeat);
    console.log(`Disconnected: ${code} ${reason}`);
    // implement exponential backoff reconnect here
  });

  ws.on("error", (err) => console.error("WS error:", err));

  return ws;
}

// Usage
createLuziaStream(["ticker:binance:BTC/USDT", "ticker:coinbase:ETH/USDT"]);

Python / websockets

import asyncio, json, websockets

API_KEY = "lz_your_api_key"
WS_URL = "wss://api.luzia.dev/v1/ws"

async def stream_prices(channels: list[str]):
    headers = {"Authorization": f"Bearer {API_KEY}"}
    async with websockets.connect(WS_URL, additional_headers=headers) as ws:
        async def heartbeat():
            while True:
                await asyncio.sleep(30)
                await ws.send(json.dumps({"type": "ping"}))

        asyncio.create_task(heartbeat())

        async for raw in ws:
            msg = json.loads(raw)
            match msg["type"]:
                case "connected":
                    print(f"Connected — tier: {msg['tier']}")
                    await ws.send(json.dumps({"type": "subscribe", "channels": channels}))
                case "ticker":
                    d = msg["data"]
                    print(f"[{msg['exchange']}] {msg['symbol']}: ${d['last']}")
                case "error":
                    print(f"Error [{msg['code']}]: {msg['message']}")

asyncio.run(stream_prices(["ticker:binance:BTC/USDT"]))

Reconnection with Exponential Backoff (TypeScript)

async function connectWithRetry(channels: string[], maxAttempts = 10) {
  let attempt = 0;
  const delay = (ms: number) => new Promise(r => setTimeout(r, ms));

  while (attempt \x3C maxAttempts) {
    try {
      await createLuziaStream(channels); // resolves on close
    } catch (err) {
      attempt++;
      const backoff = Math.min(1000 * 2 ** attempt + Math.random() * 500, 30_000);
      console.log(`Reconnecting in ${Math.round(backoff)}ms (attempt ${attempt})`);
      await delay(backoff);
    }
  }
  throw new Error("Max reconnect attempts reached");
}

SDK Quick Reference (Official)

Both SDKs wrap REST and WebSocket with TypeScript types and auto-reconnect:

TypeScript SDK

npm install @luziadev/sdk
import { Luzia } from "@luziadev/sdk";
const luzia = new Luzia({ apiKey: "lz_your_api_key" });

// REST
const ticker = await luzia.getTicker("binance", "BTC-USDT");

// WebSocket
const ws = luzia.createWebSocket({ autoReconnect: true });
ws.on("connected", () => ws.subscribe(["ticker:binance:BTC/USDT"]));
ws.on("ticker", (data) => console.log(data));
ws.connect();

Python SDK

Docs: https://luzia.dev/docs/python-sdk


Decision Tree: REST vs WebSocket

Need data?
│
├─ One-off / on-demand lookup?
│   └─ Use REST → GET /v1/ticker/:exchange/:symbol
│
├─ All pairs on an exchange?
│   └─ Use REST → GET /v1/tickers/:exchange  (paginate with limit/offset)
│
├─ Historical chart / backtesting?
│   └─ Use REST → GET /v1/history/:exchange/:symbol  (choose interval)
│
└─ Continuous stream / sub-second updates?
    ├─ Free tier? → Poll REST every N seconds (respect rate limits)
    └─ Pro tier?  → WebSocket → subscribe to specific channels

Common Mistakes to Avoid

Mistake Fix
Using BTC/USDT in REST URL path Use BTC-USDT (hyphen) in path params
Using BTC-USDT in WS channel name Use BTC/USDT (slash) in channel strings
Sending WS messages before connected Wait for { type: "connected" } before subscribing
No heartbeat on native WS Send { type: "ping" } every 30 seconds
Subscribing to exchange-level channel unnecessarily Use symbol-level channels to save sub slots
Forgetting maxAge for latency-sensitive apps Set maxAge=5000 (5s) for fresher REST data
Not handling SERVER_SHUTDOWN error Reconnect with backoff when you receive this code

Useful Links

Resource URL
Docs home https://luzia.dev/docs
Swagger UI https://api.luzia.dev/docs
WebSocket docs https://luzia.dev/docs/websocket
TypeScript SDK https://luzia.dev/docs/sdk
Python SDK https://luzia.dev/docs/python-sdk
MCP Server https://luzia.dev/docs/mcp-server
Get API key https://luzia.dev/signup
Manage API keys https://luzia.dev/keys
Usage Guidance
This skill looks like a normal API integration for Luzia, but the SKILL.md requires an API key (Authorization: Bearer lz_<key>) even though the manifest lists no required credentials. Before installing or invoking it, be prepared to: (1) supply your Luzia API key only if you trust the service and understand how the agent will use/store it, (2) confirm you are comfortable the agent will open network connections to api.luzia.dev and its WebSocket endpoint, and (3) ask the skill author or registry to declare the API key as a required credential (or document how the agent will securely obtain and handle the key). If you don't want the agent to handle secrets interactively, do not provide your API key and instead query public data sources that don't require authentication.
Capability Analysis
Type: OpenClaw Skill Name: luzia-crypto-api Version: 1.0.0 The skill bundle provides documentation and code examples for integrating with the Luzia cryptocurrency API (luzia.dev). The code snippets in SKILL.md are standard REST and WebSocket implementations for fetching market data, and there is no evidence of malicious intent, data exfiltration, or prompt injection attacks.
Capability Tags
crypto
Capability Assessment
Purpose & Capability
The name and description match the SKILL.md (fetch prices, market lists, OHLCV, WebSocket streaming). However, SKILL.md repeatedly states authentication is required (Authorization: Bearer lz_<key>) while the skill manifest lists no required environment variables or primary credential. This inconsistency is unexpected: a client that calls authenticated endpoints would normally declare a primary env var or require the API key.
Instruction Scope
The instructions are scoped to calling Luzia REST endpoints and the Luzia WebSocket URL and include example request/response shapes and client snippets. They do not instruct the agent to read unrelated files, system credentials, or exfiltrate data to third-party endpoints beyond api.luzia.dev/wss. The only notable runtime action is opening network connections to the Luzia API.
Install Mechanism
This is an instruction-only skill with no install spec and no code files, so nothing is written to disk and no external packages are pulled in. Low install risk.
Credentials
The SKILL.md requires an API key (lz_... format) for all endpoints, but the manifest declares zero required env vars or primary credential. This mismatch could lead the agent to prompt the user for credentials ad hoc or to attempt to operate without authentication. The documented need for WebSocket (streaming) access is also not reflected in any declared network or permission expectations.
Persistence & Privilege
The skill does not request persistent presence (always is false), does not modify other skills or system settings, and declares no config paths. Normal privilege level for an API integration.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install luzia-crypto-api
  3. After installation, invoke the skill by name or use /luzia-crypto-api
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
Luzia Crypto API skill v1.0.0 - Initial release providing integration with the Luzia API for cryptocurrency pricing and market data. - Supports fetching real-time prices, listing exchanges/markets, retrieving historical OHLCV data, and streaming with WebSocket (Pro plan required). - Includes detailed usage guidelines for REST and WebSocket, authentication, endpoint references, and example code in TypeScript and Python. - Skill is user-invocable and responds to requests for crypto price data or Luzia-specific functionality.
Metadata
Slug luzia-crypto-api
Version 1.0.0
License MIT-0
All-time Installs 0
Active Installs 0
Total Versions 1
Frequently Asked Questions

What is Luzia Crypto API?

Use this skill whenever the user wants to fetch cryptocurrency prices, stream real-time market data, list exchanges or markets, or retrieve historical OHLCV... It is an AI Agent Skill for Claude Code / OpenClaw, with 87 downloads so far.

How do I install Luzia Crypto API?

Run "/install luzia-crypto-api" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Luzia Crypto API free?

Yes, Luzia Crypto API is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Luzia Crypto API support?

Luzia Crypto API is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Luzia Crypto API?

It is built and maintained by hvasconcelos (@hvasconcelos); the current version is v1.0.0.

💬 Comments