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:
resolvereturns an array for address queries, a single object (or null) for name queries.status.pricingmay be null if no SETPRICE has been observed.status.pricingis not signed - treat it as advisory. See Verifying State.events.actionis a single string, not an array.events.since_heightis 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.