K-Line API User Guide
Version: 1.0.0
Audience: integrator developers, market-data clients, and operations engineers
This guide explains how to use the K-Line API to manage collection pools, query historical OHLCV data, subscribe to real-time K-line updates, and call market proxy and operational debugging APIs.
Table of contents
- 1. Quick start
- 2. Pool management APIs
- 3. Historical K-line query
- 4. Market proxy APIs
- 5. K-Line WebSocket
- 6. Generic WebSocket
- 7. Operational debugging APIs (appendix)
- 8. Health check (appendix)
- 9. Error handling and best practices
1. Quick start
1.1 Service overview
The K-Line API is a K-line data service. It provides these core capabilities:
- Manage on-chain liquidity pools that need OHLCV collection
- Query historical OHLCV candlestick data for a pool
- Dynamically subscribe to real-time K-line updates through WebSocket
- Proxy CoinGecko market data, top holders, and token metadata queries
- Inspect the ingester and KlineHub Durable Object runtime status
All HTTP APIs are mounted under the /api/v1 prefix. Public endpoints use the X-API-Key request header for API authentication and billing identification. Headers used internally by the forwarding layer are not required for external integration.
1.2 Base URL
Use the following production host as the root path for all API requests:
https://api.gelabs.org
Example:
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/health"
1.3 Unified response shape
Most successful HTTP APIs return:
{
"code": 0,
"message": "ok",
"data": {}
}
| Field | Type | Always returned | Description |
|---|---|---|---|
code | integer | Yes | Business status code. 0 means success. |
message | string | Yes | Response message. Successful responses use ok. |
data | object / array / string | Yes | Business payload. Shape depends on the endpoint. |
Failed responses usually look like:
{
"code": 10001,
"message": "Invalid query params",
"error": {
"type": "ValidationError",
"details": {
"fieldErrors": {
"chain_type": ["String must contain at least 1 character(s)"]
}
}
}
}
Note: Operational debugging endpoints do not always use the unified JSON wrapper.
/api/v1/admin/debug/ingester/ensurereturns plain textok, while status endpoints return raw Durable Object JSON.
1.4 Error codes
| Code | Meaning | Common trigger |
|---|---|---|
0 | Success | Normal response |
10001 | Validation failed | Invalid query, path, or body fields |
10002 | Invalid argument | Value violates business rules |
10003 | Resource not found | Unknown route or upstream resource |
10004 | Resource conflict | Creating a duplicate pool |
10005 | Bad request | WebSocket preflight or malformed request |
20001 | Unauthenticated | Reserved, currently not enabled |
20002 | Forbidden | Reserved, currently not enabled |
60001 | K-line resource not found | Querying a missing pool |
60002 | K-line sync failed | Reserved |
60003 | Invalid symbol | Reserved |
60004 | Invalid interval | Reserved |
99001 | Internal server error | Uncaught exception, upstream error, or rate limit |
99002 | Database error | Reserved |
99003 | Cache error | Reserved |
1.5 Common conventions
| Item | Description |
|---|---|
| Common request header | All public endpoints require X-API-Key for API authentication and billing identification. |
| Content-Type | POST bodies use application/json; WebSocket messages use JSON text frames. |
| Timestamps | HTTP query parameters from and to accept Unix seconds or milliseconds. Values greater than 10000000000 are treated as milliseconds. |
| Chain type | chain_type is normalized to lowercase. Common aliases include eth/ethereum → evm, sol/solana → svm, trx/tron → tvm, btc/bitcoin → utxo, atom → cosmos, and sui/aptos/apt → move. |
| Token side | token supports base and quote; default is base. |
| K-line intervals | Supported intervals are 1s, 1m, 15m, 1h, 4h, 1d, 1w, 1M, and 1Y. 1w, 1M, and 1Y are aggregated from 1d data. |
| Pagination | Request fields use page and page_size; response pagination uses page, pageSize, total, and totalPages. |
| Cache | HTTP query APIs do not currently promise a fixed Cache-Control policy. After a WebSocket subscription succeeds, the server may send the latest in-memory snapshot. |
Note:
1w,1M, and1Yare queried by aggregating persisted1dcandles. They do not imply direct upstream subscriptions at those granularities.
1.6 Integration flow
Recommended integration steps:
- Call
GET /api/v1/healthto verify service availability. - Call
POST /api/v1/admin/poolsto create the liquidity pool to collect. - Call
PATCH /api/v1/admin/pools/{id}/activateto activate the pool and notify the ingester. - Call
GET /api/v1/kline/ohlcvto query historical K-line data. - Connect to
GET /api/v1/kline/wsand subscribe to real-time updates.
2. Pool management APIs
Pools describe the on-chain liquidity pools whose K-line data should be collected. A newly created pool has is_active: false; activate it before expecting the ingester to subscribe to upstream real-time data.
This module is used to:
- Register pools for OHLCV collection
- Control whether the ingester subscribes to a pool
- Query pool metadata and activation status
2.1 Create a pool
Request
POST /api/v1/admin/pools
Request body
| Field | Type | Required | Description |
|---|---|---|---|
chain_type | string | Yes | Chain type, 1-50 characters, normalized by the server. |
chain_id | integer | Yes | Positive chain ID. |
network_id | string | Yes | CoinGecko network identifier, such as eth. |
pool_address | string | Yes | Pool contract address. |
token | string | No | base or quote; default is base. |
dex_id | string | No | DEX identifier. |
label | string | No | Human-readable label. |
curl example
curl -X POST "https://api.gelabs.org/api/v1/admin/pools" \
-H "X-API-Key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"chain_type": "evm",
"chain_id": 1,
"network_id": "eth",
"pool_address": "0xabc123def456",
"token": "base",
"dex_id": "uniswap_v3",
"label": "ETH/USDC"
}'
Successful response
{
"code": 0,
"message": "ok",
"data": {
"id": 42,
"chain_type": "evm",
"chain_id": 1,
"network_id": "eth",
"pool_address": "0xabc123def456",
"token": "base",
"dex_id": "uniswap_v3",
"label": "ETH/USDC",
"is_active": false,
"created_at": "2026-04-16T08:00:00.000Z",
"updated_at": "2026-04-16T08:00:00.000Z"
}
}
Pool object fields
| Field | Type | Always returned | Description |
|---|---|---|---|
id | integer | Yes | Internal pool ID. |
chain_type | string | Yes | Normalized chain type. |
chain_id | integer | Yes | Chain ID. |
network_id | string | Yes | CoinGecko network identifier. |
pool_address | string | Yes | Pool contract address. |
token | string | Yes | Collected token side, base or quote. |
dex_id | string / null | No | DEX identifier. |
label | string / null | No | Human-readable label. |
is_active | boolean | Yes | Whether collection is active. |
created_at | string | Yes | ISO 8601 creation time. |
updated_at | string | Yes | ISO 8601 update time. |
Common error response
Creating a duplicate pool usually returns HTTP 409:
{
"code": 10004,
"message": "Pool already exists",
"error": { "type": "ConflictError" }
}
2.2 List pools
Request
GET /api/v1/admin/pools
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
page | integer | No | 1 | Page number, starting from 1. |
page_size | integer | No | 20 | Page size, from 1 to 100. |
curl example
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/admin/pools?page=1&page_size=20"
Cache notes
Pool management APIs read database state directly and do not promise a fixed HTTP cache policy. Refresh local caches after creating, activating, or deactivating a pool.
2.3 Get pool details
Request
GET /api/v1/admin/pools/{id}
| Path parameter | Type | Required | Description |
|---|---|---|---|
id | integer | Yes | Internal pool ID. |
curl example
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/admin/pools/42"
The response data is a single pool object. If the pool does not exist, the API returns HTTP 404 and usually uses business code 60001.
Common error response
{
"code": 60001,
"message": "Pool not found",
"error": { "type": "NotFoundError" }
}
2.4 Activate a pool
Request
PATCH /api/v1/admin/pools/{id}/activate
After activation, the service makes a best-effort attempt to notify OhlcvIngester to subscribe to upstream real-time data. Notification failure does not roll back the database state.
curl -X PATCH "https://api.gelabs.org/api/v1/admin/pools/42/activate" \
-H "X-API-Key: $API_KEY"
Successful responses return the updated pool object with is_active: true.
2.5 Deactivate a pool
Request
PATCH /api/v1/admin/pools/{id}/deactivate
After deactivation, the service makes a best-effort attempt to notify OhlcvIngester to unsubscribe from upstream real-time data. Notification failure does not roll back the database state.
curl -X PATCH "https://api.gelabs.org/api/v1/admin/pools/42/deactivate" \
-H "X-API-Key: $API_KEY"
Successful responses return the updated pool object with is_active: false.
3. Historical K-line query
This module queries persisted OHLCV candlestick data. It is suitable for chart initialization, filling gaps after client disconnects, and loading historical data before subscribing to real-time updates.
3.1 Query OHLCV data
Request
GET /api/v1/kline/ohlcv
Query parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
interval | string | Yes | - | K-line interval: 1s, 1m, 15m, 1h, 4h, 1d, 1w, 1M, 1Y. |
from | integer | Yes | - | Query start time, Unix seconds or milliseconds. |
to | integer | Yes | - | Query end time, Unix seconds or milliseconds. |
limit | integer | No | 1000 | Maximum row count, from 1 to 5000. |
chain_type | string | Yes | - | Chain type, normalized automatically. |
chain_id | integer | Yes | - | Positive chain ID. |
pool_address | string | Yes | - | Pool contract address. |
token | string | No | base | base or quote. |
curl example
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/kline/ohlcv?interval=1h&from=1776679200&to=1776765600&limit=500&chain_type=evm&chain_id=1&pool_address=0xabc123def456&token=base"
Successful response
data is an OHLCV array and is not wrapped in items:
{
"code": 0,
"message": "ok",
"data": [
{
"pool_id": 42,
"interval": "1h",
"open_time": 1776679200,
"open": 3500.12,
"high": 3520,
"low": 3480.5,
"close": 3510.88,
"volume": 1234567.89
}
]
}
Response fields
| Field | Type | Always returned | Description |
|---|---|---|---|
pool_id | integer | Yes | Internal pool ID. |
interval | string | Yes | K-line interval. |
open_time | integer | Yes | Candle start time, Unix seconds. |
open | number / null | Yes | Open price. |
high | number / null | Yes | High price. |
low | number / null | Yes | Low price. |
close | number / null | Yes | Close price. |
volume | number / null | Yes | Volume. |
Cache notes
This endpoint does not declare a fixed HTTP cache header. For high-real-time scenarios, combine HTTP historical queries with WebSocket updates for the latest candle.
Common error response
{
"code": 10001,
"message": "Invalid query params",
"error": {
"type": "ValidationError",
"details": {
"fieldErrors": {
"interval": ["Required"],
"pool_address": ["Required"]
}
}
}
}
4. Market proxy APIs
Market APIs proxy CoinGecko APIs. The outer response still uses the K-Line unified success wrapper, while data contains the raw business structure returned by CoinGecko.
These APIs are useful for auxiliary display and debugging. They do not represent the internal K-Line collection state.
4.1 Get pool market details
GET /api/v1/market/pools/{network}/{address}
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
network | path | string | Yes | CoinGecko network identifier. |
address | path | string | Yes | Pool contract address. |
include | query | string | No | Extra included fields, comma-separated. |
include_volume_breakdown | query | string | No | "true" or "false". |
include_composition | query | string | No | "true" or "false". |
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/market/pools/eth/0xabc123def456?include=base_token,quote_token&include_volume_breakdown=true&include_composition=false"
4.2 Get token top holders
GET /api/v1/market/tokens/{network}/{address}/top-holders
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
network | path | string | Yes | CoinGecko network identifier. |
address | path | string | Yes | Token contract address. |
holders | query | string | No | Positive integer string or max. |
include_pnl_details | query | string | No | "true" or "false". |
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/market/tokens/eth/0xabc123def456/top-holders?holders=10&include_pnl_details=false"
4.3 Get token metadata by contract address
GET /api/v1/market/coins/{network}/{address}
| Parameter | Location | Type | Required | Description |
|---|---|---|---|---|
network | path | string | Yes | CoinGecko platform identifier. |
address | path | string | Yes | Token contract address. |
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/market/coins/ethereum/0xabc123def456"
Common proxy error response
{
"code": 99001,
"message": "Internal server error",
"error": { "type": "InternalServerError" }
}
5. K-Line WebSocket
K-Line real-time push uses a single WebSocket entry point. After connecting, clients dynamically subscribe or unsubscribe to multiple pools with JSON messages.
Keep HTTP historical query support on the client side. On first load or after reconnecting, query historical data first, then use WebSocket updates for the latest candle.
5.1 Connection requirements
Connection URL
GET /api/v1/kline/ws
Required headers
| Header | Required | Description |
|---|---|---|
Upgrade: websocket | Yes | Standard WebSocket upgrade header. |
Connection: Upgrade | Yes | Standard WebSocket upgrade header. |
X-API-Key | Yes | Public API key for API authentication and billing identification. |
Note:
X-API-Keyis the only required business request header for external integration. Forwarding-layer internal headers are not required from integrators.
5.2 Protocol constants
| Constant | String value | Direction | Description |
|---|---|---|---|
WS_TYPE_PING | ping | Client → server | Application heartbeat. |
WS_TYPE_PONG | pong | Server → client | Heartbeat response. |
WS_TYPE_SUBSCRIBE | subscribe | Client → server | Subscribe to a pool. |
WS_TYPE_UNSUBSCRIBE | unsubscribe | Client → server | Unsubscribe from a pool. |
WS_TYPE_SUBSCRIBED | subscribed | Server → client | Subscription acknowledgement. |
WS_TYPE_UNSUBSCRIBED | unsubscribed | Server → client | Unsubscription acknowledgement. |
WS_TYPE_OHLCV | ohlcv | Server → client | Real-time or snapshot K-line push. |
| WebSocket code | Description |
|---|---|
0 | Success |
4001 | Invalid message, such as invalid JSON or missing fields |
4002 | Unknown command |
4500 | Internal error, currently reserved |
5.3 Client message format
{
"id": "req-001",
"type": "subscribe",
"data": {}
}
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Request correlation ID. |
type | string | Yes | ping, subscribe, or unsubscribe. |
data | object | Depends on command | Command payload. |
subscribe.data
| Field | Type | Required | Description |
|---|---|---|---|
chain_type | string | Yes | Chain type, normalized automatically. |
chain_id | number | Yes | Chain ID. |
pool_address | string | Yes | Pool address; internal pool key is lowercased. |
token | string | No | base or quote; default is base. |
intervals | string[] | No | Valid intervals. Missing or empty array means all intervals. Invalid values are silently ignored. |
Note:
intervalsmust be a JSON array, not a comma-separated string.
unsubscribe.data
| Field | Type | Required | Description |
|---|---|---|---|
chain_type | string | Yes | Chain type, normalized automatically. |
chain_id | number | Yes | Chain ID. |
pool_address | string | Yes | Pool address. |
token | string | No | base or quote; default is base. |
5.4 Server message format
{
"id": "req-001",
"time": 1776682800000,
"type": "subscribed",
"code": 0,
"data": {},
"msg": "ok"
}
| Field | Type | Always returned | Description |
|---|---|---|---|
id | string | Yes | Request ID for command responses; active pushes use "". |
time | number | Yes | Server Unix millisecond timestamp. |
type | string | Yes | pong, subscribed, unsubscribed, ohlcv, or error. |
code | number | Yes | WebSocket business code. |
data | object / null | Yes | Payload. |
msg | string | Yes | Human-readable message; real-time pushes use ok, snapshots use snapshot. |
5.5 Subscribe and unsubscribe examples
Heartbeat
{ "id": "hb-001", "type": "ping", "data": {} }
Subscribe
{
"id": "sub-001",
"type": "subscribe",
"data": {
"chain_type": "evm",
"chain_id": 1,
"pool_address": "0xabc123def456",
"token": "base",
"intervals": ["1m", "1h", "4h"]
}
}
Successful response:
{
"id": "sub-001",
"time": 1776682800000,
"type": "subscribed",
"code": 0,
"data": {
"key": "evm:1:0xabc123def456:base",
"intervals": ["1m", "1h", "4h"]
},
"msg": "ok"
}
If the server already has a latest tick for the pool in memory, it may immediately push an additional ohlcv snapshot.
Unsubscribe
{
"id": "unsub-001",
"type": "unsubscribe",
"data": {
"chain_type": "evm",
"chain_id": 1,
"pool_address": "0xabc123def456",
"token": "base"
}
}
5.6 Push data fields
Real-time and snapshot pushes both use type: "ohlcv":
{
"id": "",
"time": 1776682800000,
"type": "ohlcv",
"code": 0,
"data": {
"chain_type": "evm",
"chain_id": 1,
"pool_address": "0xabc123def456",
"token": "base",
"interval": "1h",
"open_time": 1776679200,
"open": 3500.12,
"high": 3520,
"low": 3480.5,
"close": 3510.88,
"volume": 1234567.89
},
"msg": "ok"
}
| Field | Type | Always returned | Description |
|---|---|---|---|
chain_type | string / null | Yes | Chain type. |
chain_id | number / null | Yes | Chain ID. |
pool_address | string | Yes | Pool address. |
token | string | Yes | Token side. |
interval | string | Yes | K-line interval. |
open_time | number | Yes | Candle start time, Unix seconds. |
open | number / null | Yes | Open price. |
high | number / null | Yes | High price. |
low | number / null | Yes | Low price. |
close | number / null | Yes | Close price. |
volume | number / null | Yes | Volume. |
KlineHub silently ignores binary frames. Clients receive no ohlcv pushes when they have no active pool subscriptions. Repeated subscribe for the same pool overwrites the pool's intervals setting.
5.7 WebSocket error messages
{
"id": "sub-001",
"time": 1776682800000,
"type": "error",
"code": 4001,
"data": null,
"msg": "subscribe: missing required fields"
}
| Scenario | code | Example msg |
|---|---|---|
| JSON parsing failed | 4001 | Invalid JSON |
Missing id or type | 4001 | Missing id or type |
subscribe missing fields | 4001 | subscribe: missing required fields |
unsubscribe missing fields | 4001 | unsubscribe: missing required fields |
| Unknown command | 4002 | Unknown command: xxx |
6. Generic WebSocket
The generic WebSocket path is:
GET /api/v1/ws/{topic}
topic is used as the Durable Object instance name. The currently exposed capability only supports application-level ping/pong.
Generic WebSocket is suitable for connectivity or basic message-channel validation only. Use /api/v1/kline/ws for K-line real-time market data.
7. Operational debugging APIs (appendix)
These APIs directly inspect Durable Object internal state. They currently have no built-in authentication and should only be used in controlled networks or debugging environments.
Note: Operational debugging APIs do not always follow the unified JSON response shape. Handle
text/plainor raw Durable Object JSON according to each endpoint.
7.1 Ensure the ingester is running
POST /api/v1/admin/debug/ingester/ensure
curl -X POST "https://api.gelabs.org/api/v1/admin/debug/ingester/ensure" \
-H "X-API-Key: $API_KEY"
Successful response:
ok
7.2 Get ingester status
GET /api/v1/admin/debug/ingester/status
Key response fields:
| Field | Type | Always returned | Description |
|---|---|---|---|
connected | boolean | Yes | Whether the upstream CoinGecko WebSocket is connected. |
channelConfirmed | boolean | Yes | Whether the upstream Action Cable channel is confirmed. |
lastTickAt | string / null | Yes | Last tick time. |
poolCount | integer | Yes | Number of subscribed pools in memory. |
pools | array | Yes | Pool subscriptions held by the ingester. |
7.3 Get KlineHub status
GET /api/v1/admin/debug/hub/status
Key response fields:
| Field | Type | Always returned | Description |
|---|---|---|---|
wsCount | integer | Yes | Number of WebSocket clients connected to KlineHub. |
poolCount | integer | Yes | Number of pool keys with subscribed clients. |
pools[].key | string | Yes | Pool subscription key: chain_type:chain_id:pool_address:token. |
pools[].clientCount | integer | Yes | Number of clients subscribed to the pool key. |
lastIngestAt | string / null | Yes | Last ingester push time. |
lastIngestKey | string / null | Yes | Pool key of the latest ingested tick. |
8. Health check (appendix)
GET /api/v1/health
curl -H "X-API-Key: $API_KEY" "https://api.gelabs.org/api/v1/health"
Successful response:
{
"code": 0,
"message": "ok",
"data": {
"status": "ok",
"timestamp": "2026-04-16T08:00:00.000Z"
}
}
9. Error handling and best practices
9.1 Common HTTP errors
| HTTP | Common business code | Description |
|---|---|---|
| 400 | 10001 / 10002 / 10005 | Missing or invalid parameters, or WebSocket preflight error. |
| 404 | 10003 / 60001 | Route, pool, or upstream resource not found. |
| 409 | 10004 | Resource conflict, such as duplicate pool creation. |
| 500 | 99001 / 99002 / 99003 | Internal service, database, or cache error. |
Clients should check both the HTTP status code and response body code. Business success is represented by code: 0.
9.2 WebSocket integration recommendations
| Item | Recommendation |
|---|---|
| Heartbeat | Periodically send { "id": "hb-001", "type": "ping", "data": {} } and verify pong. |
| Reconnect | Use exponential backoff after disconnects and re-send subscriptions after reconnecting. |
| Gap filling | After reconnecting, call GET /api/v1/kline/ohlcv to fill missing historical data. |
| Idempotency | Treat the same pool, interval, and open_time as an upsert key. |
| Subscription state | Repeated subscribe overwrites the pool's intervals; maintain local subscription state. |
9.3 Timestamp handling
- HTTP
fromandtoaccept Unix seconds or milliseconds. - OHLCV
open_timeuses Unix seconds; WebSocket envelopetimeuses Unix milliseconds. - Store timestamps in UTC and convert time zones only in the presentation layer.
9.4 Price and volume precision
open,high,low,close, andvolumemay benull.- Use high-precision decimal types or string storage for prices, volume, and market values when precision matters.
- Market proxy data is passed through from CoinGecko and may use different field types from K-Line OHLCV data.
Document version: v1.0.0 | Last updated: 2026-04-28