eth_getFilterChanges
https://api.triport.io/v1/polygonPolls a previously created server-side filter and returns everything that has matched since the last poll.
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.
filterIdstringrequiredResponse
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": [] }addressstringblockHashstringnull while pending.blockNumberstring0x-prefixed hex string. null while pending.datastringlogIndexstringnull while pending.removedbooleantrue if the log was removed due to a chain reorganization.topicsarraytopics[0] is usually the event signature hash.transactionHashstringnull while pending.transactionIndexstringnull while pending.Errors
| Code | Meaning | When it happens |
|---|---|---|
-32000 | filter not found | The filterId is unknown — never created, already uninstalled, or expired through inactivity. Recreate the filter and resume polling. |
-32001 | trial_expired | The account's trial period has ended. |
-32002 | tier_insufficient | The account's tier is below the required level for this method. |
-32003 | rate_limited | The per-tier RPS limit was exceeded. Back off and retry. |
-32004 | subscription_expired | The account's subscription has lapsed. |
-32005 | unauthorized | Missing or invalid API key. |
-32601 | method_unknown | The 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 blockNotes
- 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 foundmeans the ID is no longer valid; recreate the filter witheth_newBlockFilter(oreth_newFilter) and continue. - Filters are server-side resources. When you are done polling, release the
filter with
eth_uninstallFilterso it does not linger as an orphaned resource. eth_getFilterChangesis available on the free tier (polygon_read_rpccategory). 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.