Upstreams
An upstream is defined to handle 1 or more networks (a.k.a. chains). There are currently these types of upstreams:
evm
A generic EVM-compatible JSON-RPC endpoint. This is the default and most-used type.
eRPC supports any EVM-compatible JSON-RPC endpoint when using evm
type. Specialized types like "alchemy" are built for well-known providers to make it easier to import "all supported evm chains" with just an API-KEY.
Config
# ...
projects:
- id: main
# ...
# Each upstream supports 1 or more networks (i.e. evm chains)
upstreams:
# (REQUIRED) Endpoint URL supports http(s) scheme along with custom schemes like "alchemy://" defined below in this docs.
- endpoint: https://arbitrum-one.blastapi.io/xxxxxxx-xxxxxx-xxxxxxx
# Each upstream can have an arbitrary group name which is used in metrics, as well as
# useful when writing an eval function in selectionPolicy below.
# Use "fallback" group to let eRPC automatically create a "default" selection policy on the network level
# and then fallback to this group if the default one doesn't have enough healthy upstreams.
group: fallback
# (OPTIONAL) Upstream ID is optional and can be used to identify the upstream in logs/metrics.
id: blastapi-chain-42161
# (OPTIONAL) Configurations for EVM-compatible upstreams.
evm:
# (OPTIONAL) chainId is optional and will be detected from the endpoint (eth_chainId),
# but it is recommended to set it explicitly, for faster initialization.
# DEFAULT: auto-detected.
chainId: 42161
# (OPTIONAL) statePollerInterval used to periodically fetch the latest/finalized/sync states.
# DEFAULT: 30s.
statePollerInterval: 30s
# (OPTIONAL) statePollerDebounce prevents too many Polls for latest/finalized block numbers during integrity checks.
# This ideally must be close (or lower than) the block time of the chain, but not too low to avoid thundering herd (e.g <1s is too low).
# DEFAULT: 5s (or equal to block time if the chainId is a known chain)
statePollerDebounce: 5s
# (OPTIONAL) nodeType is optional and you can manually set it to "full" or "archive".
# DEFAULT: archive
nodeType: full
# (OPTIONAL) maxAvailableRecentBlocks limits the maximum number of recent blocks to be served by this upstream.
# DEFAULT: 128 (for "full" nodes).
maxAvailableRecentBlocks: 128
# (OPTIONAL) getLogsMaxBlockRange limits the maximum block range for eth_getLogs requests.
# This is used to automatically split the request into smaller sub-requests if the range is too high.
# Set to -1 to disable this behavior.
# DEFAULT: 10000 (10000 blocks)
getLogsMaxBlockRange: 10000
# (OPTIONAL) Defines which budget to use when hadnling requests of this upstream (e.g. to limit total RPS)
# Since budgets can be applied to multiple upstreams they all consume from the same budget.
# For example "global-blast" below can be applied to all chains supported by BlastAPI,
# to ensure you're not hitting them more than your account allows.
# DEFAULT: <none> - no budget applied.
rateLimitBudget: global-blast
# (OPTIONAL) Rate limit budget can be automatically adjusted based on the "rate-limited" error rate,
# received from upstream. Auto-tuning is enabled by default with values below.
# This is useful to automatically increase the budget if an upstream is capable of handling more requests,
# and decrease the budget if upstream is degraded.
# Every "adjustmentPeriod" total number of requests vs rate-limited will be calculated,
# if the value (0 to 1) is above "errorRateThreshold" then budget will be decreased by "decreaseFactor",
# if the value is below "errorRateThreshold" then budget will be increased by "increaseFactor".
# Note that the new budget will be applied to any upstream using this budget (e.g. Quicknode budget decreases).
# DEFAULT: if any budget is defined, auto-tuning is enabled with these values:
rateLimitAutoTune:
enabled: true
adjustmentPeriod: 1m
errorRateThreshold: 0.1
increaseFactor: 1.05
decreaseFactor: 0.9
minBudget: 0
maxBudget: 10_000
jsonRpc:
# (OPTIONAL) To allow auto-batching requests towards the upstream.
# Remember even if "supportsBatch" is false, you still can send batch requests to eRPC
# but they will be sent to upstream as individual requests.
supportsBatch: true
batchMaxSize: 10
batchMaxWait: 50ms
# (OPTIONAL) Headers to send along with every outbound JSON-RPC request.
# This is especially useful for upstreams that require a static Bearer token for authentication.
headers:
Authorization: "Bearer 1234567890"
# (OPTIONAL) Which methods must never be sent to this upstream.
# For example this can be used to avoid archive calls (traces) to full nodes
ignoreMethods:
- "eth_traceTransaction"
- "alchemy_*"
# (OPTIONAL) Explicitly allowed methods will take precedence over ignoreMethods.
# For example if you only want eth_getLogs to be served, set ignore methods to "*" and allowMethods to "eth_getLogs".
allowMethods:
- "eth_getLogs"
# (OPTIONAL) By default a dynamic mechanism automatically adds "Unsupported" methods to ignoreMethods,
# based on errors returned by the upstream. Set this to false to disable this behavior.
# Default: true
autoIgnoreUnsupportedMethods: true
# (OPTIONAL) Refer to "Failsafe" docs section for more details.
# Here is "default" configuration if not explicitly set:
failsafe:
timeout:
duration: 15s
retry:
maxAttempts: 2
delay: 1000ms
backoffMaxDelay: 10s
backoffFactor: 0.3
jitter: 500ms
circuitBreaker:
# Open circuit after 80% of requests so far have failed (160 out of 200 last requests)
failureThresholdCount: 160
failureThresholdCapacity: 200
# Wait 5 minutes before trying again
halfOpenAfter: 5m
# Close circuit after 3 successful requests (3 out of 10)
successThresholdCount: 3
successThresholdCapacity: 10
Config defaults
The project.upstreamDefaults
configuration allows you to set default values for all upstreams
in a project. These defaults are applied before any upstream-specific configurations:
projects:
- id: main
upstreams:
# ... example above ^
upstreamDefaults:
# Default group for all upstreams
group: "default"
# Default JSON-RPC settings
jsonRpc:
supportsBatch: true
batchMaxSize: 10
batchMaxWait: "50ms"
# Default failsafe policies
failsafe:
timeout:
duration: "15s"
retry:
maxAttempts: 3
delay: "300ms"
jitter: "100ms"
backoffMaxDelay: "5s"
backoffFactor: 1.5
circuitBreaker:
failureThresholdCount: 160
failureThresholdCapacity: 200
halfOpenAfter: "5m"
successThresholdCount: 3
successThresholdCapacity: 3
# Default method filters
ignoreMethods:
- "eth_traceTransaction"
- "alchemy_*"
allowMethods:
- "eth_getLogs"
Default values are only applied if the upstream doesn't have those values explicitly set. This allows you to have consistent configuration across all upstreams while still maintaining the ability to override specific values when needed.
Defaults are merged on the first-level only (and not a deep merge).
i.e. If an upstream has its own failsafe:
defined, it will not take any of policies from upstreamDefaults.
e.g. if an upstream.failsafe only has "timeout" policy, it will NOT get retry/circuitBreaker from upstreamDefaults (those will be disabled).
Priority & selection mechanism
eRPC evaluates each upstream's performance using key metrics to decide the most suitable upstream for each request. These metrics include:
- Total request failures: Prioritizes upstreams with lower failure rates.
- Rate-limited requests: Gives preference to upstreams with fewer rate-limited requests.
- P90 request latency: Prioritizes upstreams with lower latency.
- Total requests served: Favors upstreams that have served fewer requests to balance load.
- Block head lag: Prefers upstreams with lower lag compared to the best-performing upstream.
- Finalization lag: Prioritizes upstreams with lower finalization lag.
Each upstream receives a score based on these metrics, calculated per method (e.g., eth_blockNumber
, eth_getLogs
) over a configurable time window (windowSize
, default 30 minutes). Adjust the window size in erpc.yaml
as shown:
projects:
# ...
- id: main
# ...
healthCheck:
scoreMetricsWindowSize: 1h
# ...
The scoring mechanism only affects the order in which upstreams are tried. To fully disable an unreliable upstream, use the Circuit Breaker failsafe policy at the upstream level.
Customizing scores & priorities
Upstreams are ranked by score, controlling selection order. You can adjust this ranking by setting multipliers at different levels: overall, per network or method, or for specific metrics (e.g., error rate, block lag).
upstreams:
# ...
- id: my-alchemy
# ...
routing:
scoreMultipliers:
- network: '*' # Relevant when upstream supports multiple networks (default: all networks)
method: '*' # Method(s) where you want to apply these multipliers (default: all methods)
# method: 'eth_*|alchemy_*' means apply these multipliers to all methods starting with "eth_" or "alchemy_"
# (OPTIONAL) Adjusts the overall score scale.
# DEFAULT: 1.0
overall: 1.0
# (OPTIONAL) Default multiplier values:
errorRate: 8.0 # Penalize higher error rates by increasing this value.
p90latency: 4.0 # Penalize higher latency by increasing this value.
totalRequests: 1.0 # Give more weight to upstreams with fewer requests.
throttledRate: 3.0 # Penalize higher throttled requests by increasing this value.
blockHeadLag: 2.0 # Penalize nodes lagging in block head updates by increasing this value.
finalizationLag: 1.0 # Penalize nodes lagging in finalization by increasing this value.
Example: To prioritize a less expensive (but slower) upstream, adjust the overall
score multiplier as follows:
upstreams:
# ...
- id: my-cheap-node
# ...
routing:
scoreMultipliers:
- overall: 10
- id: my-expensive-node
# ...
routing:
scoreMultipliers:
- overall: 1
A higher score means the upstream is tried first. If errors occur, other upstreams are attempted.
Upstream types
evm
These are generic well-known EVM-compatible JSON-RPC endpoints. This is the default and most-used type. They can be your own self-hosted nodes, or remote 3rd-party provider nodes.
# ...
projects:
- id: main
# ...
upstreams:
- id: my-infura
type: evm
endpoint: https://mainnet.infura.io/v3/YOUR_INFURA_KEY
# (OPTIONAL) Configurations for EVM-compatible upstreams.
evm:
# (OPTIONAL) chainId is optional and will be detected from the endpoint (eth_chainId),
# but it is recommended to set it explicitly, for faster initialization.
# DEFAULT: auto-detected.
chainId: 42161
# (OPTIONAL) statePollerInterval used to periodically fetch the latest/finalized/sync states.
# To disable state polling set this value to 0, which means no regular calls to RPC for latest/finalized/sync states.
# The consequence of this is all data will be considered "unfinalized" or "unknown" despite their block numbers (and where if theye're actually finalized or not).
# DEFAULT: 30s.
statePollerInterval: 30s
# (OPTIONAL) statePollerDebounce prevents too many Polls for latest/finalized block numbers during integrity checks.
# This ideally must be close (or lower than) the block time of the chain, but not too low to avoid thundering herd (e.g <1s is too low).
# DEFAULT: 5s (or equal to block time if the chainId is a known chain)
statePollerDebounce: 5s
# (OPTIONAL) nodeType is optional and you can manually set it to "full" or "archive".
# DEFAULT: archive
nodeType: full
# (OPTIONAL) maxAvailableRecentBlocks limits the maximum number of recent blocks to be served by this upstream.
# DEFAULT: 128 (for "full" nodes).
maxAvailableRecentBlocks: 128
# (OPTIONAL) getLogsMaxBlockRange limits the maximum block range for eth_getLogs requests.
# This is used to automatically split the request into smaller sub-requests if the range is too high.
# Set to -1 to disable this behavior.
# DEFAULT: 10000 (10000 blocks)
getLogsMaxBlockRange: 10000
# ...
eth_getLogs
max range automatic splitting
Certain providers have a limit on the maximum block range for eth_getLogs
requests. If the range is too high, eRPC will automatically split the request into smaller sub-requests.
To enable this feature, set the getLogsMaxBlockRange
in the upstream config:
upstreams:
- id: my-infura
evm:
# (OPTIONAL) getLogsMaxBlockRange limits the maximum block range for eth_getLogs requests.
# This is used to automatically split the request into smaller sub-requests if the range is too high.
# Set to -1 to disable this behavior.
# DEFAULT: 10000 (10000 blocks)
getLogsMaxBlockRange: 10000
Additionally eRPC has integrity checks for eth_getLogs you can read more about in the Integrity section.
If an RPC node complains about "too many results" eRPC will automatically split the request (first on block range, then on address filter, then on topics filter) and retry until it works, then merges the results automatically.
Compression
eRPC supports gzip compression at multiple points in the request/response cycle:
- Client → eRPC: Clients can send gzipped requests by setting
Content-Encoding: gzip
header
# Example of sending gzipped request to eRPC
curl -X POST \
-H "Content-Encoding: gzip" \
-H "Content-Type: application/json" \
--data-binary @<(echo '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[]}' | gzip) \
http://localhost:4000/main/evm/42161
- eRPC → Upstream: Configurable per upstream to send gzipped requests (disabled by default)
upstreams:
- id: my-infura
jsonRpc:
enableGzip: false # gzip when sending requests to this upstream (disabled by default)
-
Upstream → eRPC: Automatically handles gzipped responses from upstreams when they send
Content-Encoding: gzip
-
eRPC → Client: Automatically enabled when clients send
Accept-Encoding: gzip
header (can be disabled in server config)
server:
enableGzip: true # gzip compression for responses to clients (enabled by default)
Using gzip can reduce ingress/egress bandwidth costs, and in certain cases (e.g. large RPC requests) it can improve performance.
Custom HTTP Headers
You can send additional headers (e.g. Authorization
) along with every outbound JSON-RPC request to an upstream by specifying jsonRpc.headers
in the config. This is especially useful for upstreams that require a static Bearer token for authentication.
upstreams:
- id: my-private-upstream
endpoint: https://private-provider.io/v1
jsonRpc:
# (OPTIONAL) Send additional headers to this upstream on every request
# e.g. Authorization bearer token, custom X-Header, etc.
headers:
Authorization: "Bearer SECRET_VALUE_123"
X-Custom-Header: "HelloWorld"
Client proxy pools
You define proxies for outgoing traffic from eRPC to upstreams. Proxy Pools enable centralized management of http(s)/socks5 proxies with round-robin load balancing across multiple upstreams. This is particularly useful for routing requests through different proxy servers based on geographic location or specific requirements (e.g., public vs private RPC endpoints).
# Define proxy pools at the root level
proxyPools:
- id: eu-dc1-pool
urls:
- http://proxy111.myorg.local:3128
- https://proxy222.myorg.local:3129
- id: us-dc1-pool
urls:
- http://proxy333.myorg.local:3128
- socks5://proxy444.myorg.local:3129
projects:
- id: main
# Option 1: Apply proxy pool to all upstreams
upstreamDefaults:
jsonRpc:
proxyPool: eu-dc1-pool
# Option 2: Apply proxy pools selectively to specific upstreams
upstreams:
- id: public-rpc-1
endpoint: https://public-rpc-1.example.com
jsonRpc:
proxyPool: eu-dc1-pool
- id: public-rpc-2
endpoint: https://public-rpc-2.example.com
jsonRpc:
proxyPool: us-dc1-pool
# This upstream won't use a proxy since it has no proxyPool specified
- id: private-rpc-1
endpoint: https://private-rpc-1.example.com
You can use upstreamDefaults
to apply a proxy pool to all upstreams, or configure them individually. Individual upstream configurations will override the defaults.