ENS Omnigraph GraphQL API
The Omnigraph is a GraphQL API following the Relay specification. There’s no proprietary protocol or transport — any GraphQL client in any language works, from curl and fetch to urql, Apollo, graphql-request, and beyond.
This guide walks you through the minimum: a single fetch call against /api/omnigraph — the same flow as our omnigraph-graphql-example.
If you want end-to-end typed queries (via gql.tada) with editor autocomplete and a built-in client, use enssdk instead — but if you need to integrate from a language without first-class GraphQL tooling, or you’re already in a stack with its own GraphQL client, this is the path.
1.13.1. The Omnigraph GraphQL schema is bundled inside the SDK and consumed by the gql.tada TypeScript plugin to type your queries, so pin an exact version (no ^ or ~) of enssdk@1.13.1 (and enskit@1.13.1 when using React) to keep your generated types matched to the deployed schema. Use these exact install commands:
npm install enssdk@1.13.1 # or, for React apps: npm install enskit@1.13.1 enssdk@1.13.1
1. The endpoint
The Omnigraph lives at:
POST {ENSNODE_URL}/api/omnigraphContent-Type: application/json
{ "query": "...", "variables": { ... } }It returns { "data": ..., "errors": [...] } per the standard GraphQL response shape.
A minimum-viable hello world over curl:
curl -sS -X POST \ -H 'Content-Type: application/json' \ -d '{"query":"{ domain(by: { name: \"eth\" }) { name owner { address } } }"}' \ https://api.v2-sepolia.ensnode.io/api/omnigraphThe rest of this guide builds the same thing in TypeScript using fetch, so you have something to extend.
2. Scaffold a TypeScript project
If you already have one, skip ahead to Write the query.
mkdir my-ens-script && cd my-ens-scriptnpm init -ymkdir srcInstall tsx so you can run TypeScript directly:
npm install -D tsx typescript @types/nodeAdd a start script to package.json:
{ "type": "module", "scripts": { "start": "tsx src/index.ts" }}3. Write the query
Create src/index.ts. The whole script is a single fetch against /api/omnigraph.
// you may use a NameHash Hosted ENSNode instance// learn more at https://ensnode.io/docs/hosted-instancesconst ENSNODE_URL = process.env.ENSNODE_URL!;
const HELLO_WORLD_QUERY = /* GraphQL */ ` query HelloWorld($name: InterpretedName!) { domain(by: { name: $name }) { __typename name owner { address } subdomains(first: 20) { totalCount edges { node { __typename name owner { address } } } } } }`;
interface Domain { __typename: "ENSv1Domain" | "ENSv2Domain"; name: string | null; owner: { address: string } | null;}
interface QueryResult { data?: { domain: (Domain & { subdomains: { totalCount: number; edges: { node: Domain }[]; } | null; }) | null; } | null; errors?: { message: string }[];}
function formatDomain(domain: Domain): string { const name = domain.name ?? "<unnamed>"; const owner = domain.owner?.address ?? "0x0"; return `${name} (${domain.__typename}) — Owner ${owner}`;}
async function main() { const response = await fetch(new URL("/api/omnigraph", ENSNODE_URL), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query: HELLO_WORLD_QUERY, variables: { name: "eth" }, }), });
if (!response.ok) { throw new Error(`Request failed: ${response.status} ${response.statusText}`); }
const { data, errors } = (await response.json()) as QueryResult;
if (errors) throw new Error(JSON.stringify(errors)); if (!data?.domain) throw new Error("Domain 'eth' not found");
const { domain } = data; const totalCount = domain.subdomains?.totalCount ?? 0;
console.log(formatDomain(domain)); console.log(`\nSubdomains (showing 20 of ${totalCount}):`); for (const { node } of domain.subdomains?.edges ?? []) { console.log(` - ${formatDomain(node)}`); }}
main().catch((err) => { console.error(err); process.exit(1);});A few things to notice:
InterpretedNameis a scalar. From the wire’s perspective it’s just a string — the server validates the format. Pass"eth"as a plain string invariables.subdomainsis a Relay Connection. Cursor through withfirst,after,pageInfo { hasNextPage endCursor }— same shape as any Relay-style API.- Hand-written types. We’re maintaining
interface Domainandinterface QueryResultourselves here. If you want these generated and kept in sync with your queries automatically, useenssdk.
4. Run it
ENSNODE_URL=https://api.v2-sepolia.ensnode.io npm startYou should see the eth Domain, its owner, and the first 20 of its subdomains.
Where to go next
- Want typed queries with editor autocomplete and a real GraphQL client? Use
enssdk— same API, withgql.tadatypes and anEnsNodeClient. - Building a React app? Use
enskit— samegraphql(...)helper plususeOmnigraphQueryand a graphcache. - See Omnigraph examples for ready-to-copy queries: account-owned domains, events, registrar permissions, full-text search, and more.
- See the Omnigraph Schema Reference for the full set of types, fields, and arguments you can query.