[ ARKIV DOCS ]

Everything you need to build with Arkiv. From quick start guides to comprehensive API documentation.

Last updated: February 2026 · SDK v0.6.0

📚

API Reference

Arkiv JSON-RPC API documentation

JSON API

Arkiv exposes a JSON-RPC API over HTTP. This page documents the Arkiv-specific methods currently available on the public RPC endpoint.

Endpoint

FieldValue
Chain ID60138453025
HTTP RPChttps://kaolin.hoodi.arkiv.network/rpc
WebSocket RPCwss://kaolin.hoodi.arkiv.network/rpc/ws
Explorerexplorer.kaolin.hoodi.arkiv.network
Faucetkaolin.hoodi.arkiv.network/faucet

For every method on this page, send an HTTP POST request with content-type: application/json.

JSON-RPC Request Format

All Arkiv methods use the standard JSON-RPC 2.0 envelope.

jsonExample
1{
2	"jsonrpc": "2.0",
3	"id": 1,
4	"method": "arkiv_query",
5	"params": []
6}

Minimal curl template:

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":1,
6		"method":"arkiv_getEntityCount",
7		"params":[]
8	}'

Method List

MethodPurpose
arkiv_queryQuery Arkiv entities from the bitmap-backed SQLite store
arkiv_getEntityCountReturn the total number of entities currently stored
arkiv_getNumberOfUsedSlotsReturn the number of used storage-accounting slots
arkiv_getBlockTimingReturn timing information for the current head block

arkiv_query

Runs an Arkiv entity query against the bitmap-backed SQLite store and returns matching entities.

Parameters

PositionTypeRequiredDescription
0stringYesQuery expression
1object or nullNoQuery options

Behavior:

  • If the options object is omitted or null, the server uses an empty options object.
  • If options.atBlock is omitted, the query runs against the current chain head.
  • resultsPerPage is capped at 200.
  • The response may include a cursor when more results are available.

Query Syntax

Supported operators:

  • Logical && and ||
  • Negation !
  • Comparisons =, !=, <, >, <=, >=
  • Glob matching ~ and negated glob matching !~

Supported synthetic attributes:

  • $owner
  • $creator
  • $key
  • $expiration
  • $createdAtBlock
  • $sequence
  • $txIndex
  • $opIndex
  • $all

Options

FieldTypeDescription
atBlockhex quantity stringBlock to query against, for example 0x1bc
includeDataobjectControls which entity fields are returned
resultsPerPagehex quantity stringPage size, capped at 200
cursorstringCursor returned by a previous arkiv_query response

Default includeData values when omitted:

FieldDefault
keytrue
contentTypetrue
payloadtrue
creatortrue
ownertrue
attributestrue
expirationtrue

Example Request

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":1,
6		"method":"arkiv_query",
7		"params":[
8			"type = \"nft\" && status = \"active\"",
9			{
10				"atBlock":"0x1bc",
11				"resultsPerPage":"0x2",
12				"includeData":{
13					"key":true,
14					"attributes":true,
15					"syntheticAttributes":false,
16					"payload":true,
17					"contentType":true,
18					"expiration":true,
19					"creator":true,
20					"owner":true,
21					"createdAtBlock":true,
22					"lastModifiedAtBlock":true,
23					"transactionIndexInBlock":false,
24					"operationIndexInTransaction":false
25				}
26			}
27		]
28	}'

Example Response

jsonExample
1{
2	"jsonrpc": "2.0",
3	"id": 1,
4	"result": {
5		"data": [
6			{
7				"key": "0x7e3b6d5e9dc0d8d6f7d4c0b9c5a7f2d58d8f4c2df4e2f3b7f4f8b7f8a1c2d3e4",
8				"value": "0x7b226e616d65223a2241726b69762047656d202331222c2274797065223a226e6674222c22737461747573223a22616374697665227d",
9				"contentType": "application/json",
10				"expiresAt": 1200000,
11				"creator": "0x1111111111111111111111111111111111111111",
12				"owner": "0x2222222222222222222222222222222222222222",
13				"createdAtBlock": 401,
14				"lastModifiedAtBlock": 443,
15				"stringAttributes": [
16					{ "key": "status", "value": "active" },
17					{ "key": "type", "value": "nft" }
18				],
19				"numericAttributes": [
20					{ "key": "rarity", "value": 5 }
21				]
22			},
23			{
24				"key": "0x1c0d29f4024f0f463ef2e6e0f05434052ed7fa5fb9f7b0c0e2aa9308d0b5a12c",
25				"value": "0x7b226e616d65223a2241726b69762047656d202332222c2274797065223a226e6674222c22737461747573223a22616374697665227d",
26				"contentType": "application/json",
27				"expiresAt": 1200042,
28				"creator": "0x1111111111111111111111111111111111111111",
29				"owner": "0x3333333333333333333333333333333333333333",
30				"createdAtBlock": 417,
31				"lastModifiedAtBlock": 417,
32				"stringAttributes": [
33					{ "key": "status", "value": "active" },
34					{ "key": "type", "value": "nft" }
35				],
36				"numericAttributes": [
37					{ "key": "rarity", "value": 9 }
38				]
39			}
40		],
41		"blockNumber": "0x1bc",
42		"cursor": "0x2a"
43	}
44}

Response Notes

  • blockNumber is a hex quantity string.
  • data is an array of entity objects.
  • cursor is omitted when there are no more pages.
  • value is hex-encoded bytes.

Pagination

Use the returned cursor in the next request to continue from the previous page.

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":2,
6		"method":"arkiv_query",
7		"params":[
8			"type = \"nft\" && status = \"active\"",
9			{
10				"atBlock":"0x1bc",
11				"resultsPerPage":"0x2",
12				"cursor":"0x2a"
13			}
14		]
15	}'

Query Examples

By owner:

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":10,
6		"method":"arkiv_query",
7		"params":[
8			"$owner = \"0x2222222222222222222222222222222222222222\"",
9			null
10		]
11	}'

By creator or owner:

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":11,
6		"method":"arkiv_query",
7		"params":[
8			"$owner = \"0x2222222222222222222222222222222222222222\" || $creator = \"0x2222222222222222222222222222222222222222\"",
9			null
10		]
11	}'

By numeric range:

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":12,
6		"method":"arkiv_query",
7		"params":[
8			"price >= 100 && price <= 1000",
9			null
10		]
11	}'

By glob match:

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":13,
6		"method":"arkiv_query",
7		"params":[
8			"name ~ \"test*\" && !(status = \"deleted\")",
9			null
10		]
11	}'

Fetch only metadata and omit payload bytes:

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":14,
6		"method":"arkiv_query",
7		"params":[
8			"$all",
9			{
10				"resultsPerPage":"0xa",
11				"includeData":{
12					"key":true,
13					"attributes":true,
14					"syntheticAttributes":true,
15					"payload":false,
16					"contentType":true,
17					"expiration":true,
18					"creator":true,
19					"owner":true,
20					"createdAtBlock":true,
21					"lastModifiedAtBlock":true,
22					"transactionIndexInBlock":true,
23					"operationIndexInTransaction":true
24				}
25			}
26		]
27	}'

Decode a JSON payload from the first returned entity:

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-s \
4	-d '{
5		"jsonrpc":"2.0",
6		"id":15,
7		"method":"arkiv_query",
8		"params":["$all",{"resultsPerPage":"0x1"}]
9	}' \
10	| jq -r '.result.data[0].value' \
11	| sed 's/^0x//' \
12	| xxd -r -p

arkiv_getEntityCount

Returns the total number of entities currently stored in Arkiv.

Parameters

None.

Example Request

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":3,
6		"method":"arkiv_getEntityCount",
7		"params":[]
8	}'

Example Response

jsonExample
1{
2	"jsonrpc": "2.0",
3	"id": 3,
4	"result": 18427
5}

Notes

  • result is a normal JSON number, not a hex string.

arkiv_getNumberOfUsedSlots

Reads current chain state and returns the number of used storage-accounting slots.

Parameters

None.

Example Request

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":4,
6		"method":"arkiv_getNumberOfUsedSlots",
7		"params":[]
8	}'

Example Response

jsonExample
1{
2	"jsonrpc": "2.0",
3	"id": 4,
4	"result": "0x48b"
5}

Notes

  • result is a hex quantity string

arkiv_getBlockTiming

Returns timing information for the current head block.

Response Fields

FieldDescription
current_blockCurrent block number
current_block_timeCurrent block timestamp as Unix seconds
durationDifference in seconds between the current block and previous block

Example Request

bashExample
1curl https://kaolin.hoodi.arkiv.network/rpc \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":5,
6		"method":"arkiv_getBlockTiming",
7		"params":[]
8	}'

Example Response

jsonExample
1{
2	"jsonrpc": "2.0",
3	"id": 5,
4	"result": {
5		"current_block": 582143,
6		"current_block_time": 1742721127,
7		"duration": 2
8	}
9}

Notes

  • All three response fields are plain JSON numbers.
  • current_block_time is a Unix timestamp in seconds.
  • duration is the gap between the current block timestamp and the previous block timestamp.

Entity Mutations

All entity mutations are sent as standard Ethereum transactions to a special Arkiv address. The transaction data field carries an encoded payload that describes one or more operations. Multiple operations of different types can be batched into a single transaction.

ConstantValue
Arkiv Address0x00000000000000000000000000000061726b6976
Block Time2 seconds
Transaction Value0 (no ETH transferred)

Payload Structure

The transaction data is a 5-element array where each element corresponds to an operation type:

[
  creates,            // index 0
  updates,            // index 1
  deletes,            // index 2
  extensions,         // index 3
  ownershipChanges    // index 4
]

Each element is an array of operations of that type. To perform a single create, place one entry in creates and leave the rest empty.

Operation Formats

Create (index 0)

Each create entry is a 5-element array:

IndexFieldTypeDescription
0expiresInBlocksnumberceil(seconds / 2)
1contentTypestringMIME type
2payloadstringEntity content (e.g. JSON string)
3stringAttributes[[key, value], ...]String-typed attributes
4numberAttributes[[key, value], ...]Number-typed attributes

Update (index 1)

Each update entry is a 6-element array:

IndexFieldTypeDescription
0entityKeyhex string0x-prefixed entity key
1contentTypestringMIME type
2expiresInBlocksnumberceil(seconds / 2)
3payloadstringEntity content
4stringAttributes[[key, value], ...]String-typed attributes
5numberAttributes[[key, value], ...]Number-typed attributes

Note: The field ordering differs from Create. In Update, entityKey comes first, and contentType and expiresInBlocks are swapped.

Delete (index 2)

Each delete entry is just the entity key as a 0x-prefixed hex string.

Extend (index 3)

Each extend entry is a 2-element array:

IndexFieldTypeDescription
0entityKeyhex string0x-prefixed entity key
1expiresInBlocksnumberceil(additionalSeconds / 2)

Change Ownership (index 4)

Each ownership change entry is a 2-element array:

IndexFieldTypeDescription
0entityKeyhex string0x-prefixed entity key
1newOwnerhex string0x-prefixed address of the new owner

Attributes

Attributes are key-value metadata attached to entities, split into two arrays by value type:

  • String attributes[["category", "documentation"], ["version", "1.0"]]
  • Number attributes[["priority", 5], ["count", 100]]

A number attribute with value 0 is encoded as an empty byte string (0x), which is distinct from the hex encoding of 0 as a number (0x00).

Encoding Pipeline

The payload goes through four stages before it can be included in a transaction:

  1. Human-readable JSON — Start with a JSON array matching the 5-element structure above.
  2. Hex-encode all leaf values — Convert every leaf to a 0x-prefixed hex string. Strings become hex-encoded UTF-8, numbers become hex-encoded integers, and 0 becomes "0x".
  3. RLP-encode — The nested hex structure is RLP-encoded into a flat byte sequence.
  4. Brotli-compress — The RLP bytes are compressed with Brotli. The output is hex-encoded with a 0x prefix to form the final transaction data field.

Example: Creating an Entity

Goal: Create an entity with payload {"message": "Hello from Arkiv"}, content type application/json, 30-day expiration, two string attributes and two number attributes.

Step 1 — Build the JSON payload

jsonExample
1[
2	[
3		[
4			1296000,
5			"application/json",
6			"{\"message\":\"Hello from Arkiv\"}",
7			[["category", "example"], ["version", "1.0"]],
8			[["priority", 0], ["count", 100]]
9		]
10	],
11	[],
12	[],
13	[],
14	[]
15]

Step 2 — Hex-encode leaf values

jsonExample
1[
2  [
3    [
4      "0x13c680",
5      "0x6170706c69636174696f6e2f6a736f6e",
6      "0x7b226d657373616765223a2248656c6c6f2066726f6d2041726b6976227d",
7      [
8        ["0x63617465676f7279", "0x6578616d706c65"],
9        ["0x76657273696f6e", "0x312e30"]
10      ],
11      [
12        ["0x7072696f72697479", "0x"],
13        ["0x636f756e74", "0x64"]
14      ]
15    ]
16  ],
17  [],
18  [],
19  [],
20  []
21]

Step 3 — RLP-encode

Using the @ethereumjs/rlp CLI:

bashExample
1rlp encode '<hex-encoded-json>'
2# 0xf870f86af8688313c680906170706c69636174696f6e2f6a736f6e9e7b226d657373616765223a2248656c6c6f2066726f6d2041726b6976227ddfd18863617465676f7279876578616d706c65cc8776657273696f6e83312e30d3ca887072696f7269747980c785636f756e7464c0c0c0c0

This outputs a single 0x-prefixed hex string representing the RLP-encoded bytes.

Step 4 — Brotli-compress and hex-encode

bashExample
1echo "$RLP_HEX" | sed 's/^0x//' | xxd -r -p | brotli | xxd -p | tr -d '\n' | sed 's/^/0x/'
2# 0x1f7100681ca71b7a4cd5f86a48e36f7cf77f53e083ddf35f699645d0a631a650c467ad95f8836b4e3960cfc3a825e1e707c0ab1f36e0c892a721c13f9b0cdac412006f991280051ec4a7af43b0d37dc87ac70c07193a86f66e38cafedbe8c4e5dcad1d091111

This final hex string is the value to put in the transaction data field.

Signing and Sending

The encoded TX_DATA must be wrapped in a signed Ethereum transaction before submission. Use any EIP-1559 or legacy signing method — the result is a 0x-prefixed signed transaction blob.

Transaction fields:

FieldValue
to0x00000000000000000000000000000061726b6976
value0
datathe Brotli-compressed hex payload

Broadcasting via curl

bashExample
1RPC_URL="https://kaolin.hoodi.arkiv.network/rpc"
2
3curl -s "$RPC_URL" \
4	-H "content-type: application/json" \
5	-d '{
6		"jsonrpc":"2.0",
7		"id":1,
8		"method":"eth_sendRawTransaction",
9		"params":["'"$SIGNED_TX"'"],
10	}'

A successful response returns the transaction hash:

jsonExample
1{
2	"jsonrpc": "2.0",
3	"id": 1,
4	"result": "0xabc123...transactionHash"
5}

Polling for the receipt

bashExample
1curl -s "$RPC_URL" \
2	-H "content-type: application/json" \
3	-d '{
4		"jsonrpc":"2.0",
5		"id":1,
6		"method":"eth_getTransactionReceipt",
7		"params":["'"$TX_HASH"'"],
8	}'

The receipt's logs contain the entity keys for each operation. See the log ordering note below.

Batch Operations

All five operation types can be combined in a single transaction. The payload array simply has entries in multiple positions:

jsonExample
1[
2	[[1296000, "application/json", "{\"name\":\"Entity A\"}", [["type","alpha"]], []]],
3	[["0xabc...", "application/json", 2592000, "{\"name\":\"Updated B\"}", [["type","beta"]], []]],
4	["0xdef..."],
5	[["0x123...", 3888000]],
6	[["0x456...", "0x789..."]]
7]

Transaction Receipt Log Order

When parsing logs from a batch transaction, events are emitted in this order:

  1. Creates
  2. Deletes
  3. Updates
  4. Extensions
  5. Ownership Changes

Note: This differs from the payload array order. Deletes come before Updates in the logs, even though Updates come before Deletes in the payload.