Guides
Guides
Dwellir GuidesAgent CookbookWhen to use IP whitelistingeth_getLogs LimitsBackfill the Full Hyperliquid OHLCV ArchiveExport Hyperliquid OHLCV Candles to CSV or ParquetBuild a Hyperliquid Trading App with Builder CodesStream Order Book Data with the Dwellir WebSocketBuild a Real-Time Base Flashblocks Visualizer with Next.jsHow to Build a Polymarket Copy-Trading BotHyperliquid Funding Rates: Hourly Formula and APIHow to Track Hyperliquid Whales in Real TimeHow to Find the Wallet Behind a Hyperliquid OrderPolymarket API: Endpoints, API Keys and Trading GuideKalshi API: Keys, Endpoints, WebSocket and PythonBuild a Real-Time Robinhood Chain Whale Tracker in PythonHow to Build a Copy Trading Bot on Arcus (Robinhood Chain)grpcurl
grpcurl: Install, List, Describe and Stream gRPC APIs cover

grpcurl: Install, List, Describe and Stream gRPC APIs

grpcurl is curl for gRPC. Install it on macOS, Linux or Windows, then list, describe, call and stream real gRPC APIs with headers, API keys and jq.

Published November 3, 2026

Toolgrpcurl 1.9.4
ProtocolgRPC
ExamplesSui, Hyperliquid

grpcurl is a command-line tool from FullStory that works like curl for gRPC servers: it takes a JSON request, encodes it as protobuf, calls the method and prints the response as JSON. It reads the API schema from server reflection, from .proto source files or from compiled protoset files, and the latest release is v1.9.4, published on August 31, 2026.

This guide shows how to install grpcurl, then list, describe, call and stream two production gRPC APIs: Dwellir's Sui and Hyperliquid mainnet endpoints. These grpcurl examples are real. We ran every command on November 3, 2026 with grpcurl 1.9.4, and the outputs below come from those runs. We trimmed long outputs where marked.

What you will learn

  • How to install grpcurl on macOS, Linux and Windows, or run it with Docker or Go
  • How to list services and methods and describe messages with server reflection
  • How to send JSON with -d and -d @, and headers and API keys with -H
  • How to stream with grpcurl, stop a stream with -max-time and decode base64 bytes fields with jq
  • How to work without reflection, with -proto, -import-path, -protoset and -protoset-out
  • What common grpcurl errors and exit codes mean

Prerequisites

  • A terminal on macOS, Linux or Windows
  • jq 1.6 or later for the streaming examples (we used jq 1.8.0)
  • A Dwellir API key, exported as an environment variable. Create one in the Dwellir dashboard.
Bash
export DWELLIR_API_KEY=YOUR_API_KEY

The Sui examples work with any Dwellir API key. The Hyperliquid examples need the Hyperliquid gRPC add-on, which costs $299/month on an API plan plus 1 API credit per streamed message. Without a key, or with a key that the server does not accept, both endpoints return PermissionDenied.

Try it: grpcurl command builder

Pick an endpoint, an action and the flags. The builder shows the exact command, what each part does and the response we captured for that combination, with its exit code.

Endpoint

Flags

Action

Command
grpcurl \
  -max-time 5 \
  -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d '{"read_mask":{"paths":["sequence_number"]}}' \
  api-sui-mainnet-full.n.dwellir.com:443 \
  sui.rpc.v2.SubscriptionService/SubscribeCheckpoints

What each part does

  1. grpcurlThe grpcurl binary. Every flag goes before the address.
  2. -max-time 5A deadline for the whole operation, in seconds. A stream that hits it ends with DeadlineExceeded and exit code 68.
  3. -expand-headersMakes grpcurl replace ${DWELLIR_API_KEY} with the environment variable. The single quotes stop your shell from doing it, so the key stays out of the process list.
  4. -H 'x-api-key: ${DWELLIR_API_KEY}'Adds gRPC metadata. Dwellir reads the key from x-api-key. grpcurl sends -H headers on reflection requests too, which Dwellir also authenticates.
  5. -d '{"read_mask":{"paths":["sequence_number"]}}'The request body as JSON. grpcurl converts it to protobuf with the schema it got from reflection. Field names can be snake_case or camelCase.
  6. api-sui-mainnet-full.n.dwellir.com:443host:port, with no scheme. grpcurl uses TLS unless you pass -plaintext, and Dwellir serves gRPC over TLS on 443.
  7. sui.rpc.v2.SubscriptionService/SubscribeCheckpointspackage.Service/Method to call. SubscribeCheckpoints pushes every new checkpoint. The read mask keeps each frame to the sequence number.

Captured response

exit 68
{
  "cursor": "341332852",
  "checkpoint": {
    "sequenceNumber": "341332852"
  }
}
{
  "cursor": "341332853",
  "checkpoint": {
    "sequenceNumber": "341332853"
  }
}
{
  "cursor": "341332854",
  "checkpoint": {
    "sequenceNumber": "341332854"
  }
}14 more frames trimmedERROR:
  Code: DeadlineExceeded
  Message: context deadline exceeded

Exit code 68, which is 64 + 4 (DeadlineExceeded). The deadline set with -max-time ended the stream after 5 s, as intended.

Responses captured with grpcurl 1.9.4 against Dwellir mainnet on November 3, 2026. Long responses are trimmed where marked, and Hyperliquid wallet addresses are shortened.

How do I install grpcurl?

Install grpcurl with brew install grpcurl on macOS, with a release binary on Linux or Windows, or with go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest on any system with Go. Every release ships prebuilt binaries for macOS, Linux and Windows on the grpcurl releases page.

SystemCommandVersion on November 3, 2026
macOS (Homebrew)brew install grpcurl1.9.4 (formula)
macOS or Linux (release binary)Download grpcurl_1.9.4_<os>_<arch>.tar.gz1.9.4
Debian, Ubuntu, Fedora.deb and .rpm files on the releases page1.9.4
Linux (Snap)snap install grpcurlSee Snapcraft
Windows (zip)Download grpcurl_1.9.4_windows_x86_64.zip1.9.4
Windows (Scoop)scoop install grpcurl1.9.4 (manifest)
Windows (winget)winget install fullstorydev.grpcurl1.9.3 (manifest)
Dockerdocker pull ghcr.io/fullstorydev/grpcurl:latestFollows releases
Any system with Gogo install github.com/fullstorydev/grpcurl/cmd/grpcurl@latestLatest tag

We tested the macOS arm64 release binary and Homebrew. The Linux, Windows, Docker and Snap commands come from the grpcurl README and the package manifests linked in the table.

Install the release binary on macOS or Linux

The tarball holds a single grpcurl binary. Pick the file for your system: osx_arm64, osx_x86_64, linux_x86_64, linux_arm64 and others.

Bash
curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.9.4/grpcurl_1.9.4_osx_arm64.tar.gz | tar -xz grpcurl
sudo mv grpcurl /usr/local/bin/
grpcurl -version
Text
grpcurl v1.9.4

Install grpcurl on Windows

Download grpcurl_1.9.4_windows_x86_64.zip (or the arm64 or x86_32 zip) from the releases page, unzip it and add the folder with grpcurl.exe to your PATH. With a package manager, use Scoop (scoop install grpcurl) or winget (winget install fullstorydev.grpcurl). Chocolatey has no grpcurl package: community.chocolatey.org/packages/grpcurl returned 404 on November 3, 2026.

In PowerShell, set the key with $env:DWELLIR_API_KEY = "YOUR_API_KEY". PowerShell handles quotes inside arguments differently from bash, so the -d @ form below, which reads the body from standard input, avoids escaping inline JSON. We ran the examples in this guide in bash on macOS.

Run grpcurl with Docker or Go

The README points to the GitHub Container Registry image, ghcr.io/fullstorydev/grpcurl. The Docker Hub image fullstorydev/grpcurl was last updated on March 11, 2025, so pull from ghcr.io. Everything after the image name in docker run goes to grpcurl, so the flags in this guide work unchanged. The README lists three Docker details: add -i to docker run when you pipe a body into -d @, mount a folder with -v $(pwd):/protos when you pass -proto or -protoset files, and use host.docker.internal instead of localhost to reach a server on your machine. We did not run the Docker image for this guide.

With Go, go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest puts the binary in $(go env GOPATH)/bin, which defaults to $HOME/go/bin. Add that folder to your PATH.

How do I list services and methods?

Run grpcurl HOST:PORT list to list every service a server exposes through reflection, and grpcurl HOST:PORT list SERVICE to list the methods of one service. All flags, including -H, go before the address.

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  api-sui-mainnet-full.n.dwellir.com:443 list
Text
grpc.health.v1.Health
grpc.reflection.v1.ServerReflection
sui.rpc.v2.LedgerService
sui.rpc.v2.MovePackageService
sui.rpc.v2.NameService
sui.rpc.v2.SignatureVerificationService
sui.rpc.v2.StateService
sui.rpc.v2.SubscriptionService
sui.rpc.v2.TransactionExecutionService
sui.rpc.v2alpha.ProofService

List the methods of one service:

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  api-sui-mainnet-full.n.dwellir.com:443 list sui.rpc.v2.LedgerService
Text
sui.rpc.v2.LedgerService.BatchGetObjects
sui.rpc.v2.LedgerService.BatchGetTransactions
sui.rpc.v2.LedgerService.GetCheckpoint
sui.rpc.v2.LedgerService.GetEpoch
sui.rpc.v2.LedgerService.GetObject
sui.rpc.v2.LedgerService.GetServiceInfo
sui.rpc.v2.LedgerService.GetTransaction
sui.rpc.v2.LedgerService.ListCheckpoints
sui.rpc.v2.LedgerService.ListEvents
sui.rpc.v2.LedgerService.ListTransactions

list prints methods as Service.Method. To call one, you can write either Service/Method or Service.Method. This guide uses the slash.

The Hyperliquid endpoint also has reflection. It exposes the V2 and V3 gateways and the V3 MarketStreaming service:

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  api-hyperliquid-mainnet-grpc.n.dwellir.com:443 list
Text
grpc.health.v1.Health
grpc.reflection.v1.ServerReflection
grpc.reflection.v1alpha.ServerReflection
hyperliquid_l1_gateway.v2.HyperliquidL1Gateway
hyperliquid_l1_gateway.v3.HyperliquidL1Gateway
hyperliquid_l1_gateway.v3.MarketStreaming

list hyperliquid_l1_gateway.v3.MarketStreaming returns 14 typed streams, from StreamActiveAssetCtx to StreamUserFills. The Hyperliquid gRPC overview documents each one.

How do I describe a message?

Run grpcurl HOST:PORT describe SYMBOL to print a service, method or message as proto source. Add -msg-template to also print a JSON template of the request, which you can edit into a -d body.

Describe a method first to find its request type:

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  api-sui-mainnet-full.n.dwellir.com:443 describe sui.rpc.v2.LedgerService.GetServiceInfo
Text
sui.rpc.v2.LedgerService.GetServiceInfo is a method:
rpc GetServiceInfo ( .sui.rpc.v2.GetServiceInfoRequest ) returns ( .sui.rpc.v2.GetServiceInfoResponse );

Then describe the request message, with a template:

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' -msg-template \
  api-sui-mainnet-full.n.dwellir.com:443 describe sui.rpc.v2.GetCheckpointRequest
Text
sui.rpc.v2.GetCheckpointRequest is a message:
message GetCheckpointRequest {
  oneof checkpoint_id {
    uint64 sequence_number = 1;
    string digest = 2;
  }
  optional .google.protobuf.FieldMask read_mask = 3;
}

Message template:
{
  "readMask": {
    "paths": [
      ""
    ]
  }
}

The template leaves out oneof members. You set a oneof by naming the member directly, as in {"sequence_number":"1"}. The wrapper name does not work: {"checkpoint_id":{...}} fails with has no known field named checkpoint_id.

describe takes one symbol per run. Two symbols in one command fail with Too many arguments.

How do I call a method with JSON input?

Pass the request body as JSON with -d '{...}', then the address, then package.Service/Method. grpcurl converts the JSON to protobuf with the schema from reflection and prints the response as JSON. Without -d, grpcurl sends an empty request.

Bash
grpcurl -max-time 10 -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d '{}' api-sui-mainnet-full.n.dwellir.com:443 sui.rpc.v2.LedgerService/GetServiceInfo
JSON
{
  "chainId": "4btiuiMPvEENsttpZC7CZ53DruC3MAgfznDbASZ7DR6S",
  "chain": "mainnet",
  "epoch": "1300",
  "checkpointHeight": "341332843",
  "timestamp": "2026-11-03T21:17:24.319Z",
  "lowestAvailableCheckpoint": "335870173",
  "lowestAvailableCheckpointObjects": "335870173",
  "server": "sui-node/1.82.5-550e784ea54d"
}

A field mask keeps the response small:

Bash
grpcurl -max-time 10 -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d '{"read_mask":{"paths":["sequence_number","summary.timestamp"]}}' \
  api-sui-mainnet-full.n.dwellir.com:443 sui.rpc.v2.LedgerService/GetCheckpoint
JSON
{
  "checkpoint": {
    "sequenceNumber": "341332956",
    "summary": {
      "timestamp": "2026-11-03T21:17:52.148Z"
    }
  }
}

Three rules of the JSON mapping show up here. Request field names can be snake_case (read_mask) or camelCase (readMask). Responses use camelCase. 64-bit integers such as uint64 print as strings.

Read the body from stdin with -d @

-d @ tells grpcurl to read the request body from standard input. Use it for long bodies, for JSON built with jq, or on Windows, where inline JSON quoting is awkward:

Bash
grpcurl -max-time 10 -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d @ api-sui-mainnet-full.n.dwellir.com:443 sui.rpc.v2.LedgerService/GetServiceInfo <<'EOF'
{}
EOF

Piping works the same way: echo '{}' | grpcurl ... -d @ .... For methods that accept a stream of requests, put several JSON objects on stdin, and grpcurl sends each one as a message.

How do I send headers and API keys?

Add gRPC metadata with -H 'name: value' before the address, and repeat -H for each header. Dwellir reads the API key from the x-api-key header. grpcurl sends -H headers on reflection requests too, so a key that the server needs for reflection works without extra flags.

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  api-sui-mainnet-full.n.dwellir.com:443 list

-expand-headers makes grpcurl replace ${NAME} in header values with the environment variable. The single quotes stop the shell from expanding it first, so the key never appears in the process list or in your shell history. Without -expand-headers, use double quotes and let the shell expand it: -H "x-api-key: $DWELLIR_API_KEY".

FlagSent on the RPCSent on reflection
-H 'name: value'YesYes
-rpc-header 'name: value'YesNo
-reflect-header 'name: value'NoYes

Add -v to see what grpcurl sends and receives. It prints the resolved method, the request metadata, the response headers and trailers:

Text
Resolved method descriptor:
rpc GetServiceInfo ( .sui.rpc.v2.GetServiceInfoRequest ) returns ( .sui.rpc.v2.GetServiceInfoResponse );

Request metadata to send:
x-api-key: YOUR_API_KEY

Response headers received:
content-type: application/grpc
server: sui-node/1.82.5-550e784ea54d
x-sui-chain: mainnet
x-sui-checkpoint-height: 341332845
x-sui-epoch: 1300
x-sui-lowest-available-checkpoint: 336040478

Response contents:
{ ... }

Response trailers received:
(empty)
Sent 1 request and received 1 response

We trimmed the response headers. -v prints the real key in Request metadata to send, so redact it before you paste the output into a ticket or a chat. -vv adds timing data. On one of our runs, a GetServiceInfo call took 466.7 ms in total, of which 40.8 ms was the dial.

How do I stream with grpcurl?

Call a server-streaming method the same way as a unary one: grpcurl prints each message as a separate JSON object as it arrives. The stream runs until the server ends it, you press Ctrl+C, or the -max-time deadline expires. With -max-time, the stream ends with DeadlineExceeded and exit code 68, which is the expected way to stop an unbounded stream in a script.

grpcurl stream with -max-time 5 on Sui: 17 checkpoint frames in 5 seconds, then DeadlineExceeded and exit code 68 (64 + status code 4). Other exit codes: 67, 69 and 71 for gRPC statuses, 2 for usage errors, 1 for dial, reflection or JSON errors.

Stream Sui checkpoints

SubscribeCheckpoints pushes every new Sui checkpoint. The read mask keeps each frame to the sequence number:

Bash
grpcurl -max-time 5 -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d '{"read_mask":{"paths":["sequence_number"]}}' \
  api-sui-mainnet-full.n.dwellir.com:443 sui.rpc.v2.SubscriptionService/SubscribeCheckpoints
echo "exit=$?"
Text
{
  "cursor": "341332852",
  "checkpoint": {
    "sequenceNumber": "341332852"
  }
}
{
  "cursor": "341332853",
  "checkpoint": {
    "sequenceNumber": "341332853"
  }
}
... 15 more frames ...
ERROR:
  Code: DeadlineExceeded
  Message: context deadline exceeded
exit=68

The stream delivered 17 frames in 5 seconds. Each frame is a separate JSON object, not an array, so pipe the stream to jq -c to get one line per message:

Bash
grpcurl -max-time 3 -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d '{"read_mask":{"paths":["sequence_number","summary.timestamp"]}}' \
  api-sui-mainnet-full.n.dwellir.com:443 sui.rpc.v2.SubscriptionService/SubscribeCheckpoints \
  | jq -c '{seq: .checkpoint.sequenceNumber, ts: .checkpoint.summary.timestamp}'
Text
ERROR:
  Code: DeadlineExceeded
  Message: context deadline exceeded
{"seq":"341335344","ts":"2026-11-03T21:27:34.474Z"}
{"seq":"341335345","ts":"2026-11-03T21:27:34.675Z"}
{"seq":"341335346","ts":"2026-11-03T21:27:34.876Z"}

The ERROR lines go to stderr, so they print before jq flushes its output. In a pipe, $? is jq's exit code. Use set -o pipefail or ${PIPESTATUS[0]} in bash to get grpcurl's 68. The Sui grpcurl setup page has more Sui stream and checkpoint-range examples.

Stream Hyperliquid trades

The Hyperliquid MarketStreaming service returns decoded JSON. Describe the request first, because the field names are plural:

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  api-hyperliquid-mainnet-grpc.n.dwellir.com:443 describe hyperliquid_l1_gateway.v3.TradesRequest
Text
hyperliquid_l1_gateway.v3.TradesRequest is a message:
message TradesRequest {
  repeated string coins = 1;
  repeated string users = 2;
}
Bash
grpcurl -max-time 5 -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d '{"coins":["BTC"]}' \
  api-hyperliquid-mainnet-grpc.n.dwellir.com:443 hyperliquid_l1_gateway.v3.MarketStreaming/StreamTrades
JSON
{
  "time": "1793740659339",
  "blockNumber": "1208044332",
  "trades": [
    {
      "coin": "BTC",
      "side": "B",
      "px": "77911.0",
      "sz": "0.12925",
      "hash": "0x6efdf4436318ab1670770448014b2c0205550028fe1bc9e812c69f96221c8501",
      "time": "1793740659339",
      "tid": "170037552688317",
      "bidUsers": ["0xecb6…2b00"],
      "askUsers": ["0x7717…bd7e"]
    }
  ]
}

We shortened the wallet addresses and the trades array. In 5 seconds the stream sent 9 updates for BTC. -d '{"coin":"BTC"}' (singular) fails before the RPC is sent, with message type hyperliquid_l1_gateway.v3.TradesRequest has no known field named coin. See the StreamTrades reference for every field.

Decode base64 bytes fields with jq

grpcurl prints protobuf bytes fields as base64. The Hyperliquid V3 raw feeds, such as StreamFills, put the block's JSON in a bytes field named data:

Text
{
  "blockNumber": "1208044715",
  "timestamp": "1793740687002",
  "data": "eyJsb2NhbF90aW1lIjoiMjAyNi0xMS0wM1QyMToxODowNy40NDMwNzY2NjkiLCJibG9ja190aW1lIjo..."
}

jq's @base64d decodes it, and fromjson parses the result:

Bash
grpcurl -max-time 10 -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -d '{"filter":{"fields":[{"field":"coins","values":["BTC"]}]}}' \
  api-hyperliquid-mainnet-grpc.n.dwellir.com:443 hyperliquid_l1_gateway.v3.HyperliquidL1Gateway/StreamFills \
  | jq -c '.data | @base64d | fromjson | {block_number, n: (.events|length), first: .events[0][1] | {coin, px, sz, side, dir}}'
Text
{"block_number":1208044577,"n":2,"first":{"coin":"BTC","px":"77915.0","sz":"0.00032","side":"B","dir":"Open Long"}}
{"block_number":1208044592,"n":2,"first":{"coin":"BTC","px":"77915.0","sz":"0.00031","side":"B","dir":"Open Long"}}
{"block_number":1208044615,"n":2,"first":{"coin":"BTC","px":"77915.0","sz":"0.00019","side":"B","dir":"Open Long"}}

The filter in this request is a server-side filter. ListFeeds tells you which filter fields each feed accepts. On November 3, 2026 the fills feed accepted users, coins and dexes. See the StreamFills reference and ListFeeds.

A filtered stream can stay silent for minutes when nothing matches. Ask for progress frames, or use a longer -max-time. The Hyperliquid gRPC troubleshooting page covers both. To see what the same feeds look like in a production pipeline, read how we streamed the Hyperliquid mempool over gRPC and built a machine learning dataset from Hyperliquid gRPC.

What if the server has no reflection?

Without reflection, grpcurl needs the schema from you: pass -proto with the .proto file (and -import-path for its imports), or -protoset with a compiled descriptor set. A server without reflection fails list with this error:

Text
Failed to list services: server does not support the reflection API

That output is from grpcurl pubsub.googleapis.com:443 list, a Google API that does not expose reflection.

grpcurl gets a gRPC schema three ways: server reflection by default, local .proto files with -proto and -import-path, or a compiled protoset with -protoset; the last two work offline.

Use a .proto file with -proto and -import-path

Dwellir publishes the Hyperliquid V3 schema at /docs/hyperliquid/grpc/v3.proto. With -proto, list and describe read the file and need no server address:

Bash
curl -fsSLo v3.proto https://www.dwellir.com/docs/hyperliquid/grpc/v3.proto
grpcurl -proto v3.proto list
Text
hyperliquid_l1_gateway.v3.HyperliquidL1Gateway
hyperliquid_l1_gateway.v3.MarketStreaming

A call with -proto skips reflection and sends the RPC directly:

Bash
grpcurl -max-time 10 -proto v3.proto \
  -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' -d '{}' \
  api-hyperliquid-mainnet-grpc.n.dwellir.com:443 hyperliquid_l1_gateway.v3.HyperliquidL1Gateway/ListFeeds

If your .proto imports other files, add -import-path for each folder that holds them. grpcurl knows the google.protobuf well-known types without an import path. An absolute file path needs an import path too: grpcurl -proto /abs/path/v3.proto list fails with must specify at least one import path if any absolute file paths are given. Use -import-path /abs/path -proto v3.proto instead.

Save and reuse a protoset with -protoset-out

When a server has reflection, -protoset-out saves the schema of what you list or describe to a file, with all its dependencies. You can then work offline, or against a copy of the server without reflection:

Bash
grpcurl -expand-headers -H 'x-api-key: ${DWELLIR_API_KEY}' \
  -protoset-out sui.protoset \
  api-sui-mainnet-full.n.dwellir.com:443 describe sui.rpc.v2.LedgerService

grpcurl -protoset sui.protoset list
Text
sui.rpc.v2.LedgerService

The file for this one service was 52,646 bytes, because it bundles every message the service depends on. A call with -protoset sui.protoset returns the same GetServiceInfo response as the reflection call above. To build a protoset from source files without a server, use protoc --descriptor_set_out=out.protoset --include_imports your.proto.

You cannot combine -proto and -protoset in one command. Add -use-reflection to either one if you want grpcurl to use reflection as well.

Common grpcurl errors and fixes

Most grpcurl errors come from a flag that does not match the server, a missing key, or a request body that does not match the schema. Each error below is real output from grpcurl 1.9.4.

Error outputCauseFix
Failed to list services: server does not support the reflection APIThe server has no reflectionPass -proto with -import-path, or -protoset
PermissionDenied desc = unexpected HTTP status code received from server: 403 (Forbidden)Missing or invalid API key. Dwellir adds API key does not exist in a data: lineSend -H 'x-api-key: ...'. Hyperliquid also needs the gRPC add-on
Failed to dial target host "api-sui-mainnet-full.n.dwellir.com:443": EOF-plaintext against a TLS portDrop -plaintext. Use it only for servers without TLS, such as localhost
Failed to dial target host "...": context deadline exceededWrong host or port, or a firewall. grpcurl waits 10 s by defaultCheck the address. Add -connect-timeout 2 to fail faster
message type ... has no known field named coinThe JSON body does not match the schemadescribe the request message, or use -msg-template
service "sui.rpc.v2.LedgerService" does not include a method named "Nope"A typo in the method nameRun list SERVICE
grpc: received message larger than max (1426 vs. 10) with ResourceExhaustedThe response is larger than -max-msg-sz (default 4,194,304 bytes)Raise it, for example -max-msg-sz 16777216
Code: DeadlineExceeded after streamed output-max-time expiredExpected for a capped stream. Raise or drop -max-time for longer runs
Code: NotFound, Message: Checkpoint 1 not foundThe data is older than the node keepsRead lowestAvailableCheckpoint from GetServiceInfo
Code: InvalidArgument, block_number 0 is not a valid cursor; omit position for latestAn explicit zero cursor on a Hyperliquid streamLeave position out to start from the latest record
must specify at least one import path if any absolute file paths are given-proto with an absolute pathAdd -import-path and pass the file name
error getting request data: line 1, col 1: Expecting an identifier or tag number-format text with a JSON bodyDrop -format text, or write the body in protobuf text format

Add -format-error to print the gRPC error status as JSON, which is easier to parse in scripts:

JSON
{
  "code": 5,
  "message": "Checkpoint 1 not found"
}

grpcurl exit codes

grpcurl 1.9.4 exits with 0 on success, 2 on a usage error (a bad flag or too many arguments), 1 when a call fails in grpcurl itself, and 64 plus the gRPC status code when the RPC returns an error status. We observed these codes in our runs. The grpcurl README and -help text do not document them, so treat the mapping as observed behaviour that a future release could change.

Exit codeMeaningWhere we saw it
0SuccessEvery successful call
1Tool error: connection failure, reflection failure, bad request JSON-plaintext on a TLS port, no reflection, an unknown field
2Usage error: unknown flag or too many arguments-nope, describe with two symbols
6764 + 3, InvalidArgumentHyperliquid stream with block_number 0
6864 + 4, DeadlineExceededAny stream stopped by -max-time
6964 + 5, NotFoundSui GetCheckpoint for checkpoint 1
7164 + 7, PermissionDeniedHyperliquid call with -proto and no API key

The same missing key gives different exit codes depending on how grpcurl gets the schema. With reflection, the 403 hits the reflection request, so grpcurl reports a tool error and exits with 1. With -proto, there is no reflection request, the RPC itself returns PermissionDenied, and grpcurl exits with 71.

grpcurl vs grpcui vs Postman vs Evans

grpcurl is the scriptable choice: one command, JSON in and out, exit codes you can test. The other three tools suit interactive exploration.

ToolInterfaceSchema sourcesStreamingLatest release
grpcurlCommand lineReflection, .proto, protosetAll four kinds, interactive bidirectional from a terminalv1.9.4, August 31, 2026
grpcuiWeb UI started from the command lineReflection, .proto, protosetAll kinds, but you send the whole request stream at once and see the whole response stream at oncev1.5.4, September 2, 2026
PostmanDesktop and web appReflection, or an imported .protoUnary, client, server and bidirectional streamingHosted app
EvansREPL and CLI modesReflection, .protoClient, server and bidirectional streamingv0.10.11, February 26, 2023

grpcui comes from the same FullStory team and takes the same flags for reflection, -proto and -protoset, so a command that works in grpcurl usually works in grpcui. Evans has had no release since February 2023.

Next steps

  • Use the Sui grpcurl setup page for Sui-specific calls: balances, package functions, checkpoint ranges and transaction execution. The Sui gRPC overview lists every service.
  • Read the Hyperliquid gRPC overview for positions, filters, progress frames and retention on the V3 API, and see Hyperliquid gRPC streaming for plans and pricing.
  • Move from grpcurl to code with the Dwellir gRPC code examples once your request bodies work.

Frequently Asked Questions

What is grpcurl?

grpcurl is an open-source command-line tool from FullStory that works like curl for gRPC servers. It takes a JSON request, encodes it as protobuf, calls the method and prints the response as JSON. It reads the API schema from server reflection, from .proto source files or from a compiled protoset file. The latest release is v1.9.4, published on August 31, 2026.

How do I pass headers in grpcurl?

Use -H 'name: value' before the server address, and repeat -H for each extra header. grpcurl sends -H headers on the RPC and on reflection requests. Use -rpc-header for headers that only the RPC needs, and -reflect-header for headers that only reflection needs. To keep an API key out of the process list, add -expand-headers and write the header as -H 'x-api-key: ${DWELLIR_API_KEY}' in single quotes.

Does grpcurl support streaming?

Yes. grpcurl supports unary, server streaming, client streaming and bidirectional streaming methods. A server stream prints each message as its own JSON object until the server ends the stream, you press Ctrl+C, or -max-time expires. With -max-time the stream ends with DeadlineExceeded, and grpcurl 1.9.4 exits with code 68. Pipe the output to jq -c to get one line per message.

How do I use grpcurl without reflection?

Pass the schema yourself. Use -proto file.proto, with -import-path for the folders that hold its imports, or use -protoset file.protoset with a compiled FileDescriptorSet. With either flag, list and describe work offline without a server address. If a server does have reflection, -protoset-out saves its schema to a protoset file that you can reuse later.

How do I install grpcurl on Windows?

Download grpcurl_1.9.4_windows_x86_64.zip (or the arm64 or x86_32 zip) from the grpcurl GitHub releases page and put grpcurl.exe on your PATH. Scoop has the package in its main bucket at version 1.9.4: scoop install grpcurl. winget has fullstorydev.grpcurl at version 1.9.3. Chocolatey has no grpcurl package. You can also run the ghcr.io/fullstorydev/grpcurl Docker image.

What do grpcurl exit codes mean?

In our tests with grpcurl 1.9.4, exit code 0 meant success, 2 meant a usage error (a bad flag or too many arguments) and 1 meant a tool-side failure, such as a failed connection, a reflection error or a request body that does not match the schema. A gRPC error status from the RPC gave 64 plus the status code: 67 for InvalidArgument, 68 for DeadlineExceeded, 69 for NotFound and 71 for PermissionDenied. The grpcurl README does not document these codes.

How to Build a Copy Trading Bot on Arcus (Robinhood Chain)

Build a copy trading bot for Arcus on Robinhood Chain: detect a target wallet's Stock Token trades over WebSocket and mirror them through the Arcus spot flow.

On this page

What you will learnPrerequisitesTry it: grpcurl command builderHow do I install grpcurl?Install the release binary on macOS or LinuxInstall grpcurl on WindowsRun grpcurl with Docker or GoHow do I list services and methods?How do I describe a message?How do I call a method with JSON input?Read the body from stdin with -d @How do I send headers and API keys?How do I stream with grpcurl?Stream Sui checkpointsStream Hyperliquid tradesDecode base64 bytes fields with jqWhat if the server has no reflection?Use a .proto file with -proto and -import-pathSave and reuse a protoset with -protoset-outCommon grpcurl errors and fixesgrpcurl exit codesgrpcurl vs grpcui vs Postman vs EvansNext steps