Skip to main content

API Overview

The XNDR Network REST API returns indexed ALX Blocks, source-scoped observations, and relationship data as JSON.

The REST API is available under /api/v1 at the production and local base URLs:

EnvironmentValue
Productionhttps://xndr.network/api/v1
Local developmenthttp://127.0.0.1:4173/api/v1

Available Interfaces​

The REST API is read-only JSON under /api/v1. Every route is GET. Other methods return 405 method_not_allowed with an Allow: GET header. GET /api/v1/openapi.yaml serves an OpenAPI 3.1 description of every route, parameter, and response schema (XNDR Index Read API, version 1.0.0). The document declares /api/v1 as a relative server path, so a generated client needs the deployment origin. The REST API sends no CORS headers, so a browser on another origin cannot call the REST API.

Routes​

Paths are relative to the base URL. Every hash parameter is a full 0x-prefixed, 64-character hexadecimal Block or content hash. The API assigns no numeric Block id.

RoutePurposeKey Parameters
GET /scopesCurrent named scopes and source membershipNone
GET /openapi.yamlOpenAPI 3.1 description of the XNDR Network REST APINone
GET /statusSource-scoped counts and current revisionscope, scopeVersion, atRevision
GET /search?q={hash}Exact Block or content identity lookupq (required), scope parameters, limit, cursor
GET /blocks/{blockHash}Block and observation contextScope parameters
GET /content/{contentHash}Blocks sharing an exact content identityScope parameters, limit, cursor
GET /blocks/{blockHash}/parentsDeclared parents and scoped availabilityScope parameters, limit, cursor
GET /blocks/{blockHash}/childrenSource-scoped indexed childrenScope parameters, limit, cursor
GET /blocks/{blockHash}/verificationIndependent verification facetsScope parameters
GET /blocks/{blockHash}/neighborhoodBounded parent and child neighborhooddirection, depth, maxNodes, contentType, status, source, scope parameters, limit, cursor

GET /scopes returns { data } only; every other JSON route returns the Response Envelope.

Query Capabilities​

The shell commands use BASE for the base URL and BLOCK_HASH for the Block under review.

BASE="https://xndr.network/api/v1"
BLOCK_HASH="0x..."

GET /search?q={hash} resolves one exact identity. A Block hash fills data.block with the matching Block view; a content hash fills data.contentMatches with one page of indexed Blocks sharing the content hash, and data.query echoes the normalized hash. Follow page.nextCursor for further matches. Partial hashes, metadata terms, and Block numbers are rejected with 400 invalid_request.

curl "$BASE/search?q=$BLOCK_HASH"

Block Context​

GET /blocks/{blockHash} returns the Block view.

FieldMeaning
blockCanonical ALX content: blockHash, contentHash, parentHashes, content, and protocolVersion
indexedChildrenOptional inline child hashes; use the paginated children route to retrieve child hashes when this field is absent
indexedChildCountHow many Blocks in the selected scope declare the path Block as a parent
observedSourcesThe sources that reported the path Block, each with an id and a type
firstObservedAtWhen the selected scope first observed the Block, not a creation timestamp
lastObservedAtThe most recent observation within the selected scope
verificationThe facets inline, identical to the dedicated verification route

A Block that is not indexed in the selected scope returns 404 block_not_found. The 404 block_not_found response is limited to the selected scope and is not proof that the Block does not exist.

curl "$BASE/blocks/$BLOCK_HASH"

Content Matches​

GET /content/{contentHash} returns one page of indexed Blocks in the selected scope whose contentHash equals the path value. Follow page.nextCursor for further matches.

Parent Lookup​

GET /blocks/{blockHash}/parents returns a page of the Block's declared parentHashes as { blockHash, available } items. Across all pages, the hashes are exactly those the Block declares; available reports whether the selected scope has indexed each declared parent.

Children Lookup​

GET /blocks/{blockHash}/children lists Blocks observed in the selected scope that declare the path Block as a parent, as { blockHash, available } items. Children are reverse-index observations, not protocol facts, so an absent child is unobserved, never disproven.

Verification Facets​

GET /blocks/{blockHash}/verification returns the validation record for the Block in the selected scope: blockHash, the verification facets, missingParents, checkedAt, and validatorVersion.

FacetValue
identityvalid, invalid, or not-checked
schemavalid, invalid, or not-checked
lineagevalid, invalid, or not-checked
availabilityavailable, unavailable, or unknown

missingParents lists declared parents the scope has not indexed, and lineage becomes not-checked when declared parents fall outside the scope. not-checked is an XNDR Network index value, not an ALX Protocol canonical Graph status.

A 404 block_not_found from GET /blocks/{blockHash}/verification means no validation record exists yet; fall back to the inline verification facets on the Block view.

Bounded Neighborhood​

GET /blocks/{blockHash}/neighborhood expands parents, children, or both from a root Block within explicit budgets. The neighborhood is an application view, not an ALX Protocol Graph verification or Attribution Trace.

ParameterValuesDefault
directionparents, children, bothboth
depthInteger from 0 through 81
maxNodesInteger from 1 through 100001000
contentTypeKeep nodes whose application-declared content.type matchesUnfiltered
statusvalid, invalid, incomplete, not_checkedUnfiltered
sourceKeep nodes observed by the supplied source id within the scopeUnfiltered

The status filter spells the final listed value not_checked, with an underscore, while the verification facets report not-checked. The REST API serves not_checked on the neighborhood status parameter and not-checked on the verification facets.

The response's data member returns the root, applied traversal parameters, content types, nodes, and edges. Each edge pairs a child Block with the parent hash that child declares. The root Block is always returned, even when a filter excludes the root Block. maxNodes bounds traversal and limit bounds the returned page; data.budgetTruncated reports when traversal stopped at maxNodes.

See the ALX Block Model for relationship semantics.

Scope Parameters​

Every query route accepts three snapshot parameters. A named scope is a versioned set of sources; the index revision advances once for every accepted observation and every quarantined candidate.

ParameterFormatBehavior
scope1 through 128 letters, digits, dots, colons, underscores, or hyphens, starting with a letter or digit; default defaultSelects the named scope. An unknown id returns 404 scope_not_found.
scopeVersionPositive integer stringPins one immutable scope version. Publishing a new version leaves existing versions queryable.
atRevisionNon-negative integer stringPins the index revision. A revision XNDR Network cannot serve returns 409 revision_unavailable.

GET /scopes lists the current scopes, each with id, name, version, sources, completeness, latestRevision, and indexedThrough. Source types are local, retrieval, partner, and network.

GET /status returns counters for the selected snapshot.

CounterMeaning
blockCountIndexed Blocks
edgeCountResolved parent-child edges
observationCountSource observations recorded
validationCountValidation records written
quarantineCountInputs held back, and excluded from Block and graph queries

Authentication​

Authentication is optional and selects the rate tier. Send an issued key in the X-XNDR-API-Key header, or as Authorization: Bearer followed by a space and the key.

A key is 16 through 512 characters. A malformed, unknown, or disabled key returns 401 invalid_api_key; the request is rejected, not downgraded to the anonymous tier.

An XNDR Network operator issues keys. A key is displayed once at creation. The API provides no self-serve key endpoint and no signup route. Request a key from support@alxlabs.io.

Rate Limits​

TierBudgetCounted By
Anonymous60 requests per minuteClient IP address
Keyed600 requests per minuteIssued API key

Counting uses a fixed 60-second window. Exceeding the budget returns 429 rate_limited with a Retry-After header in seconds; wait at least the Retry-After interval before retrying. JSON responses carry Cache-Control: no-store, so cache on your side deliberately and key cached entries by scope and revision.

Pagination and Snapshots​

List routes accept limit (1 through 1000, default 100) and cursor. Each response reports page.limit, page.nextCursor, and page.truncated; a null nextCursor means no further page follows.

Cursors are signed and bound to:

  • The query kind.
  • The normalized query.
  • The selected scope.
  • The result offset.
  • The exact index revision.

A cursor is rejected with 400 invalid_request after the index revision changes or when reused for another query, so pin atRevision to the scope.indexRevision of the first page before continuing.

curl "$BASE/blocks/$BLOCK_HASH/children?atRevision=$REVISION&limit=5"

Repeat the request with cursor set to the returned page.nextCursor and the same atRevision until nextCursor is null. A further page sets page.truncated to true. Separately, a bounded-neighborhood traversal that reaches maxNodes sets data.budgetTruncated to true and scope.completeness to truncated, even when page.truncated is false.

Response Envelope​

Every query route returns data, scope, and page. data holds the route result, scope reports the snapshot that produced the route result, and page reports limit, nextCursor, and truncated.

FieldMeaning
idThe scope the request was served from
nameDisplay name of the served scope
versionThe immutable scope version
sourcesThe configured sources in the scope, each with an id and a type
indexRevisionThe revision the response was served from
indexedThroughLatest observation timestamp in the served scope and revision, or null when the scope has no observations; not a measure of source catch-up or Block creation time
completenessCompleteness reported for the served scope
CompletenessMeaning
complete-within-scopeEvery configured source is caught up to the source's advertised boundary and the query stayed within budget
partialAt least one source has succeeded, but not every source is caught up
truncatedPagination or a traversal budget cut the result short; inspect page.truncated, page.nextCursor, and, for neighborhoods, data.budgetTruncated
unknownThe service has not derived completeness for the scope

complete-within-scope describes the configured source high-water marks and the query budget. complete-within-scope never claims coverage of every ALX Block. Responses may gain additive fields within /api/v1; removals, renamed meanings, and incompatible type changes require a new major API prefix.

Error Handling​

Errors are JSON with one error object carrying a code and a message: { "error": { "code", "message" } }.

StatusCodeMeaning
400invalid_requestA parameter, hash, or cursor failed validation
401invalid_api_keyThe supplied key is malformed, unknown, or disabled
404block_not_foundThe Block is not indexed in the selected scope
404scope_not_foundThe scope id is not configured
404route_not_foundThe path is not an API route
405method_not_allowedThe method is not GET
409revision_unavailableThe requested atRevision cannot be served
414request_target_too_longThe request target exceeds 8192 bytes
429rate_limitedThe per-minute budget is exhausted; honor Retry-After
500internal_errorThe request could not be completed; no server internals are exposed

Error codes are stable within /api/v1. Messages may be refined without changing the code, so branch on error.code and the HTTP status, never on message text.

Build for Source-Scoped Data​

Scope, parent availability, unobserved children, pagination, and completeness are defined in the route sections above. Review the Indexing Model before building a workflow that depends on relationship completeness or availability. Integrate with XNDR Network applies those rules to search, lineage, and verification.