Introduction
Welcome to the Hgraph.io GraphQL API! You can use the Hgraph.io API to access Hedera mainnet and testnet data.
FAQS
Data Formats
How does the API format data in comparison to the mirror node REST api?
Entity Id Format
Hgraph.io will return entity ids without the "0.0." prefix. The GraphQL API also expects any variable to be a bigint
, and not include the "0.0." prefix. This is true for token ids, account ids, and topic ids.
When passing variables into a query, remember to remove the "0.0." from the variable first.
Metadata Format
Metadata values are returned in an encoded hex format. Decoding the metadata string is imperitive in order to interact with the metadata string or stringified JSON object.
Amount / Price Format
All amount values returned are in tiny bars. Be sure to convert the tinybars to HBARs before executing logic or sending back to the user.
const HBAR = amountReturnedFromQuery / 100000000
Timestamps
Timestamps are returned in numeric form, bigint
, and do not fit into the standard javascript Number
type. One has to take explicit care in conserving the precision of nanoseconds.
The adjacent example demonstrates getting the latest transaction from json-bigint
library.
import fetch from 'isomorphic-fetch'
import JSONBigInt from 'json-bigint'
const API_KEY = '<HGRAPH_API_KEY>'
const {parse, stringify} = JSONBigInt({
useNativeBigInt: true,
// alwaysParseAsBig: true,
})
BigInt.prototype.toJSON = function () {
if (Number.MIN_SAFE_INTEGER < this && this < Number.MAX_SAFE_INTEGER) return Number(this)
else return this.toString()
}
async function patchedFetch(...args) {
const response = await fetch(...args)
const text = await response.text()
const json = parse(text)
response.json = async () => json
return response
}
async function main() {
// return
const response = await patchedFetch('https://beta.api.hgraph.io/v1/graphql', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': API_KEY,
},
// JSON.stringify is okay here because we have no BigInts that need special treatment
body: stringify({
query: `
query LatestTransaction {
transaction(limit: 1, order_by: {consensus_timestamp: desc}) {
consensus_timestamp
valid_start_ns
payer_account_id
}
}
`,
}),
})
const json = await response.json()
console.dir(json, {depth: null})
console.log(stringify(json)) // can also use JSON.stringify because we declared the `.toJSON()` above
// Test
const {valid_start_ns, payer_account_id} = json.data.transaction[0]
const transactionId = `0.0.${payer_account_id}-${valid_start_ns
.toString()
.substring(0, 10)}-${valid_start_ns.toString().substring(10)}`
const restApiEndpoint = `https://mainnet-public.mirrornode.hedera.com/api/v1/transactions/${transactionId}`
if (restApiEndpoint) console.log('🤞')
const restResponse = await patchedFetch(restApiEndpoint)
if (restResponse.ok) console.log('✅', response.status)
else console.log('😕')
console.dir(await restResponse.json(), {depth: null})
}
main()
Transaction Id
How do I construct a REST API transactionId
?
The transactionId
follows the format <entity_id>-<seconds>-<nanoseconds>
The GraphQL query to the right will return the latest transaction. An example output is in the json
tab to the right.
The javascript
tab shows the formula for constructing the transaction id from this output.
Corresponding endpoints:
- https://hashscan.io/mainnet/transaction/0.0.41099-1670266826-039225748
- https://mainnet-public.mirrornode.hedera.com/api/v1/transactions/0.0.41099-1670266826-039225748
Supporting documentation:
- https://docs.hedera.com/guides/core-concepts/transactions-and-queries
- https://docs.hedera.com/guides/docs/mirror-node-api/rest-api#transaction-by-transaction-id
query LatestTransaction {
transaction(limit: 1, order_by: {consensus_timestamp: desc}) {
consensus_timestamp
valid_start_ns
payer_account_id
}
}
{
"data": {
"transaction": [
{
"consensus_timestamp": "1670266835889120003",
"valid_start_ns": "1670266826039225748",
"payer_account_id": 41099
}
]
}
}
const {valid_start_ns, payer_account_id} = json.data.transaction[0]
const transactionId = `0.0.${payer_account_id}-${valid_start_ns.substring(
0,
10
)}-${valid_start_ns.substring(10)}`
const restApiEndpoint = `https://mainnet-public.mirrornode.hedera.com/api/v1/transactions/${transactionId}`
Query Tips
Getting Transaction Values
When getting transaction values and aggregate transaction sums, be sure to only get the negative values.
If you don't specify only negative (HBAR leaving the account) the value will always be 0.
This is because the API will total the amount leaving as negative and the amount being received as positive which will always be 0.
In the example below, we remove all values under 1 HBAR (100,000,000 tiny bars). This is a good way to not have the fees of the transaction come back in the query.
transactions {
crypto_transfer(where: {amount: {_lte: "-100000000"}}) {
amount
}
sender_account_id
}
Authorization
Developer portal
The quickest way to get started is to use the GraphiQL editor on our developer portal.
The editor in our developer portal to help you develop queries specifically for your use case. You can also use whichever GraphQL client you choose.
If using a different client, you can inspect the network tab in your browser to find your current authorization token and add that as a header to your GraphQL IDE.
Authorization: Bearer <OAUTH_TOKEN>
Programmatic access
Hgraph.io uses API keys to allow programmatic access to the API. You can register for an API key at our developer portal.
Hgraph.io expects for the API key to be included in all API requests to the server in a header that looks like the following:
To authorize, add and
x-api-key
to each request. Here’s an example demonstrating the use of anAPI_KEY
curl -X POST "https://beta.api.hgraph.io/v1/graphql" \
-H 'Content-Type: application/json' \
-H 'x-api-key: <API_KEY>' \
-d '{"query":"query LatestTransaction {\\n transaction(limit: 1, order_by: {consensus_timestamp: desc}) {\\n consensus_timestamp\\n }\\n}\\n","variables":null,"operationName":"LatestTransaction"}'
// You can use whatever http client library you prefer for simple queries.
// We recommend using a GraphQL specific client for more robust use cases
import {fetch} from 'undici'
async function authenticateWithHgraph() {
const response = await fetch('https://beta.api.hgraph.io/v1/graphql', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-api-key': '<API_KEY>',
},
body: {
query:
'query LatestTransaction {\\n transaction(limit: 1, order_by: {consensus_timestamp: desc}) {\\n consensus_timestamp\\n }\\n}\\n',
},
})
const json = response.json()
// Do what you like with the data
console.log(json)
}
authenticateWithHgraph()
Make sure to replace
<API_KEY>
with your API key.
Common Queries
Get the latest transaction timestamp
curl -X POST "https://beta.api.hgraph.io/v1/graphql" \
-H 'Content-Type: application/json' \
-H 'x-api-key: <API_KEY>' \
-d '{"query":"query LatestTransaction {\\n transaction(limit: 1, order_by: {consensus_timestamp: desc}) {\\n consensus_timestamp\\n }\\n}\\n","variables":null,"operationName":"LatestTransaction"}'
The example query above returns JSON structured like this:
{"data": {"transaction": [{"consensus_timestamp": 1665369499984826092}]}}
This query gets the timestamp of the most rescent transaction.
Get messages from topic id
Getting topic id messages through an api call is helpful to be able to easily read this information without needing to do a transaction through the Hedera SDKs.
// Don't use format 0.0.xxxxxx for account ids, token ids, or topic ids.
// Instead only use the integers after the 0.0. when talking to the API
const topicId = 1319899
const endpoint = 'https://beta.api.hgraph.io/v1/graphql'
// Create query for getting farming topic messages
const headers = {
'content-type': 'application/json',
'x-api-key': '<API_KEY> ',
}
const topicMessagesQuery = {
operationName: 'GetFarmingHCS',
query: `query GetFarmingHCS($topicId: bigint) {
topic_message(where: {topic_id: {_eq: $topicId}}, order_by: {consensus_timestamp: desc}) {
message
sequence_number
consensus_timestamp
}
}`,
variables: {
topicId: topicId,
},
}
// This example shows how to use axios npm package
// to call Hgraph.io
const queryResponse = await axios({
url: endpoint,
method: 'post',
headers: headers,
data: topicMessagesQuery,
})
// Map to topic messages
const topicMessages = queryResponse.data.data.topic_message
Get NFT metadata string
When interacting with NFTs, it's important to be able to easily get the metadata string so you can execute logic based off of the metadata object variables.
This query will get the metadata string for a specific NFT by its token id and serial number.
// Example API call for token information
const queryVariables = {
tokenId: 621100,
serialNumber: 1,
}
// GraphQL call to get token information
const GetNftInfo = `
nft(where: {serial_number: {_eq: $serialNumber}, token_id: {_eq: $tokenId}}) {
metadata
}
`
const body = {query: GetNftInfo, variables: queryVariables}
// Fetch call to post the GraphQL call to hgraph.io
// Set process.env.HGRAPH_API_KEY in your .env file or replace "process.env.HGRAPH_API_KEY with your key
const response = await fetch('https://beta.api.hgraph.io/v1/graphql', {
method: 'POST',
headers: {
'x-api-key': process.env.HGRAPH_API_KEY,
'content-type': 'application/json',
},
body: JSON.stringify(body),
})
// Get JSON from fetch response
const res = await response.json()
The above command returns JSON structured like this:
{
"data": {
"nft": [
{
"metadata": "\\x697066733a2f2f6261667972656964747635726472686c727a6c69676f6b6a636577336c7577776d66346e6264746a34757a65686a6d7079366976753466716a76612f6d657461646174612e6a736f6e"
}
]
}
}
Function to convert encoded metadata strings
Metadata values are returned in an encoded format. This helper function easily decodes that value into the expected string in the message.
// Taking the reponse from the previous example:
// Map new variable to returned data.
const nft = res.data.data.nft
// Function used to convert hex ASCII (encoding of metadata on mainnet) to text
const getMetadataString = (hexx) => {
let hex = hexx.toString() //force conversion
hex = hex.split('\\x')[1]
let str = ''
for (let i = 0; i < hex.length; i += 2)
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16))
return str
}
// Convert metadata string to usable IPFS CID string
const metadataString = getMetadataString(nft.metadata)
Sense token associated with an account id
A very common piece of data needed by developers is knowing whether a certain account id has a token id associated. Without the account id associating a particular token id, a developer cannot send tokens to that account.
This function allows developers to easily determine association by passing in the account id and token id in question.
// Sense if an account has associated the token id.
// If not, create transactions and send to client
const senseTokenAssociation = async (accountId, tokenId) => {
// Set headers for hgraph.io
const tokenAssociationEndpoint = 'https://beta.api.hgraph.io/v1/graphql'
const tokenAssociationHeaders = {
'content-type': 'application/json',
'x-api-key': process.env.HGRAPH_SECRET,
}
// Call and sense token Association
// token ids and account ids for queries
const tokenAssociationQuery = {
operationName: 'GetTokenAssociation',
query: `query GetTokenAssociation($accountId: bigint, $tokenId: bigint) {
token_account(where: {token_id: {_eq: $tokenId}, account_id: {_eq: $accountId}}) {
associated
}
}`,
variables: {
accountId: accountId.split('.0.')[1],
tokenId: tokenId.split('.0.')[1],
},
}
// Example using axios npm package to post GraphQL query
const userTokenAssociationQueryResponse = await axios({
url: tokenAssociationEndpoint,
method: 'post',
headers: tokenAssociationHeaders,
data: tokenAssociationQuery,
})
// Get data from response
const data = userTokenAssociationQueryResponse.data
const tokenAccount = data.data.token_account
let associated = tokenAccount.length >= 1
return {associated: associated}
}
Get NFTs held by account id
This query will return all of the NFTs held by a particular account id. In this example, the account id being passed to the function is in the standard entity id format (0.0.XXXXXXX).
// Get all NFTs being held by an account id
// Example input: accountId = "0.0.306412"
const getNftsHeldByAccountId = async (accountId) => {
// call API to get farming NFTs from accountId
const endpoint = 'https://beta.api.hgraph.io/v1/graphql'
const headers = {
'content-type': 'application/json',
'x-api-key': process.env.HGRAPH_SECRET,
}
const nftsHeldByAccountIdQuery = {
operationName: 'GetAccountNfts',
query: `query GetAccountNfts($accountId: bigint) {
nft(where: {account_id: {_eq: $accountId}}, order_by: {modified_timestamp: desc}) {
account_id
serial_number
metadata
token_id
created_timestamp
token {
name
treasury_account_id
}
}
}`,
variables: {
accountId: accountId.split('0.0.')[1],
},
}
const nftsHeldByAccountIdQueryResponse = await axios({
url: endpoint,
method: 'post',
headers: headers,
data: nftsHeldByAccountIdQuery,
})
// Get data from response
const data = nftsHeldByAccountIdQueryResponse.data
const nftsHeld = data.data.nft
for (let index = 0; index < nftsHeld.length; index++) {
const nftHeld = nftsHeld[index]
// Uses previous metadata string function in example above
nftHeld.metadata = getMetadataString(nftHeld.metadata)
nftHeld.name = nftHeld.token.name
nftHeld.treasury_account_id = nftHeld.token.treasury_account_id
}
return nftsHeld
}
Token and hbar balance snapshots
The account_balance
node and the token_balance
node (in the sense of a GraphQL query node) are balance snapshots.
Every 15 minutes, the consensus nodes (Hedera network servers) calculate the hbar and token balances for every account and store those as snapshots.
To see the most recent consensus timestamp for token_balances
:
query MostRecentTokenBalanceConsensusTimestamp {
token_balance(
limit: 1
order_by: {consensus_timestamp: desc}
distinct_on: consensus_timestamp
) {
account_id
balance
consensus_timestamp
}
}
Get the top holders of a token
When saving snapshots, the token_balance
consensus timestamp may not be updated if there was not a change in token balance for a given timestamp.
First, find the most recent snapshot consensus_timestamp
that has an entry for a token balance.
query MostRecentTokenBalanceConsensusTimestamp {
token_balance(
limit: 1
order_by: {consensus_timestamp: desc}
distinct_on: consensus_timestamp
) {
account_id
balance
consensus_timestamp
}
}
query TopHoldersOfToken($token_id: bigint, $consensus_timestamp: bigint) {
token(where: {token_id: {_eq: $token_id}}) {
type
treasury_account_id
total_supply
symbol
name
}
token_balance(where: {token_id: {_eq: $token_id}, consensus_timestamp: {_eq: $consensus_timestamp}, balance: {_gt: 0}}, order_by: {balance: desc}) {
account_id
balance
consensus_timestamp
token_id
}
}