TriportRPC

eth_getFilterChanges

POSThttps://api.triport.io/v1/polygon

Polls a previously created server-side filter and returns everything that has matched since the last poll.

Polygonpolygon_read_rpcfree 15 RPS · basic 20 RPS · pro 100 RPS · business 250 RPS

eth_getFilterChanges is the polling half of the stateful filter API. You first create a filter — with eth_newBlockFilter for new blocks or eth_newFilter for logs — and receive a filter ID. You then call eth_getFilterChanges with that ID, and the server returns the items that have appeared since your previous call (or since the filter was created, for the first poll). Each call advances the filter's cursor, so consecutive polls never return the same item twice.

This is the HTTP-based alternative to WebSocket subscriptions (eth_subscribe): instead of the server pushing updates to you, your client polls on an interval. On Polygon, where block time is roughly 2 seconds, a polling loop of about that interval keeps you close to the chain tip. The trade-off versus a WebSocket subscription is more requests and slightly higher latency, in exchange for working over plain request/response HTTP with no persistent connection.

The shape of the returned array depends on the type of filter the ID refers to: a block filter yields an array of 32-byte block hashes, while a log filter yields an array of log objects. A poll that finds nothing new returns an empty array — this is normal and not an error.

Filters are server-side resources with a limited lifetime. If a filter ID is unknown — never created, already removed with eth_uninstallFilter, or expired through inactivity — the call fails with -32000 filter not found. Treat that as a signal to recreate the filter and resume polling.

Parameters

Pass a positional array with exactly one element.

filterIdstringrequired
A 0x-prefixed hex filter ID returned by a prior eth_newBlockFilter or eth_newFilter call.

Response

For a block filter, the result is an array of new block hashes:

For a log filter, the result is an array of log objects:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": [
    {
      "address": "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270",
      "blockHash": "0x9c2b1a3f8e0d4c7b6a5f9e2d1c0b8a7f6e5d4c3b2a1f0e9d8c7b6a5f4e3d2c1b",
      "blockNumber": "0x2f1a3c5",
      "data": "0x0000000000000000000000000000000000000000000000008ac7230489e80000",
      "logIndex": "0x2",
      "removed": false,
      "topics": [
        "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"
      ],
      "transactionHash": "0x2d6a7b8c9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b",
      "transactionIndex": "0x1"
    }
  ]
}

A poll with nothing new since the last call returns an empty array:

{ "jsonrpc": "2.0", "id": 1, "result": [] }
addressstring
Address of the contract that emitted the log.
blockHashstring
Hash of the block containing the log. null while pending.
blockNumberstring
Block number as a 0x-prefixed hex string. null while pending.
datastring
Non-indexed event arguments, ABI-encoded.
logIndexstring
Index of this log within the block, hex-encoded. null while pending.
removedboolean
true if the log was removed due to a chain reorganization.
topicsarray
Indexed event arguments; topics[0] is usually the event signature hash.
transactionHashstring
Hash of the transaction that produced the log. null while pending.
transactionIndexstring
Index of the transaction within the block, hex-encoded. null while pending.

Errors

CodeMeaningWhen it happens
-32000filter not foundThe filterId is unknown — never created, already uninstalled, or expired through inactivity. Recreate the filter and resume polling.
-32001trial_expiredThe account's trial period has ended.
-32002tier_insufficientThe account's tier is below the required level for this method.
-32003rate_limitedThe per-tier RPS limit was exceeded. Back off and retry.
-32004subscription_expiredThe account's subscription has lapsed.
-32005unauthorizedMissing or invalid API key.
-32601method_unknownThe method is not available on this surface.

See errors.md for the full error envelope and handling guidance.

Examples

JavaScript (fetch)

async function poll(filterId) {
  const res = await fetch("https://api.triport.io/v1/polygon", {
    method: "POST",
    headers: {
      "Authorization": `Bearer ${process.env.TRIPORT_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      jsonrpc: "2.0",
      id: 1,
      method: "eth_getFilterChanges",
      params: [filterId],
    }),
  });


  const { result, error } = await res.json();
  if (error) {
    if (error.code === -32000) throw new Error("filter expired — recreate it");
    throw new Error(error.message);
  }
  return result; // array of block hashes or log objects, possibly empty
}


// Poll roughly once per Polygon block (~2s).
setInterval(async () => {
  const changes = await poll("0x1a4b0d2e9f7c8a6b5d3e1f0c2b4a6d8e");
  if (changes.length) console.log("new items:", changes.length);
}, 2000);

TypeScript SDK (@triport/sdk)

import { Triport } from "@triport/sdk";


const client = new Triport({ apiKey: process.env.TRIPORT_API_KEY! });


// Create a block filter, then poll it for new block hashes.
const filterId = await client.polygon.request<string>("eth_newBlockFilter", []);


async function tick() {
  const hashes = await client.polygon.request<string[]>(
    "eth_getFilterChanges",
    [filterId],
  );
  for (const hash of hashes) {
    console.log("new block:", hash);
  }
}


setInterval(tick, 2000);

Python (triport-sdk)

import os
import time
from triport import Triport


client = Triport(api_key=os.environ["TRIPORT_API_KEY"])


# Create a block filter, then poll it for new block hashes.
filter_id = client.polygon.request("eth_newBlockFilter", [])


while True:
    changes = client.polygon.request("eth_getFilterChanges", [filter_id])
    for item in changes:
        print("new item:", item)
    time.sleep(2)  # ~one Polygon block

Notes

  • The cursor advances on every call: each poll returns only what is new since the previous poll, so you never see the same item twice. Process the full array before polling again.
  • An empty array means "nothing new yet" — it is the expected result between blocks, not an error.
  • Filters expire after a period of inactivity. A -32000 filter not found means the ID is no longer valid; recreate the filter with eth_newBlockFilter (or eth_newFilter) and continue.
  • Filters are server-side resources. When you are done polling, release the filter with eth_uninstallFilter so it does not linger as an orphaned resource.
  • eth_getFilterChanges is available on the free tier (polygon_read_rpc category). Rate limiting is RPS-per-tier with burst; there is no daily quota.
  • For high-frequency log streams, polling can fall behind the chain tip; a WebSocket subscription is the lower-latency option where one is available.