Protocols & transports
Every RPC is served over four protocols at the same URL. Pick whichever fits your stack.
| Protocol | Content-Type | Best for |
|---|---|---|
| gRPC (binary) | application/grpc | Native Rust / Go / Python clients — hot path. |
| gRPC-Web (binary) | application/grpc-web | Browsers using a gRPC-Web library. |
| Connect (JSON) | application/json | curl, Postman, debugging. |
| Connect (binary) | application/proto | Browser SDK default; smaller than JSON. |
The Superis SDKs default to:
| SDK | Default transport |
|---|---|
| Rust | gRPC (binary) over HTTPS |
| Go | gRPC (binary) over HTTPS |
| TypeScript-web | Connect (binary) over HTTPS |
All three let you override.
curl examples
POST to any RPC URL with a JSON body:
# Public — no auth needed.
curl -sS -X POST \
-H 'Content-Type: application/json' \
-d '{}' \
https://api.superis.exchange/sweetspot.api.v1.MarketDataService/ListPairs | jq
# Authenticated — pass the session token.
curl -sS -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${SUPERIS_TOKEN}" \
-d '{}' \
https://api.superis.exchange/sweetspot.api.v1.BalanceService/Get | jq
# Historical candles — note from_sec / to_sec are unix seconds.
curl -sS -X POST \
-H 'Content-Type: application/json' \
-H "Authorization: Bearer ${SUPERIS_TOKEN}" \
-d '{"pair":{"base":{"id":1},"quote":{"id":0}},"interval":"INTERVAL_5M","from_sec":1735689600,"to_sec":1735776000}' \
https://api.superis.exchange/sweetspot.api.v1.HistoricalService/GetCandles | jqThe JSON shape mirrors the proto field names exactly. Enums travel as their variant name ("INTERVAL_5M", not "5m" or 1).
Connection reuse
A single tonic Channel (Rust), *grpc.ClientConn (Go), or Connect Transport (TypeScript) handles every RPC across every service. Build it once at boot, share it across your service clients, and let HTTP/2 multiplexing handle the per-call traffic.
let channel = Channel::from_static("https://api.superis.exchange:443")
.connect()
.await?;
let auth = AuthFlow::new(channel.clone(), Arc::new(my_signer));
let market = MarketDataServiceClient::new(channel.clone());
let balances = BalanceServiceClient::with_interceptor(channel.clone(), auth.interceptor());
let tx = TxServiceClient::with_interceptor(channel.clone(), auth.interceptor());Streaming
Long-lived server streams (MarketDataService.Subscribe, BalanceService.Subscribe, TxService.SubscribeBlockhash / SubscribeSlots / SubscribeTxStatus) are the recommended path for anything event-driven. Wrap them in ResilientStream to get auto-reconnect + fan-out.