| title | Delegated Routing V1 HTTP API | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| description | Delegated routing is a mechanism for IPFS implementations to use for offloading content routing, peer routing and naming to another process/server. This specification describes an HTTP API for delegated routing of content, peers, and IPNS. | |||||||||||||||||||||||||||||||||||||||||||||||||
| date | 2025-11-20 | |||||||||||||||||||||||||||||||||||||||||||||||||
| maturity | reliable | |||||||||||||||||||||||||||||||||||||||||||||||||
| editors |
|
|||||||||||||||||||||||||||||||||||||||||||||||||
| former_editors |
|
|||||||||||||||||||||||||||||||||||||||||||||||||
| thanks |
|
|||||||||||||||||||||||||||||||||||||||||||||||||
| xref |
|
|||||||||||||||||||||||||||||||||||||||||||||||||
| order | 3 | |||||||||||||||||||||||||||||||||||||||||||||||||
| tags |
|
Delegated routing is a mechanism for IPFS implementations to use for offloading content routing, peer routing, and naming to another process/server. This specification describes a vendor-agnostic HTTP API for delegated content routing.
The Routing HTTP API uses the application/json content type by default. For :ref[IPNS Names], the verifiable application/vnd.ipfs.ipns-record content type is used.
As such, human-readable encodings of types are preferred. This specification may be updated in the future with a compact application/cbor encoding, in which case compact encodings of the various types would be used.
- CIDs are always string-encoded using a multibase-encoded CIDv1.
- Multiaddrs are string-encoded according to the human-readable multiaddr specification.
- Peer IDs are string-encoded according PeerID string representation specification: either a Multihash in Base58btc, or a CIDv1 with libp2p-key (
0x72) codec in Base36 or Base32. - Multibase bytes are string-encoded according to the Multibase spec, and SHOULD use base64.
- Timestamps are Unix millisecond epoch timestamps.
Until required for business logic, servers should treat these types as opaque strings, and should preserve unknown JSON fields.
This API uses a standard version prefix in the path, such as /v1/.... If a backwards-incompatible change must be made, then the version number should be increased.
cidis the CID to fetch provider records for (preferably normalized to a CIDv1 in Base32, to maximize HTTP cache hits).
Optional ?filter-addrs to apply Network Address Filtering from IPIP-484.
?filter-addrs=<comma-separated-list>optional parameter that indicates which network transports to return by filtering the multiaddrs in theAddrsfield of the Peer schema.- The value of the
filter-addrsparameter is a comma-separated (,or%2C) list of network transport protocol name strings as defined in the multiaddr protocol registry, e.g.?filter-addrs=tls,webrtc-direct,webtransport. unknowncan be be passed to include providers whose multiaddrs are unknown, e.g.?filter-addrs=unknown. This allows for not removing providers whose multiaddrs are unknown at the time of filtering (e.g. keeping DHT results that require additional peer lookup).- Multiaddrs are filtered by checking if the protocol name appears in any of the multiaddrs (logical OR).
- Negative filtering is done by prefixing the protocol name with
!, e.g. to skip IPv6 and QUIC addrs:?filter-addrs=!ip6,!quic-v1. Note that negative filtering is done by checking if the protocol name does not appear in any of the multiaddrs (logical AND). - If no parameter is passed, the default behavior is to return the original list of addresses unchanged.
- If only negative filters are provided, addresses not passing any of the negative filters are included.
- If positive filters are provided, only addresses passing at least one positive filter (and no negative filters) are included.
- If both positive and negative filters are provided, the address must pass all negative filters and at least one positive filter to be included.
- If there are no multiaddrs that match the passed transports, the provider is omitted from the response.
- Filtering is case-insensitive.
Optional ?filter-protocols to apply IPFS Protocol Filtering from IPIP-484.
- The
filter-protocolsparameter is a comma-separated (,or%2C) list of transfer protocol names, e.g.?filter-protocols=unknown,transport-bitswap,transport-ipfs-gateway-http. - Transfer protocols names should be treated as opaque strings and have a max length of 63 characters. A non-exhaustive list of transfer protocols are defined per convention in the multicodec registry.
- Implementations MUST preserve all transfer protocol names when returning a positive result that matches one or more of them.
- A special
unknownname can be be passed to include providers whose transfer protocol list is empty (unknown), e.g.?filter-protocols=unknown. This allows for including providers returned from the DHT that do not contain explicit transfer protocol information. - Providers are filtered by checking if the transfer protocol name appears in the
Protocolsarray (logical OR). - If the provider doesn't match any of the passed transfer protocols, the provider is omitted from the response.
- If a provider passes the filter, it is returned unchanged, i.e. the full set of protocols is returned including protocols that not included in the filter. (note that this is different from
filter-addrswhere only the multiaddrs that pass the filter are returned) - Filtering is case-insensitive.
- If no parameter is passed, the default behavior is to not filter by transfer protocol.
200(OK): the response body contains 0 or more records.404(Not Found): must be returned if no matching records are found.422(Unprocessable Entity): request does not conform to schema or semantic constraints.
Content-Type: the content type of this response, which MUST beapplication/jsonorapplication/x-ndjson(see streaming).Last-Modified: an HTTP-date timestamp (RFC9110, Section 5.6.7) of the resolution, allowing HTTP proxies and CDNs to support inexpensive update checks viaIf-Modified-SinceCache-Control: public, max-age={ttl}, public, stale-while-revalidate={max-ttl}, stale-if-error={max-ttl}: meaningful cache TTL returned with the response.- The
max-ageSHOULD be shorter for responses whose resolution ended in no results (e.g. 15 seconds), and longer for responses that have results (e.g. 5 minutes). - Implementations SHOULD include
max-ttl, set to the maximum cache window of the underlying routing system. For example, if Amino DHT results are returned,stale-while-revalidateSHOULD be set to172800(48h, which at the time of writing this specification, is the provider record expiration window).
- The
Vary: Accept: allows intermediate caches to play nicely with the different possible content types.
{
"Providers": [
{
"Schema": "<schema>",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
...
},
...
]
}The application/json responses SHOULD be limited to 100 providers.
The client SHOULD be able to make a request with Accept: application/x-ndjson and get a stream with more results.
Each object in the Providers list is a record conforming to a schema, usually the Peer Schema.
peer-idis the Peer ID to fetch peer records for, represented as either a Multihash in Base58btc, or a CIDv1 with libp2p-key (0x72) codec (in Base36 or Base32).
Optional, same rules as filter-addrs providers request query parameter.
Optional, same rules as filter-protocols providers request query parameter.
200(OK): the response body contains the peer record.404(Not Found): must be returned if no matching records are found.422(Unprocessable Entity): request does not conform to schema or semantic constraints.
Content-Type: the content type of this response, which MUST beapplication/jsonorapplication/x-ndjson(see streaming).Last-Modified: an HTTP-date timestamp (RFC9110, Section 5.6.7) of the resolution, allowing HTTP proxies and CDNs to support inexpensive update checks viaIf-Modified-SinceCache-Control: public, max-age={ttl}, public, stale-while-revalidate={max-ttl}, stale-if-error={max-ttl}: meaningful cache TTL returned with the response.- When present,
ttlSHOULD be shorter for responses whose resolution ended in no results (e.g. 15 seconds), and longer for responses that have results (e.g. 5 minutes). - Implementations SHOULD include
max-ttl, set to the maximum cache window of the underlying routing system. For example, if Amino DHT results are returned,stale-while-revalidateSHOULD be set to172800(48h, which at the time of writing this specification, is the provider record expiration window).
- When present,
Vary: Accept: allows intermediate caches to play nicely with the different possible content types.
{
"Peers": [
{
"Schema": "<schema>",
"Protocols": ["<protocol-a>", "<protocol-b>", ...],
"ID": "bafz...",
"Addrs": ["/ip4/..."],
...
},
...
]
}The application/json responses SHOULD be limited to 100 peers.
The client SHOULD be able to make a request with Accept: application/x-ndjson and get a stream with more results.
Each object in the Peers list is a record conforming to the Peer Schema.
nameis the :ref[IPNS Name] to resolve, encoded as CIDv1.
200(OK): the response body contains the :ref[IPNS Record] for the given :ref[IPNS Name].404(Not Found): must be returned if no matching records are found.406(Not Acceptable): requested content type is missing or not supported. Error message returned in body should inform the user to retry withAccept: application/vnd.ipfs.ipns-record.
Etag: a globally unique opaque string used for HTTP caching. MUST be derived from the protobuf record returned in the body.Cache-Control: public, max-age={ttl}, public, stale-while-revalidate={sig-ttl}, stale-if-error={sig-ttl}: meaningful cache TTL returned with :ref[IPNS Record]- The
max-agevalue in seconds SHOULD match duration fromIpnsEntry.data[TTL], if present and bigger than0. Otherwise, implementation SHOULD default tomax-age=60. - Implementations SHOULD include
sig-ttl, set to the remaining number of seconds the returned IPNS Record is valid.
- The
Expires:: an HTTP-date timestamp (RFC9110, Section 5.6.7) when the validity of IPNS Record expires (ifValidityType=0, when signature expires)Last-Modified: an HTTP-date timestamp of when cacheable resolution occurred: allows HTTP proxies and CDNs to support inexpensive update checks viaIf-Modified-SinceVary: Accept: allows intermediate caches to play nicely with the different possible content types.
The response body contains a :ref[IPNS Record] serialized using the verifiable application/vnd.ipfs.ipns-record protobuf format.
nameis the :ref[IPNS Name] to publish, encoded as CIDv1.
The content body must be a application/vnd.ipfs.ipns-record serialized :ref[IPNS Record], with a valid signature matching the name path parameter.
200(OK): the provided :ref[IPNS Record] was published.400(Bad Request): the provided :ref[IPNS Record] or :ref[IPNS Name] are not valid.406(Not Acceptable): submitted content type is not supported. Error message returned in body should inform the user to retry withContent-Type: application/vnd.ipfs.ipns-record.
The DHT Routing API is OPTIONAL. Implementations that do not support DHT operations MAY return 404 (Not Found) or 501 (Not Implemented) as specified in Error Codes.
This optional endpoint allows light clients to lower the cost of DHT walks in browser contexts.
keyis a [CID] or Peer ID to find the closest peers to.- [CID] SHOULD be a CIDv1 in any encoding.
- Peer ID can be represented as a Multihash in Base58btc, or a CIDv1 with
libp2p-key(0x72) codec in Base36 or Base32. - Arbitrary multihash lookups can be performed by wrapping the multihash in a CIDv1 with
raw(0x55) codec. - Implementations SHOULD support both CID and Peer ID formats for maximum interoperability.
200(OK): the response body contains peer records.404(Not Found): must be returned if no matching records are found.422(Unprocessable Entity): request does not conform to schema or semantic constraints.501(Not Implemented): may be returned if DHT operations are not supported.
Content-Type: the content type of this response, which MUST beapplication/jsonorapplication/x-ndjson(see streaming).Last-Modified: an HTTP-date timestamp (RFC9110, Section 5.6.7) of the resolution, allowing HTTP proxies and CDNs to support inexpensive update checks viaIf-Modified-SinceCache-Control: public, max-age={ttl}, public, stale-while-revalidate={max-ttl}, stale-if-error={max-ttl}: meaningful cache TTL returned with the response.- When present,
ttlSHOULD be shorter for responses whose resolution ended in no results (e.g. 15 seconds), and longer for responses that have results (e.g. 5 minutes). - Implementations SHOULD include
max-ttl, set to the maximum cache window of the underlying routing system. For example, if Amino DHT results are returned,stale-while-revalidateSHOULD be set to172800(48h, which at the time of writing this specification, is the provider record expiration window).
- When present,
Vary: Accept: allows intermediate caches to play nicely with the different possible content types.
{
"Peers": [
{
"Schema": "<schema>",
"Protocols": ["<protocol-a>", "<protocol-b>", ...],
"ID": "bafz...",
"Addrs": ["/ip4/..."],
...
},
...
]
}The number of peer records in the response SHOULD be limited to the DHT bucket size (20 for Amino DHT).
Peers SHOULD be returned sorted by closeness to the key. For Kademlia-based DHT implementations (such as Amino DHT), this means sorting by XOR distance with the closest peers first.
The client SHOULD be able to make a request with Accept: application/x-ndjson and get a stream with results. Note that due to the XOR sorting requirement, the streamed response may be blocked until the DHT lookup completes and peers can be sorted before transmission.
Each object in the Peers list is a record conforming to the Peer Schema.
This API does not support pagination, but optional pagination can be added in a backwards-compatible spec update.
JSON-based endpoints support streaming requests made
with Accept: application/x-ndjson HTTP Header.
Steaming responses are formatted as Newline Delimited JSON (ndjson), with one result per line:
{"Schema": "<schema>", ...}
{"Schema": "<schema>", ...}
{"Schema": "<schema>", ...}
...:::note
Streaming is opt-in and backwards-compatible with clients and servers that do not support streaming:
- Requests without the
Accept: application/x-ndjsonheader MUST default to regular, non-streaming, JSON responses. - Legacy server MAY respond with non-streaming
application/jsonresponse even if the client requested streaming. It is up to the client to inspect theContent-Typeheader before parsing the response. - The server MUST NOT respond with streaming response if the client did not explicitly request so.
:::
400(Bad Request): must be returned if an unknown path is requested.429(Too Many Requests): may be returned along with optional Retry-After header to indicate to the caller that it is issuing requests too quickly.501(Not Implemented): must be returned if a method/path is not supported.
Browser interoperability requires implementations to support CORS.
JavaScript client running on a third-party Origin must be able to send HTTP request to the endpoints defined in this specification, and read the received values. This means HTTP server implementing this API must (1) support CORS preflight requests sent as HTTP OPTIONS, and (2) always respond with headers that remove CORS limits, allowing every site to query the API for results:
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, OPTIONS
This section contains a non-exhaustive list of known schemas that MAY be supported by clients and servers.
The peer schema represents an arbitrary peer.
{
"Schema": "peer",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
"Protocols": ["transport-bitswap", ...]
...
}ID: the Peer ID as Multihash in Base58btc or CIDv1 with libp2p-key codec.Addrs: an optional list of known multiaddrs for this peer.- If missing or empty, it means the router server is missing that information, and the client should use
IDto lookup updated peer information.
- If missing or empty, it means the router server is missing that information, and the client should use
Protocols: an optional list of protocols known to be supported by this peer.- If missing or empty, it means the router server is missing that information, and the client should use
IDandAddrsto lookup connect to the peer and use the libp2p identify protocol to learn about supported ones.
- If missing or empty, it means the router server is missing that information, and the client should use
:::note
To allow for protocol-specific fields and future-proofing, the parser MUST allow for unknown fields, and the clients MUST ignore unknown ones.
Below is an example on how one could include protocol-a and protocol-b
protocols that includes an additional fields protocol-a and protocol-b.
If the client knows the protocol, they are free to use the extra binary (base64) or JSON information contained in the additional field. If that is not the case, the field MUST be ignored.
{
"Schema": "peer",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
"Protocols": ["transport-bitswap", "protocol-a", "protocol-b", ...],
"protocol-a": "[base64-blob]",
"protocol-b": { "foo": "bar" }
}:::
Legacy schemas include ID and optional Addrs list just like
the peer schema does.
These schemas are deprecated and SHOULD be replaced with peer over time, but
MAY be returned by some legacy endpoints. In such case, a client MAY parse
them the same way as the peer schema.
A legacy schema used by some routers to indicate a peer supports retrieval over
the /ipfs/bitswap[/*] libp2p protocol.
{
"Protocol": "transport-bitswap",
"Schema": "bitswap",
"ID": "bafz...",
"Addrs": ["/ip4/..."]
}A legacy schema used by some routers to indicate a peer supports retrieval over the graphsync libp2p protocol.
{
"Protocol": "transport-graphsync-filecoinv1",
"Schema": "graphsync-filecoinv1",
"ID": "bafz...",
"Addrs": ["/ip4/..."],
"PieceCID": "<cid>",
"VerifiedDeal": true,
"FastRetrieval": true
}