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.
- thumbnails: A list of thumbnail objects.
- 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:
Argument | Type | Description |
---|---|---|
first | Int | Limits the number of items returned (i.e., first: 10 fetches the first 10 items). |
after | String | Specifies the cursor after which to start fetching items (i.e., after: "4" ). |
skip | Int | (Optional) Skips a specified number of items after the cursor (useful for offsets within a page). |
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
andisERC1155
) 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.