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. The endpoint
Section titled “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.alpha.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
Section titled “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
Section titled “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/integrate/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
Section titled “4. Run it”ENSNODE_URL=https://api.alpha.ensnode.io npm startYou should see the eth Domain, its owner, and the first 20 of its subdomains.
Where to go next
Section titled “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 the Omnigraph Cookbook 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.