prismRPCDocs

Observability

The numbers Prism uses to choose a provider are the same ones it shows you. Nothing about the routing decision is hidden.

Response headers

Every response carries what happened to it:

bash
X-Prism-Provider: quicknode
X-Prism-Upstream-Latency: 38
X-Prism-Total-Latency: 39
X-Prism-Cache: miss
X-Prism-Attempts: 1
X-Prism-Request-Id: req_01j9x8k2m4n7p
X-Prism-Block-Height: 0x1f4a2c
HeaderMeaning
X-Prism-ProviderWhich upstream answered
X-Prism-Upstream-LatencyMilliseconds spent at the provider
X-Prism-Total-LatencyMilliseconds including routing overhead
X-Prism-Cachehit, miss or dedupe
X-Prism-AttemptsUpstreams tried; above 1 means a failover occurred
X-Prism-Request-IdQuote this in a support request
X-Prism-Block-HeightHead height of the provider that answered

X-Prism-Attempts is the useful one to alert on. A rising rate means your pool is degrading even while success rates look fine.

Logging the routing decision

ts
const response = await fetch(PRISM_URL, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify(payload),
});

logger.info('rpc', {
  method: payload.method,
  provider: response.headers.get('X-Prism-Provider'),
  latency: Number(response.headers.get('X-Prism-Total-Latency')),
  attempts: Number(response.headers.get('X-Prism-Attempts')),
  cache: response.headers.get('X-Prism-Cache'),
  requestId: response.headers.get('X-Prism-Request-Id'),
});

Recording requestId costs nothing and turns "it was slow yesterday" into a specific request we can look up.

Prometheus

Each key exposes a scrape endpoint authenticated with a separate metrics token:

bash
curl https://metrics.prismrpc.co/v1/YOUR_METRICS_TOKEN/metrics
bash
# HELP prism_requests_total Requests by method, provider and status.
# TYPE prism_requests_total counter
prism_requests_total{method="eth_call",provider="quicknode",status="ok"} 184213

# HELP prism_request_duration_ms Request latency including routing overhead.
# TYPE prism_request_duration_ms histogram
prism_request_duration_ms_bucket{method="eth_call",le="50"} 171004

# HELP prism_breaker_state Circuit breaker state: 0 closed, 1 half-open, 2 open.
# TYPE prism_breaker_state gauge
prism_breaker_state{provider="chainstack"} 2

# HELP prism_cache_hits_total Responses served without an upstream call.
# TYPE prism_cache_hits_total counter
prism_cache_hits_total{kind="deterministic"} 92311
prism_cache_hits_total{kind="dedupe"} 41880

# HELP prism_provider_block_height Head block height per provider.
# TYPE prism_provider_block_height gauge
prism_provider_block_height{provider="ankr"} 2050604

Scrape config:

json
{
  "scrape_configs": [
    {
      "job_name": "prism-rpc",
      "scrape_interval": "30s",
      "metrics_path": "/v1/YOUR_METRICS_TOKEN/metrics",
      "scheme": "https",
      "static_configs": [{ "targets": ["metrics.prismrpc.co"] }]
    }
  ]
}

Alerts worth having

Failover rate. X-Prism-Attempts above 1 on more than a few percent of requests means the pool is degrading. Alert before it becomes an outage.

Cache hit rate falling. A sudden drop usually means a client started sending unique parameters where it used to send repeated ones - often an accidental cache-buster in a query.

Provider height spread. A widening gap between the highest and lowest prism_provider_block_height is an early signal of network trouble, visible well before error rates move.

Your own p99, not ours. Prism reports latency to the provider. Alert on what your users experience, which includes your network path to our edge.

What is never recorded

Metrics carry method, latency, status, provider and cache outcome. They never carry method parameters, addresses, calldata, transaction contents or response results. Request bodies are held in memory for the life of the request and are not written to durable storage.

See the privacy policy for the full statement.