При вивантаженні історії з блокчейн-експлорерів ми стикалися з жорсткими обмеженнями: Etherscan віддає максимум 10 000 записів на запит, 5 запитів на секунду на безкоштовному плані, немає стрімінгу. Якщо потрібно зібрати всі транзакції контракту USDT за останні три роки — це понад 30 мільйонів записів. Для аналізу DeFi-стратегій або відстеження транзакцій китів потрібні мільйони записів — стандартні API не справляються. Просте рішення не працює: пагінація через page не дасть більше 10 000. Потрібна стратегія, і ми її знайшли: пагінація по блоках з подальшою дедуплікацією.
У цій статті розберемо основні підходи: від прямого Etherscan API до використання Alchemy та Moralis, а також парсинг HTML і роботу з власною нодою. Кожен метод відрізняється за швидкістю, повнотою та вартістю. Наприклад, Alchemy дозволяє отримати всі транзакції адреси одним запитом без ліміту записів, а власна нода дає доступ до внутрішніх викликів.
Alchemy обходить Etherscan за всіма параметрами: у 5 разів вищий rate limit, немає ліміту записів завдяки pageKey, підтримка WebSocket стрімінгу та кросс-чейн запитів. Для проектів, що вимагають максимальної продуктивності, Alchemy — безальтернативний вибір.
Якщо вам потрібно зібрати історичні дані для аналізу — зв'яжіться з нами, ми підберемо оптимальний інструмент за один день.
Як обійти обмеження Etherscan API у 10 000 записів?
Etherscan надає параметри startblock та endblock. Пагінація по блоках дозволяє витягнути всі транзакції. Код нижче перебирає блоки, збільшуючи startblock до останнього зустрінутого:
import httpx import asyncio from typing import AsyncGenerator async def get_all_transactions( address: str, api_key: str, start_block: int = 0 ) -> AsyncGenerator[dict, None]: """Вивантаження ВСІХ транзакцій адреси через пагінацію по блоках""" base_url = "https://api.etherscan.io/api" current_block = start_block while True: async with httpx.AsyncClient() as client: resp = await client.get(base_url, params={ "module": "account", "action": "txlist", "address": address, "startblock": current_block, "endblock": 99999999, "sort": "asc", "apikey": api_key, "offset": 10000, "page": 1, }) data = resp.json() if data["status"] != "1" or not data["result"]: break txs = data["result"] for tx in txs: yield tx if len(txs) < 10000: break current_block = int(txs[-1]["blockNumber"]) + 1 await asyncio.sleep(0.2) Важливий нюанс: якщо в одному блоці > 10 000 транзакцій на адресу — цикл зависне. Для таких випадків потрібна вкладена пагінація з параметром page всередині блоку.
Чому варто використовувати Alchemy або Moralis замість Etherscan?
Alchemy та Moralis знімають більшість обмежень Etherscan. Alchemy має rate limit 25 req/s, що в 5 разів перевищує ліміт Etherscan (5 req/s). Ось порівняння:
| Параметр | Etherscan (free) | Alchemy (free tier) | Moralis (free tier) |
|---|---|---|---|
| Ліміт записів | 10000 | Немає (pageKey) | Немає (cursor) |
| Rate limit | 5 req/s | 25 req/s | 100 requests/min |
| Streaming | Немає | WebSocket (Enhanced API) | WebSocket (Real-time) |
| Кросс-чейн | Немає | Так | Так |
Alchemy getAssetTransfers повертає ETH + ERC-20 + ERC-721 одним викликом.
import { Alchemy, Network } from 'alchemy-sdk'; const alchemy = new Alchemy({ apiKey: process.env.ALCHEMY_KEY, network: Network.ETH_MAINNET }); const transfers = await alchemy.core.getAssetTransfers({ fromAddress: '0x...', category: ['external', 'erc20', 'erc721', 'erc1155'], withMetadata: true, maxCount: 1000, }); if (transfers.pageKey) { const more = await alchemy.core.getAssetTransfers({ pageKey: transfers.pageKey, }); } Alchemy Asset Transfers API рекомендує використовувати pageKey для пагінації. Moralis додатково віддає внутрішні транзакції через getWalletTransactionsVerbose. Для швидкої оцінки масштабу зв'яжіться з нами — ми підготуємо прототип за 2 дні.
Коли парсинг HTML-сторінок виправданий?
Якщо API не віддає власників токенів або verified-контракти, використовуємо парсинг HTML. Наступний приклад збирає власників токена з Etherscan:
import httpx from bs4 import BeautifulSoup import asyncio async def get_token_holders(token_address: str, pages: int = 10) -> list[dict]: headers = { "User-Agent": "Mozilla/5.0", } holders = [] async with httpx.AsyncClient(headers=headers) as client: for page in range(1, pages + 1): resp = await client.get( f"https://etherscan.io/token/{token_address}", params={"a": "#holders", "p": page} ) soup = BeautifulSoup(resp.text, 'html.parser') table = soup.find('table', {'id': 'holdersTable'}) if not table: break for row in table.find_all('tr')[1:]: cols = row.find_all('td') if len(cols) >= 3: holders.append({ 'rank': cols[0].text.strip(), 'address': cols[1].find('a')['href'].split('/')[-1], 'quantity': cols[2].text.strip(), }) await asyncio.sleep(2) return holders Etherscan захищається Cloudflare — для об'ємного збору потрібні резидентні проксі або офіційне API. Парсинг BscScan та Solscan працює за аналогією.
Як уникнути дублів при паралельному зборі?
При паралельному зборі з кількох джерел дублі неминучі. Використовуємо ON CONFLICT в PostgreSQL:
CREATE TABLE eth_transactions ( tx_hash CHAR(66) PRIMARY KEY, block_number BIGINT NOT NULL, from_address CHAR(42) NOT NULL, to_address CHAR(42), value NUMERIC(38) DEFAULT 0, gas_used BIGINT, status SMALLINT, ts TIMESTAMPTZ ); INSERT INTO eth_transactions VALUES (...) ON CONFLICT (tx_hash) DO NOTHING; Для подій унікальний ключ — (tx_hash, log_index).
Пряма робота з нодою
Для максимальної повноти (внутрішні транзакції, storage slots, MEV) ставимо власну ноду з Erigon та --tracing. Це дає:
- Всі internal transactions без лімітів
- Трасування викликів
- Дані з storage
curl -X POST $ETH_RPC_URL \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_getBlockReceipts","params":["0x1234567"],"id":1}' eth_getBlockReceipts EIP-1559 повертає всі receipts блока одним запитом — альтернатива N окремих викликів eth_getTransactionReceipt.
Які етапи включає збір даних?
| Етап | Опис | Строк |
|---|---|---|
| Аналіз джерел | Визначення необхідних даних та їх розташування (API, HTML, нода) | 1-2 дні |
| Вибір інструментів | Etherscan API, Alchemy, Moralis або власна нода | 1 день |
| Написання парсера | Асинхронний збір з урахуванням rate limit та пагінації | 2-5 днів |
| Дедуплікація та нормалізація | Склеювання даних в єдину схему | 1-2 дні |
| Тестування на реальному обсязі | Мільйони транзакцій, перевірка на пропуски | 2-3 дні |
| Деплой | Контейнеризація, моніторинг, автоматичний перезапуск | 1-2 дні |
Наш процес займає від 3 до 10 днів залежно від складності.
Що входить в результат
- Документація за схемою даних та методами збору.
- Готовий код на Python/TypeScript з підтримкою асинхронності.
- Налаштування пагінації під кожен експлорер.
- Деплой на сервер або в хмару (AWS Lambda, Kubernetes).
- Навчання команди роботі з системою.
- Підтримка протягом місяця після здачі.
Наш досвід — понад 30 проектів зі збору та аналізу даних on-chain. Отримайте консультацію — ми допоможемо вибрати оптимальний інструмент під ваше завдання. Замовте проект, і ми налаштуємо збір даних будь-якої складності.







