[ 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
| Field | Value |
|---|---|
| Chain ID | 60138453025 |
| HTTP RPC | https://kaolin.hoodi.arkiv.network/rpc |
| WebSocket RPC | wss://kaolin.hoodi.arkiv.network/rpc/ws |
| Explorer | explorer.kaolin.hoodi.arkiv.network |
| Faucet | kaolin.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.
jsonExample1{ 2 "jsonrpc": "2.0", 3 "id": 1, 4 "method": "arkiv_query", 5 "params": [] 6}
Minimal curl template:
bashExample1curl 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
| Method | Purpose |
|---|---|
arkiv_query | Query Arkiv entities from the bitmap-backed SQLite store |
arkiv_getEntityCount | Return the total number of entities currently stored |
arkiv_getNumberOfUsedSlots | Return the number of used storage-accounting slots |
arkiv_getBlockTiming | Return 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
| Position | Type | Required | Description |
|---|---|---|---|
0 | string | Yes | Query expression |
1 | object or null | No | Query options |
Behavior:
- If the options object is omitted or
null, the server uses an empty options object. - If
options.atBlockis omitted, the query runs against the current chain head. resultsPerPageis capped at200.- The response may include a
cursorwhen 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
| Field | Type | Description |
|---|---|---|
atBlock | hex quantity string | Block to query against, for example 0x1bc |
includeData | object | Controls which entity fields are returned |
resultsPerPage | hex quantity string | Page size, capped at 200 |
cursor | string | Cursor returned by a previous arkiv_query response |
Default includeData values when omitted:
| Field | Default |
|---|---|
key | true |
contentType | true |
payload | true |
creator | true |
owner | true |
attributes | true |
expiration | true |
Example Request
bashExample1curl 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
jsonExample1{ 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
blockNumberis a hex quantity string.datais an array of entity objects.cursoris omitted when there are no more pages.valueis hex-encoded bytes.
Pagination
Use the returned cursor in the next request to continue from the previous page.
bashExample1curl 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:
bashExample1curl 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:
bashExample1curl 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:
bashExample1curl 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:
bashExample1curl 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:
bashExample1curl 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:
bashExample1curl 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
bashExample1curl 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
jsonExample1{ 2 "jsonrpc": "2.0", 3 "id": 3, 4 "result": 18427 5}
Notes
resultis 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
bashExample1curl 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
jsonExample1{ 2 "jsonrpc": "2.0", 3 "id": 4, 4 "result": "0x48b" 5}
Notes
resultis a hex quantity string
arkiv_getBlockTiming
Returns timing information for the current head block.
Response Fields
| Field | Description |
|---|---|
current_block | Current block number |
current_block_time | Current block timestamp as Unix seconds |
duration | Difference in seconds between the current block and previous block |
Example Request
bashExample1curl 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
jsonExample1{ 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_timeis a Unix timestamp in seconds.durationis 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.
| Constant | Value |
|---|---|
| Arkiv Address | 0x00000000000000000000000000000061726b6976 |
| Block Time | 2 seconds |
| Transaction Value | 0 (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:
| Index | Field | Type | Description |
|---|---|---|---|
| 0 | expiresInBlocks | number | ceil(seconds / 2) |
| 1 | contentType | string | MIME type |
| 2 | payload | string | Entity content (e.g. JSON string) |
| 3 | stringAttributes | [[key, value], ...] | String-typed attributes |
| 4 | numberAttributes | [[key, value], ...] | Number-typed attributes |
Update (index 1)
Each update entry is a 6-element array:
| Index | Field | Type | Description |
|---|---|---|---|
| 0 | entityKey | hex string | 0x-prefixed entity key |
| 1 | contentType | string | MIME type |
| 2 | expiresInBlocks | number | ceil(seconds / 2) |
| 3 | payload | string | Entity content |
| 4 | stringAttributes | [[key, value], ...] | String-typed attributes |
| 5 | numberAttributes | [[key, value], ...] | Number-typed attributes |
Note: The field ordering differs from Create. In Update,
entityKeycomes first, andcontentTypeandexpiresInBlocksare 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:
| Index | Field | Type | Description |
|---|---|---|---|
| 0 | entityKey | hex string | 0x-prefixed entity key |
| 1 | expiresInBlocks | number | ceil(additionalSeconds / 2) |
Change Ownership (index 4)
Each ownership change entry is a 2-element array:
| Index | Field | Type | Description |
|---|---|---|---|
| 0 | entityKey | hex string | 0x-prefixed entity key |
| 1 | newOwner | hex string | 0x-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:
- Human-readable JSON — Start with a JSON array matching the 5-element structure above.
- 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, and0becomes"0x". - RLP-encode — The nested hex structure is RLP-encoded into a flat byte sequence.
- Brotli-compress — The RLP bytes are compressed with Brotli. The output is hex-encoded with a
0xprefix to form the final transactiondatafield.
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
jsonExample1[ 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
jsonExample1[ 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:
bashExample1rlp 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
bashExample1echo "$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:
| Field | Value |
|---|---|
to | 0x00000000000000000000000000000061726b6976 |
value | 0 |
data | the Brotli-compressed hex payload |
Broadcasting via curl
bashExample1RPC_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:
jsonExample1{ 2 "jsonrpc": "2.0", 3 "id": 1, 4 "result": "0xabc123...transactionHash" 5}
Polling for the receipt
bashExample1curl -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:
jsonExample1[ 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:
- Creates
- Deletes
- Updates
- Extensions
- 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.