⚠️Blast API (blastapi.io) ends Oct 31. Migrate to Dwellir and skip Alchemy's expensive compute units.
Switch Today →
Skip to main content

Coming soon: Need support for this? Email support@dwellir.com and we will enable it for you.

ANS Queries

Aptos Name Service (ANS) provides human-readable names for Aptos addresses, similar to DNS for the internet. The GraphQL API enables efficient querying of ANS registrations, allowing applications to resolve names to addresses, lookup reverse mappings, check expiration dates, and manage domain portfolios programmatically.

Overview#

ANS transforms complex hexadecimal addresses like 0x1a2b3c... into memorable names like alice.apt, significantly improving user experience. The GraphQL indexer provides indexed access to ANS data, enabling fast lookups for wallet displays, payment systems, social features, and any application needing human-readable identifiers.

Core Query Patterns#

Resolve Name to Address#

query ResolveName($name: String!) {
ans_lookup(where: { name: { _eq: $name } }) {
name
address
expiration_timestamp
registered_at
owner
}
}

Reverse Lookup (Address to Names)#

query AddressToNames($address: String!) {
ans_lookup(
where: { address: { _eq: $address } },
order_by: { registered_at: desc }
) {
name
expiration_timestamp
is_primary
}
}

Check Name Availability#

query CheckAvailability($name: String!) {
ans_lookup(
where: {
name: { _eq: $name },
expiration_timestamp: { _gt: "now()" }
}
) {
name
owner
}
}

Get Primary Name#

query GetPrimaryName($address: String!) {
ans_lookup(
where: {
address: { _eq: $address },
is_primary: { _eq: true }
},
limit: 1
) {
name
expiration_timestamp
}
}

Search Names by Pattern#

query SearchNames($pattern: String!, $limit: Int!) {
ans_lookup(
where: { name: { _like: $pattern } },
order_by: { registered_at: desc },
limit: $limit
) {
name
address
owner
registered_at
}
}

Expiring Names#

query ExpiringNames($days: Int!) {
ans_lookup(
where: {
expiration_timestamp: {
_gte: "now()",
_lte: "now() + ${days} days"
}
},
order_by: { expiration_timestamp: asc }
) {
name
address
expiration_timestamp
}
}

Real-World Use Cases#

  1. Wallet Applications: Display user-friendly names instead of addresses in transaction histories, contact lists, and payment interfaces for improved UX.

  2. Payment Systems: Allow users to send payments to names like "alice.apt" instead of copying long addresses, reducing errors and improving accessibility.

  3. Social Platforms: Enable username-based social features where users can follow, message, or interact with others using memorable names.

  4. Domain Marketplaces: Build platforms for buying, selling, and trading ANS domains with search, filtering, and expiration monitoring.

  5. Portfolio Management: Track domain portfolios, monitor expiration dates, and manage renewal workflows for users with multiple names.

  6. Identity Verification: Use ANS as a lightweight identity system where verified names provide reputation and trust signals.

Best Practices#

Cache Resolved Names: ANS data changes infrequently - implement caching with appropriate TTLs to reduce API calls.

Handle Non-Existent Names: Always check for null/empty results when resolving names and provide clear user feedback.

Validate Name Format: Implement client-side name validation (format, length, allowed characters) before querying.

Check Expiration: Always verify expiration_timestamp to ensure names are still active before relying on them.

Support Both Directions: Implement both name-to-address and address-to-name lookups for complete functionality.

Prioritize Primary Names: When an address owns multiple names, prefer displaying the primary name for consistency.

Batch Lookups: When resolving multiple names, batch them into single GraphQL queries for efficiency.

TypeScript Integration#

import { ApolloClient, gql } from "@apollo/client";

const client = new ApolloClient({
uri: "https://api-aptos-mainnet.n.dwellir.com/YOUR_API_KEY/v1/graphql"
});

async function resolveName(name: string): Promise<string | null> {
const { data } = await client.query({
query: gql`
query ResolveName($name: String!) {
ans_lookup(where: { name: { _eq: $name } }) {
address
expiration_timestamp
}
}
`,
variables: { name }
});

const result = data.ans_lookup[0];
if (!result) return null;

const now = Date.now();
const expiration = new Date(result.expiration_timestamp).getTime();

return expiration > now ? result.address : null;
}

async function getPrimaryName(address: string): Promise<string | null> {
const { data } = await client.query({
query: gql`
query GetPrimaryName($address: String!) {
ans_lookup(
where: {
address: { _eq: $address },
is_primary: { _eq: true }
},
limit: 1
) {
name
expiration_timestamp
}
}
`,
variables: { address }
});

const result = data.ans_lookup[0];
if (!result) return null;

const now = Date.now();
const expiration = new Date(result.expiration_timestamp).getTime();

return expiration > now ? result.name : null;
}

Advanced Queries#

Domain Portfolio Analysis#

query PortfolioStats($owner: String!) {
active: ans_lookup_aggregate(
where: {
owner: { _eq: $owner },
expiration_timestamp: { _gt: "now()" }
}
) {
aggregate { count }
}

expiring_soon: ans_lookup_aggregate(
where: {
owner: { _eq: $owner },
expiration_timestamp: {
_gt: "now()",
_lte: "now() + 30 days"
}
}
) {
aggregate { count }
}

domains: ans_lookup(
where: { owner: { _eq: $owner } },
order_by: { expiration_timestamp: asc }
) {
name
address
expiration_timestamp
registered_at
}
}
query TrendingNames($hours: Int!) {
ans_lookup(
where: {
registered_at: { _gte: "now() - ${hours} hours" }
},
order_by: { registered_at: desc },
limit: 50
) {
name
address
owner
registered_at
}
}

Common Patterns#

// Display name with fallback to address
function displayName(address: string, ansName?: string | null): string {
if (ansName) return ansName;
return `${address.substring(0, 6)}...${address.substring(address.length - 4)}`;
}

// Validate name format
function isValidANSName(name: string): boolean {
return /^[a-z0-9-]{1,63}\.apt$/.test(name);
}

// Check if name is expired
function isExpired(expirationTimestamp: string): boolean {
return new Date(expirationTimestamp).getTime() < Date.now();
}