Skip to content

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.

The Omnigraph lives at:

POST {ENSNODE_URL}/api/omnigraph
Content-Type: application/json
{ "query": "...", "variables": { ... } }

It returns { "data": ..., "errors": [...] } per the standard GraphQL response shape.

A minimum-viable hello world over curl:

Terminal window
curl -sS -X POST \
-H 'Content-Type: application/json' \
-d '{"query":"{ domain(by: { name: \"eth\" }) { name owner { address } } }"}' \
https://api.alpha.ensnode.io/api/omnigraph

The rest of this guide builds the same thing in TypeScript using fetch, so you have something to extend.

If you already have one, skip ahead to Write the query.

Terminal window
mkdir my-ens-script && cd my-ens-script
npm init -y
mkdir src

Install tsx so you can run TypeScript directly:

Terminal window
npm install -D tsx typescript @types/node

Add a start script to package.json:

package.json
{
"type": "module",
"scripts": {
"start": "tsx src/index.ts"
}
}

Create src/index.ts. The whole script is a single fetch against /api/omnigraph.

src/index.ts
// you may use a NameHash Hosted ENSNode instance
// learn more at https://ensnode.io/docs/integrate/hosted-instances
const 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:

  • InterpretedName is a scalar. From the wire’s perspective it’s just a string — the server validates the format. Pass "eth" as a plain string in variables.
  • subdomains is a Relay Connection. Cursor through with first, after, pageInfo { hasNextPage endCursor } — same shape as any Relay-style API.
  • Hand-written types. We’re maintaining interface Domain and interface QueryResult ourselves here. If you want these generated and kept in sync with your queries automatically, use enssdk.
Terminal window
ENSNODE_URL=https://api.alpha.ensnode.io npm start

You should see the eth Domain, its owner, and the first 20 of its subdomains.

  • Want typed queries with editor autocomplete and a real GraphQL client? Use enssdk — same API, with gql.tada types and an EnsNodeClient.
  • Building a React app? Use enskit — same graphql(...) helper plus useOmnigraphQuery and 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.