Home
How to migrate to NOWNodes

Migrating from QuickNode to NOWNodes

This tutorial shows how to move from QuickNode to NOWNodes without turning the migration into a full rewrite. Standard RPC requests usually need only a new endpoint and a new authentication method. QuickNode-specific products, such as Streams, Webhooks, Marketplace Add-ons, IPFS, KV storage, and Solana gRPC, should be migrated separately.

This tutorial shows how to move from QuickNode to NOWNodes without turning the migration into a full rewrite. Standard RPC requests usually need only a new endpoint and a new authentication method. QuickNode-specific products, such as Streams, Webhooks, Marketplace Add-ons, IPFS, KV storage, and Solana gRPC, should be migrated separately.

1. Start with a Quick Audit

Before changing code, list what your app currently uses in QuickNode.

Check for:

  • HTTP RPC endpoints, usually ending with quiknode.pro/{token}/;
  • WSS endpoints;
  • standard methods such as eth_blockNumber, eth_getBalance, eth_call, eth_getLogs, eth_sendRawTransaction;
  • debug or trace methods such as debug_traceTransaction;
  • QuickNode Streams;
  • QuickNode Webhooks;
  • Marketplace Add-ons;
  • REST APIs that use x-api-key;
  • Solana gRPC, IPFS, or KV storage;
  • current request rate, errors, and alerts.

Search your codebase for:

Copied!
quiknode.pro
QUICKNODE
x-api-key
@quicknode/sdk
debug_trace
trace_

The main goal of this audit is to separate standard RPC usage from QuickNode-specific features.

2. What Moves Directly

These standard RPC methods can usually be sent to NOWNodes without changing the JSON-RPC body:

MethodMigration
eth_chainIdDirect
eth_blockNumberDirect
eth_getBalanceDirect
eth_callDirect
eth_estimateGasDirect
eth_getLogsDirect, but check block ranges
eth_getTransactionReceiptDirect
eth_sendRawTransactionDirect, but test nonce and gas behavior
debug_traceTransactionCheck NOWNodes Trace & Debug support first

QuickNode often puts the token in the URL:

Copied!
https://your-endpoint.quiknode.pro/YOUR_QUICKNODE_TOKEN/

NOWNodes uses a network endpoint plus the api-key header:

Copied!
https://eth.nownodes.io/
api-key: YOUR_NOWNODES_API_KEY

3. What Does Not Move Directly

Do not treat these as simple URL replacements:

QuickNode featureWhat to do instead
StreamsReplace with WSS + worker, eth_getLogs, Blockbook, or your own indexer
WebhooksUse NOWNodes Webhooks if available, or build a small notification worker
Marketplace Add-onsCheck each add-on separately and replace its custom API/methods
REST APIs with x-api-keyReplace with the matching NOWNodes API or another service
IPFSMove to an IPFS provider or self-hosted IPFS
KV storageMove to your app database or managed KV
Solana gRPCKeep temporarily or move to a compatible gRPC provider

Move standard RPC first. Migrate these product-specific features after the core provider switch is stable.

4. Endpoint Mapping

QuickNode endpoint names are custom. Use your actual dashboard URL as the source and switch to the matching NOWNodes network endpoint.

NetworkQuickNode patternNOWNodes HTTPNOWNodes WSS
Ethereum Mainnethttps://{endpoint}.quiknode.pro/{token}/https://eth.nownodes.io/wss://eth.nownodes.io/wss/{NOWNODES_API_KEY}
Ethereum SepoliaQuickNode dashboard URLhttps://eth-sepolia.nownodes.io/Check NOWNodes docs
Polygon MainnetQuickNode dashboard URLhttps://matic.nownodes.io/wss://matic.nownodes.io/wss/{NOWNODES_API_KEY}
Base MainnetQuickNode dashboard URLhttps://base.nownodes.io/wss://base.nownodes.io/wss/{NOWNODES_API_KEY}
Arbitrum OneQuickNode dashboard URLhttps://arbitrum.nownodes.io/Check NOWNodes docs
Optimism MainnetQuickNode dashboard URLhttps://optimism.nownodes.io/wss://optimism.nownodes.io/wss/{NOWNODES_API_KEY}
BNB Smart ChainQuickNode dashboard URLhttps://bsc.nownodes.io/wss://bsc.nownodes.io/wss/{NOWNODES_API_KEY}
Solana MainnetQuickNode dashboard URLhttps://sol.nownodes.io/wss://sol.nownodes.io/wss/{NOWNODES_API_KEY}

Always verify the network with eth_chainId or the chain-specific equivalent before sending real traffic.

5. Configure Environment Variables

Before:

Copied!
QUICKNODE_ETH_RPC_URL=https://your-endpoint.quiknode.pro/YOUR_QUICKNODE_TOKEN/
QUICKNODE_ETH_WSS_URL=wss://your-endpoint.quiknode.pro/YOUR_QUICKNODE_TOKEN/

After:

Copied!
NOWNODES_ETH_RPC_URL=https://eth.nownodes.io/
NOWNODES_ETH_WSS_URL=wss://eth.nownodes.io/wss/YOUR_NOWNODES_API_KEY
NOWNODES_API_KEY=YOUR_NOWNODES_API_KEY

During rollout, keep both providers available:

Copied!
RPC_PROVIDER=nownodes
QUICKNODE_ETH_RPC_URL=https://your-endpoint.quiknode.pro/YOUR_QUICKNODE_TOKEN/
NOWNODES_ETH_RPC_URL=https://eth.nownodes.io/
NOWNODES_API_KEY=YOUR_NOWNODES_API_KEY

This makes rollback a configuration change instead of another code change.

6. Test the New RPC Endpoint

First, check the current QuickNode response.

Copied!
curl "https://your-endpoint.quiknode.pro/$QUICKNODE_TOKEN/" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

Then run the same request through NOWNodes.

Copied!
curl "https://eth.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'

Check:

  • both responses return result;
  • NOWNodes is close to QuickNode by block number;
  • no 401, 403, 429, or 5xx errors appear;
  • request latency is acceptable.

Also verify the network:

Copied!
curl "https://eth.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","method":"eth_chainId","params":[],"id":1}'

For Ethereum Mainnet, the expected result is 0x1.

7. Update Application Code

Direct fetch

Before:

Copied!
const response = await fetch(process.env.QUICKNODE_ETH_RPC_URL, {
  method: "POST",
  headers: {
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    method: "eth_getBalance",
    params: [address, "latest"],
    id: 1
  })
});

After:

Copied!
const response = await fetch(process.env.NOWNODES_ETH_RPC_URL, {
  method: "POST",
  headers: {
    "api-key": process.env.NOWNODES_API_KEY,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    method: "eth_getBalance",
    params: [address, "latest"],
    id: 1
  })
});

viem

Copied!
import { createPublicClient, http } from "viem";
import { mainnet } from "viem/chains";

export const client = createPublicClient({
  chain: mainnet,
  transport: http(process.env.NOWNODES_ETH_RPC_URL, {
    fetchOptions: {
      headers: {
        "api-key": process.env.NOWNODES_API_KEY
      }
    }
  })
});

Small RPC Wrapper

Use a wrapper if your provider library does not pass custom headers cleanly.

Copied!
export async function rpc(method, params = []) {
  const response = await fetch(process.env.NOWNODES_ETH_RPC_URL, {
    method: "POST",
    headers: {
      "api-key": process.env.NOWNODES_API_KEY,
      "Content-Type": "application/json"
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: Date.now(),
      method,
      params
    })
  });

  if (!response.ok) {
    const error = new Error(`RPC HTTP error: ${response.status}`);
    error.status = response.status;
    throw error;
  }

  const payload = await response.json();

  if (payload.error) {
    const error = new Error(payload.error.message);
    error.status = 200;
    error.code = payload.error.code;
    throw error;
  }

  return payload.result;
}

8. Migrate Common Flows

Balances

Use the same eth_getBalance body and change only endpoint/auth.

Copied!
curl "https://eth.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "latest"],
    "id": 1
  }'

Check that the result is hex wei and your UI still formats it correctly.

Contract Reads

Use the same eth_call body. If the call targets an old block, use archive access.

Copied!
curl "https://eth.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_call",
    "params": [
      {
        "to": "0x0000000000000000000000000000000000000000",
        "data": "0x"
      },
      "latest"
    ],
    "id": 1
  }'

Check ABI decoding and revert handling.

Transaction Broadcasting

Use the same eth_sendRawTransaction body.

Copied!
curl "https://eth.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_sendRawTransaction",
    "params": ["0xSIGNED_TRANSACTION"],
    "id": 1
  }'

Check chainId, nonce behavior, fee settings, and retry logic before sending production transactions.

Event Indexing

Use eth_getLogs, but avoid very large block ranges.

Copied!
curl "https://eth.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getLogs",
    "params": [
      {
        "fromBlock": "0x12D687",
        "toBlock": "latest",
        "address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
        "topics": [
          "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
        ]
      }
    ],
    "id": 1
  }'

Use checkpoints and split large ranges into smaller chunks. After WebSocket reconnects, backfill missed blocks with eth_getLogs.

9. Move WebSocket Subscriptions

QuickNode:

Copied!
wscat -c "wss://your-endpoint.quiknode.pro/$QUICKNODE_TOKEN/"

NOWNodes:

Copied!
wscat -c "wss://eth.nownodes.io/wss/$NOWNODES_API_KEY"

Subscribe to new blocks:

Copied!
{"jsonrpc":"2.0","id":1,"method":"eth_subscribe","params":["newHeads"]}

Check:

  • the connection opens;
  • a subscription id is returned;
  • events arrive continuously;
  • reconnect works;
  • subscriptions are recreated after reconnect;
  • missed blocks are backfilled.

10. Replace QuickNode-Specific Features

Streams

QuickNode Streams are managed pipelines. Replace them with a small worker:

Copied!
NOWNodes RPC/WSS
-> indexing worker
-> filter
-> database / webhook / queue / storage

Use:

  • WSS newHeads for new block signals;
  • eth_getLogs for contract events;
  • Blockbook for address or transaction history where suitable;
  • your existing destination, such as PostgreSQL, Kafka, S3, or webhook.

Keep the payload shape stable if downstream services depend on the old Stream output.

Webhooks

QuickNode Webhooks are not regular RPC calls. Replace them with:

  • NOWNodes Webhooks, if the required trigger is available;
  • WSS + worker;
  • polling with eth_getLogs;
  • temporary QuickNode fallback while the notification worker is being built.

Use transaction hash + log index as an idempotency key for EVM events.

Marketplace Add-ons

Check every enabled add-on. Some add standard methods, but others expose custom RPC methods or REST APIs.

For each add-on, record:

  • method or URL used by the app;
  • response fields the app reads;
  • replacement in NOWNodes, Blockbook, MarketData, or another API;
  • parser changes required by the new response shape.

Do not send custom add-on methods to NOWNodes unless support is verified.

Solana RPC

QuickNode:

Copied!
curl "https://your-solana-endpoint.quiknode.pro/$QUICKNODE_TOKEN/" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBalance",
    "params": ["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"]
  }'

NOWNodes:

Copied!
curl "https://sol.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "getBalance",
    "params": ["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri"]
  }'

If your app uses Solana gRPC, treat it as a separate migration. Solana JSON-RPC and Yellowstone-compatible gRPC are different interfaces.

11. Archive, Trace, and Debug

Use archive access when the app reads old block state, runs historical eth_call, or indexes from old ranges.

Ethereum archive endpoint example:

Copied!
https://eth-archive.nownodes.io/

Historical balance example:

Copied!
curl "https://eth-archive.nownodes.io/" \
  -X POST \
  -H "api-key: $NOWNODES_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "method": "eth_getBalance",
    "params": ["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", "0xA00000"],
    "id": 1
  }'

For trace/debug methods:

  1. List every method currently used.
  2. Check NOWNodes Trace & Debug support for the same chain.
  3. Test known transaction hashes.
  4. Compare response fields before switching parsers.

12. Retries and Rate Limits

QuickNode may return 429 when traffic exceeds plan limits and 413 when payloads or ranges are too large. NOWNodes limits depend on the selected plan.

Before production:

  • estimate current request volume from QuickNode usage;
  • identify heavy calls such as eth_getLogs, debug methods, and batch requests;
  • add 30-50% headroom above current peak usage;
  • split large log ranges;
  • add retry with backoff for read requests;
  • add alerts for 429 and 5xx.

fn must throw errors with status or code fields. withRetry does not inspect HTTP responses directly. If fn uses fetch, check response.ok inside fn and throw a normalized error before returning the parsed JSON.

Copied!
async function withRetry(fn, options = {}) {
  const maxAttempts = options.maxAttempts ?? 5;
  const baseDelayMs = options.baseDelayMs ?? 500;
  const maxDelayMs = options.maxDelayMs ?? 8000;

  let lastError;

  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;

      const retryable =
        error.status === 429 ||
        error.status >= 500 ||
        error.code === "ETIMEDOUT" ||
        error.code === "ECONNRESET";

      if (!retryable || attempt === maxAttempts) {
        throw error;
      }

      const jitter = Math.floor(Math.random() * 250);
      const delay = Math.min(baseDelayMs * 2 ** (attempt - 1) + jitter, maxDelayMs);
      await new Promise((resolve) => setTimeout(resolve, delay));
    }
  }

  throw lastError;
}

For eth_sendRawTransaction, retry carefully. Check transaction hash, nonce, and receipt before sending again.

13. Production Rollout

Use a phased rollout:

  1. Test NOWNodes locally.
  2. Move staging standard RPC reads.
  3. Compare selected production reads in shadow mode.
  4. Move 5-10% of production read traffic.
  5. Move all read traffic.
  6. Move transaction broadcasting.
  7. Move WebSocket/indexers after reconnect and backfill tests.
  8. Migrate Streams, Webhooks, and Add-ons separately.

Feature flag example:

Copied!
const provider = process.env.RPC_PROVIDER;

const rpcConfig =
  provider === "nownodes"
    ? {
        url: process.env.NOWNODES_ETH_RPC_URL,
        headers: {
          "api-key": process.env.NOWNODES_API_KEY
        }
      }
    : {
        url: process.env.QUICKNODE_ETH_RPC_URL,
        headers: {
          "Content-Type": "application/json"
        }
      };

Rollback:

  1. Set RPC_PROVIDER=quicknode.
  2. Restart affected services.
  3. Check eth_blockNumber.
  4. Check transaction broadcasting.
  5. Check indexer lag and WebSocket subscriptions.
  6. Keep the NOWNodes config until the issue is diagnosed.

14. Troubleshooting

SymptomCauseFix
401 UnauthorizedMissing or invalid api-key headerSend api-key: $NOWNODES_API_KEY
Wrong chainIdWrong network endpointUse the correct NOWNodes endpoint
method not foundQuickNode add-on method sent to NOWNodesReplace the add-on method separately
413 Content Too LargeRequest or block range is too largeSplit the request into smaller chunks
429 Too Many RequestsRate limit exceededAdd throttling/backoff or upgrade the plan
Empty eth_getLogsWrong filter, old range, or archive issueCheck topics, split ranges, use archive
WSS disconnectsNetwork interruption or endpoint limitsReconnect and resubscribe automatically
Missed eventsNo backfill after reconnectBackfill with eth_getLogs
Duplicate Stream/Webhook eventsNo idempotency keyUse transaction hash + log index

15. Final Checklist

Before removing QuickNode fallback, confirm:

  • RPC and WSS work via NOWNodes, including broadcasting, reconnect, and backfill.
  • On-chain data is intact: balances, logs, contract reads, transaction history, and indexer events.
  • QuickNode-specific features are replaced or fallback-ready: Streams, Webhooks, IPFS, KV, gRPC, and Marketplace Add-ons.
  • Monitoring is stable and rollback has been tested.
  • NOWNodes API key is secured; revoke unused QuickNode tokens after the observation period.