Skip to Content
SDKsDirect RPC

Direct RPC

You don’t need the TypeScript SDK to talk to the indexer. Any HTTP client that can POST JSON works. This page shows how to call every method from raw curl, how to construct requests from any language, and the minimum plumbing you need.

The transport

Every method is a JSON-RPC 2.0 request over HTTP. POST a JSON body to the endpoint:

POST / HTTP/1.1 Host: light.zcash.me Content-Type: application/json { "jsonrpc": "2.0", "id": 1, "method": "<method>", "params": { … } }

No auth headers, no cookies, no rate limiting on the default deployment. If the hosted endpoint has a path prefix (/zns-testnet, /zns-mainnet-test) include it.

Resolve a name

curl -sX POST https://light.zcash.me/zns-testnet \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"resolve","params":{"query":"alice"}}'

Response:

{ "jsonrpc": "2.0", "id": 1, "result": { "name": "alice", "address": "utest1qqq…", "txid": "abc123…", "height": 3901200, "nonce": 2, "signature": "AQID…", "last_action": "UPDATE", "listing": null } }

Resolve by address

curl -sX POST https://light.zcash.me/zns-testnet \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"resolve","params":{"query":"utest1qqq…"}}'

For address queries the result is an array of registrations (possibly empty). The indexer decides which branch based on whether the query parses as a Zcash address.

List for sale

curl -sX POST https://light.zcash.me/zns-testnet \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"list_for_sale","params":{}}'

Status

curl -sX POST https://light.zcash.me/zns-testnet \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"status","params":{}}'

Use this to read pricing (.result.pricing.tiers), the admin pubkey (.result.admin_pubkey), and the UIVK (.result.uivk).

Events with filters

# All claims, newest 10 curl -sX POST https://light.zcash.me/zns-testnet \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"events","params":{"action":"CLAIM","limit":10}}' # History for a specific name curl -sX POST https://light.zcash.me/zns-testnet \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"events","params":{"name":"alice"}}' # Events after a block height (strict >) curl -sX POST https://light.zcash.me/zns-testnet \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","id":1,"method":"events","params":{"since_height":3900000,"limit":100}}'

See JSON-RPC Reference § events for the full parameter list.

A minimal Rust client

use serde_json::{json, Value}; async fn rpc(method: &str, params: Value) -> anyhow::Result<Value> { let body = json!({ "jsonrpc": "2.0", "id": 1, "method": method, "params": params }); let res: Value = reqwest::Client::new() .post("https://light.zcash.me/zns-testnet") .json(&body) .send() .await? .json() .await?; if let Some(err) = res.get("error") { anyhow::bail!("RPC error: {err}"); } Ok(res["result"].clone()) } #[tokio::main] async fn main() -> anyhow::Result<()> { let reg = rpc("resolve", json!({ "query": "alice" })).await?; println!("{reg:#}"); Ok(()) }

A minimal Python client

import requests URL = "https://light.zcash.me/zns-testnet" def rpc(method, params=None): body = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params or {}} r = requests.post(URL, json=body, timeout=6) r.raise_for_status() data = r.json() if "error" in data: raise RuntimeError(data["error"]) return data["result"] print(rpc("resolve", {"query": "alice"})) print(rpc("status"))

A minimal Go client

package main import ( "bytes" "encoding/json" "fmt" "io" "net/http" ) const url = "https://light.zcash.me/zns-testnet" type Envelope struct { JsonRPC string `json:"jsonrpc"` ID int `json:"id"` Method string `json:"method"` Params interface{} `json:"params"` } type Response struct { Result json.RawMessage `json:"result"` Error *struct { Code int `json:"code"` Message string `json:"message"` } `json:"error"` } func RPC(method string, params interface{}) (json.RawMessage, error) { body, _ := json.Marshal(Envelope{"2.0", 1, method, params}) resp, err := http.Post(url, "application/json", bytes.NewReader(body)) if err != nil { return nil, err } defer resp.Body.Close() raw, _ := io.ReadAll(resp.Body) var r Response if err := json.Unmarshal(raw, &r); err != nil { return nil, err } if r.Error != nil { return nil, fmt.Errorf("rpc: %s", r.Error.Message) } return r.Result, nil } func main() { raw, _ := RPC("resolve", map[string]string{"query": "alice"}) fmt.Println(string(raw)) }

A note on schema drift

The RPC shape is normative - see the OpenRPC Explorer for an interactive rendering or openrpc.json for the raw file. Hand-rolled clients should mirror it exactly, especially for these details:

  • resolve returns an array for address queries, a single object (or null) for name queries.
  • status.pricing may be null if no SETPRICE has been observed.
  • status.pricing is not signed - treat it as advisory. See Verifying State.
  • events.action is a single string, not an array.
  • events.since_height is strict >, not >=.

Verifying signatures

Direct RPC clients should consider verifying every response’s signature. See Signature Scheme for the pre-image templates and verification procedure. Ed25519 libraries exist in every major language - use the platform-native one.

Last updated on