Skip to main content

Working With API Responses

Structure of the Response

This is an example response you might get when executing the Get NFT Token Details example query from the Playgrounds guides.

{
"data": {
"token": {
"name": "Singular Focus",
"description": "The Partition is formed End Chapter 1 These former husks / Vessels adorned with ancestry / Carry the code / Of shared memory // Though flayed and quartered / The bone is porous / Fertile grounds / Ripe for the return // New flesh grows / Once we learn how to die / Souls for the bankless / The tree decides // Power sown in urns and ashes / Eternity learned / in collective demise.",
"image": {
"thumbnails": [
{
"url": "/nftindexer/thumbnail/1/42141102/medium"
}
]
},
"contract": {
"id": "0xc9041f80dce73721a5f6a779672ec57ef255d27c",
"isERC721": true,
"isERC1155": false
}
}
}
}

Response Structure

Here's the general response structure for the Get NFT Token Details query.

{
"data": {
"token": {
"name": "String",
"description": "String",
"image": {
"thumbnails": [
{
"url": "String"
}
]
},
"contract": {
"id": "String",
"isERC721": Boolean,
"isERC1155": Boolean
}
}
}
}

Response Object Definitions

  • data: The root object containing the response data.
  • token: The object representing the token details.
    • name: The name of the token.
    • description: A description of the token.
    • image: An object containing media details.
      • thumbnails: A list of thumbnail objects.
        • url: The URL of the thumbnail image.
    • contract: An object representing the contract details.
      • id: The contract address.
      • isERC721: Whether the contract is ERC-721 compliant.
      • isERC1155: Whether the contract is ERC-1155 compliant.

Accessing the Data

The response structure is static and its response fields can be accessed in a consistent manner.

Below are some examples of how the response fields may be accessed in a project.

JavaScript

if (response.data && response.data.token) {
const tokenName = response.data.token.name;
const tokenDescription = response.data.token.description;
const thumbnailUrl = response.data.token.image.thumbnails[0].url;
const contractId = response.data.token.contract.id;
const isERC721 = response.data.token.contract.isERC721;
const isERC1155 = response.data.token.contract.isERC1155;

console.log("Token Name: " + tokenName);
console.log("Description: " + tokenDescription);
console.log("Thumbnail URL: " + thumbnailUrl);
console.log("Contract ID: " + contractId);
console.log("Is ERC-721: " + isERC721);
console.log("Is ERC-1155: " + isERC1155);

// Example: Determine token type
if (isERC721) {
console.log("This is an ERC-721 token.");
} else if (isERC1155) {
console.log("This is an ERC-1155 token.");
} else {
console.log("This token does not match ERC-721 or ERC-1155 standards.");
}
} else {
console.error("Invalid response structure");
}

Python

if "data" in response and "token" in response["data"]:
token_name = response["data"]["token"]["name"]
token_description = response["data"]["token"]["description"]
thumbnail_url = response["data"]["token"]["image"]["thumbnails"][0]["url"]
contract_id = response["data"]["token"]["contract"]["id"]
is_erc721 = response["data"]["token"]["contract"]["isERC721"]
is_erc1155 = response["data"]["token"]["contract"]["isERC1155"]

print("Token Name: " + token_name)
print("Description: " + token_description)
print("Thumbnail URL: " + thumbnail_url)
print("Contract ID: " + contract_id)
print("Is ERC-721: " + str(is_erc721))
print("Is ERC-1155: " + str(is_erc1155))

# Example: Determine token type
if is_erc721:
print("This is an ERC-721 token.")
elif is_erc1155:
print("This is an ERC-1155 token.")
else:
print("This token does not match ERC-721 or ERC-1155 standards.")
else:
print("Invalid response structure")

Ordering

OneSource databases are built with PostgreSQL which does not have default ordering.

The only way to ensure consistent ordering of results within responses is to use the sorting arguments orderBy and/or orderDirection.

Example

query GetWalletBalances {
balances(
owner: "0x742d35Cc6634C0532925a3b844Bc454e4438f44e"
first: 20
orderBy: VALUE
orderDirection: DESC
) {
balances {
value
contract {
name
type
id
}
}
count
}
}

Pagination

When querying large datasets, it is best to paginate through results to avoid requesting the entire dataset in a single response.

Refer to the GraphQL documentation on pagination for a complete overview of GraphQL pagination.

OneSource supports simple cursor-based pagination using three arguments:

ArgumentTypeDescription
firstIntLimits the number of items returned (i.e., first: 10 fetches the first 10 items).
afterStringSpecifies the cursor after which to start fetching items (i.e., after: "4").
skipInt(Optional) Skips a specified number of items after the cursor (useful for offsets within a page).
tip

Use ordering in conjunction with pagination when querying large datasets in order to ensure the entire dataset is retrieved.

Example

query Tokens {
tokens(first: 50, orderBy: NAME, after: "50") {
tokens {
contract {
name
id
}
metadataContent
tokenID
}
count
cursor
}
}

JavaScript

const PAGE_SIZE = 50;
let after = null;
let allTokens = [];
let totalCount = 0;

while (true) {
const query = `
query Tokens($after: String) {
tokens(first: ${PAGE_SIZE}, orderBy: NAME, after: $after) {
tokens {
contract {
name
id
}
metadataContent
tokenID
}
count
cursor
}
}
`;

const response = await fetch(https://api.onesource.io/v1/ethereum/graphql, {
method: "POST",
headers: {
"x-bp-token": ONESOURCE_APIKEY,
"Content-Type": "application/json"
},
body: JSON.stringify({
query,
variables: { after }
}),
}).then((r) => r.json());

const { tokens, cursor, count } = response.data?.tokens || {};

if (!tokens || tokens.length === 0) break;

allTokens.push(...tokens);
totalCount = count;

if (!cursor || tokens.length < PAGE_SIZE) break;
after = cursor;
}

console.log(`Fetched ${allTokens.length} tokens (total count: ${totalCount})`);

Python

import requests
import json

PAGE_SIZE = 50
after = None
all_tokens = []

while True:
query = """
query Tokens($after: String) {
tokens(first: %s, orderBy: NAME, after: $after) {
tokens { contract { name id } metadataContent tokenID }
cursor
}
}
""" % PAGE_SIZE

response = requests.post(
"https://api.onesource.io/v1/ethereum/graphql",
headers={"x-bp-token": "ONESOURCE_APIKEY", "Content-Type": "application/json"},
json={"query": query, "variables": {"after": after}}
).json()

tokens = response.get("data", {}).get("tokens", {})
batch = tokens.get("tokens", [])
cursor = tokens.get("cursor")

all_tokens.extend(batch)
if not cursor or len(batch) < PAGE_SIZE: break
after = cursor

print(f"Total tokens fetched:", len(all_tokens))

Best Practices

  • Choose a sensible first value (50–100) to balance response size vs. response time.
  • Always check for an empty or smaller-than‑page response to know when you've reached the end.

Handling Errors

Since the response is dynamic, developers should always check for errors and validate the structure of the response before accessing data.

JavaScript

if (response.errors) {
console.error("Error: " + response.errors[0].message);
} else if (response.data && response.data.token) {
const isERC721 = response.data.token.contract.isERC721;
const isERC1155 = response.data.token.contract.isERC1155;

if (isERC721) {
console.log("This is an ERC-721 token.");
} else if (isERC1155) {
console.log("This is an ERC-1155 token.");
} else {
console.log("This token does not match ERC-721 or ERC-1155 standards.");
}
} else {
console.error("Invalid response structure");
}

Python

if "errors" in response:
print("Error: " + response["errors"][0]["message"])
elif "data" in response and "token" in response["data"]:
is_erc721 = response["data"]["token"]["contract"]["isERC721"]
is_erc1155 = response["data"]["token"]["contract"]["isERC1155"]

if is_erc721:
print("This is an ERC-721 token.")
elif is_erc1155:
print("This is an ERC-1155 token.")
else:
print("This token does not match ERC-721 or ERC-1155 standards.")
else:
print("Invalid response structure")

Common Use Cases

Displaying Token Information

JavaScript

function displayTokenInfo(response) {
if (response.data && response.data.token) {
const tokenName = response.data.token.name;
const tokenDescription = response.data.token.description;
const thumbnailUrl = response.data.token.image.thumbnails[0].url;
const isERC721 = response.data.token.contract.isERC721;
const isERC1155 = response.data.token.contract.isERC1155;

document.getElementById("token-name").innerText = tokenName;
document.getElementById("token-description").innerText = tokenDescription;
document.getElementById("token-image").src = thumbnailUrl;

if (isERC721) {
document.getElementById("token-type").innerText = "ERC-721";
} else if (isERC1155) {
document.getElementById("token-type").innerText = "ERC-1155";
} else {
document.getElementById("token-type").innerText = "Unknown Token Type";
}
} else {
console.error("Invalid response structure");
}
}

Filtering Tokens by Contract Type

Python

def filter_tokens(response):
if "data" in response and "tokens" in response["data"]:
tokens = response["data"]["tokens"]
erc721_tokens = [token for token in tokens if token["contract"]["isERC721"]]
erc1155_tokens = [token for token in tokens if token["contract"]["isERC1155"]]

print("ERC-721 Tokens: " + str(erc721_tokens))
print("ERC-1155 Tokens: " + str(erc1155_tokens))
else:
print("Invalid response structure")

Best Practices

  • Validate the Response Structure: Always check if the expected fields (including isERC721 and isERC1155) exist before accessing them.
  • Handle Missing Fields Gracefully: Use optional chaining (JavaScript) or .get() (Python) to avoid runtime errors.
  • Cache Responses: Cache frequently accessed data to reduce API calls and improve performance.
  • Use TypeScript or Type Annotations: If possible, use TypeScript (JavaScript) or type annotations (Python) to ensure type safety when working with the response.