The hive-tx library provides three methods for querying the Hive blockchain: callRPC, callREST, and callWithQuorum. This guide shows you how to use each one effectively.
RPC API Calls
Use callRPC to call JSON-RPC 2.0 methods on Hive nodes.
Basic RPC Calls
import { callRPC } from 'hive-tx'
// Get account information
const accounts = await callRPC('condenser_api.get_accounts', [['alice', 'bob']])
console.log(accounts[0].name) // 'alice'
console.log(accounts[0].balance) // '1000.000 HIVE'
// Get dynamic global properties
const props = await callRPC('condenser_api.get_dynamic_global_properties', [])
console.log('Head block:', props.head_block_number)
console.log('Total HIVE:', props.current_supply)
// Get post content
const post = await callRPC('condenser_api.get_content', ['alice', 'my-post-permlink'])
console.log('Title:', post.title)
console.log('Body:', post.body)
console.log('Upvotes:', post.net_votes)
Common RPC Methods
Account APIs
// Get accounts
callRPC('condenser_api.get_accounts', [['alice']])
// Get account history
callRPC('condenser_api.get_account_history', [
'alice',
-1, // start (most recent)
1000 // limit
])
// Get followers
callRPC('condenser_api.get_followers', [
'alice',
'', // start follower
'blog', // type
100 // limit
])
Content APIs
// Get post
callRPC('condenser_api.get_content', [
'author',
'permlink'
])
// Get discussions (feed)
callRPC('condenser_api.get_discussions_by_blog', [{
tag: 'alice',
limit: 20
}])
// Get active votes
callRPC('condenser_api.get_active_votes', [
'author',
'permlink'
])
Blockchain APIs
// Get block
callRPC('condenser_api.get_block', [12345678])
// Get block header
callRPC('condenser_api.get_block_header', [12345678])
// Get config
callRPC('condenser_api.get_config', [])
Witness APIs
// Get witnesses
callRPC('condenser_api.get_witnesses', [['witness1']])
// Get witness by account
callRPC('condenser_api.get_witness_by_account', [
'witness1'
])
// Get witness schedule
callRPC('condenser_api.get_witness_schedule', [])
Custom Timeout and Retry
import { callRPC } from 'hive-tx'
// Default: 4000ms timeout, 3 retries
const data1 = await callRPC('condenser_api.get_accounts', [['alice']])
// Custom timeout (10 seconds)
const data2 = await callRPC(
'condenser_api.get_content',
['author', 'permlink'],
10_000 // 10 second timeout
)
// Custom retry count (8 attempts)
const data3 = await callRPC(
'condenser_api.get_dynamic_global_properties',
[],
4000, // timeout
8 // retry attempts
)
The library automatically switches to the next node in the list on failure and retries the request.
Error Handling
import { callRPC, RPCError } from 'hive-tx'
try {
const accounts = await callRPC('condenser_api.get_accounts', [['nonexistent']])
if (accounts.length === 0) {
console.log('Account not found')
}
} catch (error) {
if (error instanceof RPCError) {
console.error('RPC Error:', error.message)
console.error('Error code:', error.code)
console.error('Error data:', error.data)
} else {
console.error('Network or other error:', error)
}
}
REST API Calls
Use callREST for type-safe REST API calls to specialized Hive APIs.
Available REST APIs
balance - Account balance and history
hafah - HAF Account History
hafbe - HAF Block Explorer
hivemind - Social layer (follows, communities)
hivesense - Analytics and statistics
reputation - Account reputation
nft-tracker - NFT tracking
hafsql - SQL queries
status - Node status
Balance API
import { callREST } from 'hive-tx'
// Get account balances
const balances = await callREST(
'balance',
'/accounts/{account-name}/balances',
{ 'account-name': 'alice' }
)
console.log('HIVE balance:', balances.hive)
console.log('HBD balance:', balances.hbd)
// Get aggregated history
const history = await callREST(
'balance',
'/accounts/{account-name}/aggregated-history',
{
'account-name': 'alice',
'coin-type': 'HBD',
'from-block': 0,
'to-block': 10000000
}
)
Status API
import { callREST } from 'hive-tx'
// Get node status
const status = await callREST('status', '/status')
console.log('Node version:', status.version)
console.log('Head block:', status.head_block_num)
console.log('Synced:', status.sync_status)
Path and Query Parameters
import { callREST } from 'hive-tx'
// Path parameters are replaced in the URL
const data1 = await callREST(
'balance',
'/accounts/{account-name}/balances',
{ 'account-name': 'alice' } // Replaces {account-name} in path
)
// Remaining parameters become query strings
const data2 = await callREST(
'balance',
'/accounts/{account-name}/aggregated-history',
{
'account-name': 'alice', // Path parameter
'coin-type': 'HIVE', // Query parameter: ?coin-type=HIVE
'from-block': 0, // Query parameter: &from-block=0
'to-block': 1000000 // Query parameter: &to-block=1000000
}
)
Custom Timeout and Retry
import { callREST } from 'hive-tx'
// With custom timeout and retry
const data = await callREST(
'status',
'/status',
undefined, // no parameters
10_000, // 10 second timeout
5 // 5 retry attempts
)
REST Error Handling
import { callREST } from 'hive-tx'
try {
const balances = await callREST(
'balance',
'/accounts/{account-name}/balances',
{ 'account-name': 'alice' }
)
} catch (error) {
if (error.message.includes('HTTP 404')) {
console.error('Endpoint not found or wrong parameters')
} else {
console.error('Request failed:', error.message)
}
}
REST API 404 errors often indicate incorrect parameters rather than missing data. Double-check your path and query parameters.
Quorum Calls for Critical Operations
Use callWithQuorum for critical operations that require consensus from multiple nodes:
import { callWithQuorum } from 'hive-tx'
// Get account balance with 2-node consensus (default)
const accounts = await callWithQuorum(
'condenser_api.get_accounts',
[['alice']]
)
// Require 3-node consensus for critical operation
const props = await callWithQuorum(
'condenser_api.get_dynamic_global_properties',
[],
3 // quorum size
)
How Quorum Works
From call.ts:267-309:
- Calls random nodes in parallel batches
- Compares results using JSON.stringify
- Returns the first result that appears in at least
quorum responses
- Automatically tries additional nodes if consensus not reached
// Example: Check if account exists with high confidence
import { callWithQuorum } from 'hive-tx'
try {
const accounts = await callWithQuorum(
'condenser_api.get_accounts',
[['alice']],
3 // require 3 nodes to agree
)
if (accounts.length > 0) {
console.log('Account exists (confirmed by 3 nodes)')
}
} catch (error) {
if (error.message.includes("Couldn't reach quorum")) {
console.error('Could not get consensus from nodes')
} else if (error.message.includes('No more nodes available')) {
console.error('All nodes failed')
}
}
Use quorum calls for:
- Account balance checks before large transfers
- Verifying witness votes
- Confirming proposal data
- Any operation where data accuracy is critical
Quorum calls are slower because they query multiple nodes. Use them only when you need guaranteed data accuracy.
Configuring Nodes
Configure RPC and REST nodes globally:
import { config } from 'hive-tx'
// Configure RPC nodes
config.nodes = [
'https://api.hive.blog',
'https://api.deathwing.me',
'https://api.openhive.network'
]
// Configure REST nodes
config.restNodes = [
'https://rest.hive.blog',
'https://rest.api.openhive.network'
]
// Configure timeout (milliseconds)
config.timeout = 10_000 // 10 seconds
// Configure retry attempts
config.retry = 5
The library automatically cycles through nodes on failure. Configure multiple nodes for high availability.
Complete Examples
Check Account Balance Before Transfer
import { callRPC, Transaction, PrivateKey } from 'hive-tx'
async function safeTransfer(
from: string,
to: string,
amount: string,
privateKey: PrivateKey
) {
// Get account info
const accounts = await callRPC('condenser_api.get_accounts', [[from]])
if (accounts.length === 0) {
throw new Error('Account not found')
}
// Parse balance
const balance = parseFloat(accounts[0].balance.split(' ')[0])
const transferAmount = parseFloat(amount.split(' ')[0])
if (balance < transferAmount) {
throw new Error(`Insufficient balance: ${balance} HIVE`)
}
// Create and broadcast transaction
const tx = new Transaction()
await tx.addOperation('transfer', {
from,
to,
amount,
memo: ''
})
tx.sign(privateKey)
return await tx.broadcast(true)
}
// Usage
const key = PrivateKey.fromLogin('alice', 'password', 'active')
const result = await safeTransfer('alice', 'bob', '10.000 HIVE', key)
console.log('Transfer successful:', result.tx_id)
Get Post with Full Details
import { callRPC } from 'hive-tx'
async function getPostDetails(author: string, permlink: string) {
// Get post content
const post = await callRPC('condenser_api.get_content', [author, permlink])
// Get active votes
const votes = await callRPC('condenser_api.get_active_votes', [author, permlink])
return {
title: post.title,
body: post.body,
created: post.created,
author: post.author,
permlink: post.permlink,
upvotes: votes.filter((v: any) => v.percent > 0).length,
downvotes: votes.filter((v: any) => v.percent < 0).length,
payout: post.pending_payout_value,
replies: post.children
}
}
// Usage
const details = await getPostDetails('alice', 'my-first-post')
console.log(details)
Monitor New Blocks
import { callRPC } from 'hive-tx'
async function watchBlocks(callback: (block: any) => void) {
let lastBlock = 0
setInterval(async () => {
try {
const props = await callRPC('condenser_api.get_dynamic_global_properties', [])
const currentBlock = props.head_block_number
if (currentBlock > lastBlock && lastBlock > 0) {
// Get new blocks
for (let i = lastBlock + 1; i <= currentBlock; i++) {
const block = await callRPC('condenser_api.get_block', [i])
callback(block)
}
}
lastBlock = currentBlock
} catch (error) {
console.error('Error fetching blocks:', error)
}
}, 3000) // Check every 3 seconds
}
// Usage
watchBlocks((block) => {
console.log('New block:', block.block_id)
console.log('Transactions:', block.transactions.length)
})
Next Steps