Shown once
A key is displayed at creation and never again — we store only a hash. Lost it? Revoke and make a new one; that takes five seconds and is safer than a system that can show you your own secret.
Docs
Standard JSON-RPC over HTTPS. If your code already talks to a node, the only thing that changes is the URL.
Create an account, make a key, send a request. Nothing else to install.
curl https://rpc.phantrium.com/v1/ethereum/YOUR_KEY \
-X POST \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]}'
import { createPublicClient, http } from 'viem'
import { mainnet } from 'viem/chains'
const client = createPublicClient({
chain: mainnet,
transport: http('https://rpc.phantrium.com/v1/ethereum/YOUR_KEY')
})
const block = await client.getBlockNumber()
import { JsonRpcProvider } from 'ethers'
const provider = new JsonRpcProvider(
'https://rpc.phantrium.com/v1/base/YOUR_KEY'
)
Prefer to keep the key out of the URL? Send it as a header instead:
Authorization: Bearer YOUR_KEY against https://rpc.phantrium.com/v1/ethereum.
Use the header in production. A key in the path travels through every proxy, browser history and log
between you and us. We strip it from our own access logs, but we are not the only hop, and we would rather tell you
that than let the shorter example look free of consequence.
One URL shape for every chain: https://rpc.phantrium.com/v1/<chain>/<key>
| Chain | Slug | Chain ID | Type |
|---|---|---|---|
| Ethereum | ethereum | 0x1 · 1 | L1 |
| Polygon PoS | polygon | 0x89 · 137 | L1 |
| Base | base | 0x2105 · 8453 | L2 |
| Arbitrum One | arbitrum | 0xa4b1 · 42161 | L2 |
Batch requests are supported — send an array, get an array back, up to 50 calls per batch. Each call in the batch is metered separately, because each one costs us separately.
A key is displayed at creation and never again — we store only a hash. Lost it? Revoke and make a new one; that takes five seconds and is safer than a system that can show you your own secret.
Restrict a key to your domains or IP addresses from the dashboard. A key that only works from your own backend is worth much less to whoever finds it in a bundle.
Revoking takes effect within 30 seconds everywhere — that is the lifetime of our key cache, and we would rather tell you the number than let you guess it.
We bill units, not requests. An eth_getLogs over 200,000 blocks is not the same work as an eth_chainId, and pricing that pretends otherwise ends in an invoice nobody can explain.
| Units | Methods | Why |
|---|---|---|
| 1 | eth_chainId, eth_blockNumber, eth_gasPrice, net_version, web3_clientVersion | Answered from memory |
| 2 | eth_getBalance, eth_getCode, eth_getStorageAt, eth_getTransactionCount, eth_getBlockByNumber, eth_getTransactionByHash, eth_getTransactionReceipt | One state lookup |
| 3 | eth_feeHistory | Reads a window of blocks |
| 4 | eth_call, eth_sendRawTransaction | Executes or broadcasts |
| 6 | eth_estimateGas, eth_createAccessList | Executes repeatedly to find a bound |
| 10 + range | eth_getLogs, eth_getFilterLogs | +5 units per 1,000 blocks scanned, capped at +500 |
| 60–100 | debug_trace*, trace_* | Replays execution; by far the most expensive thing you can ask a node |
| 4 | anything not listed | Default weight |
Every response carries X-Chain-Units for that call and X-Chain-Quota-Remaining for the month, so you can meter yourself without waiting for an invoice.
Methods that touch node-local accounts or administration — eth_accounts, eth_sign, personal_*, miner_*, admin_* — are refused, not proxied. A shared endpoint has no accounts to unlock.
Two separate limits: how fast you may ask, and how much you may use in a month.
| Plan | Units / month | Requests / second | Active keys | Price |
|---|---|---|---|---|
| Beta | 1,000,000 | 5 | 3 | free |
| Starter | 25,000,000 | 25 | 10 | €29 / month |
| Growth | 150,000,000 | 100 | 30 | €99 / month |
| Scale | 600,000,000 | 300 | 100 | €299 / month |
At 100% of your monthly units the gateway returns HTTP 429 with the numbers in the body. There is no overage rate, because an invoice you did not agree to is not a business model.
You get an email at 80%. Move up a plan and the limit lifts immediately — no waiting for a sales call.
On Ethereum your call is answered by a node we run ourselves, and the answer is checked before you get it. X-Chain-Verified tells you whether that happened.
| Header value | What it means |
|---|---|
yes | Our own Helios light client produced this answer and checked it against a chain header it verified itself through the beacon sync committee. |
no | A vetted upstream answered. Either the chain has no Phantrium node yet (Polygon, Base, Arbitrum), or the method is one our light client does not implement, or our node was out of rotation at that moment. |
partial | A batch in which some calls were verified and others were not. |
Methods our light client does not implement — eth_feeHistory, debug_*, trace_* — are forwarded to an upstream and answered normally. You lose the verification, not the call. The header will never say yes for an answer we could not prove; if our node is down or lagging behind the chain head it is taken out of rotation and the header drops to no. What the node is, and is not.
Answers that cannot change any more are served from our cache. It makes your calls faster and our margin real — and X-Chain-Cache: HIT tells you when it happened. A cached answer keeps the verification status it had when it was stored, so X-Chain-Verified stays truthful on a cache hit.
| Method | Cached for | Reason |
|---|---|---|
eth_chainId, net_version | 1 hour | Constant for the life of the chain |
eth_getBlockByNumber (fixed number) | 10 minutes | A finalised block does not change |
eth_getBlockByHash | 10 minutes | Addressed by content |
eth_getTransactionByHash, eth_getTransactionReceipt | 30 seconds | Short on purpose — a reorg must not catch us |
eth_blockNumber | 2 seconds | Cheaper than a stampede, fresh enough to be true |
eth_call, eth_getLogs, anything with latest | not cached | The answer depends on the head |
Cached responses still count units. The cache exists to make your latency and our cost better, not to make metering ambiguous.
HTTP status for anything about your account; JSON-RPC error for anything about the chain.
| Status | Means | Do |
|---|---|---|
| 401 | Key missing, wrong, or revoked | Check the key in the dashboard |
| 403 | Key is restricted to other domains or IPs | Widen the restriction, or call from the right place |
| 429 | Rate limit or monthly quota reached — the body says which | Back off, or change plan |
| 404 | Unknown chain slug | See the endpoint table above |
| 413 | Body over 1 MB | Split the batch |
| 200 + JSON-RPC error | The chain rejected the call — a revert, bad params, a missing block | That is an answer, not an outage: it is billed and not retried |
If no backend answers, you get JSON-RPC -32603 and we get an alert. Check the status page before you start debugging your own code.