Skip to content

Instantly share code, notes, and snippets.

@prettyirrelevant
Last active January 19, 2026 14:33
Show Gist options
  • Select an option

  • Save prettyirrelevant/bf496ae490767cb1c91a6adae3d01071 to your computer and use it in GitHub Desktop.

Select an option

Save prettyirrelevant/bf496ae490767cb1c91a6adae3d01071 to your computer and use it in GitHub Desktop.
Test Etherscan eth_call URL limits for token balance queries
#!/usr/bin/env python3
"""
Test max token addresses for Etherscan eth_call before HTTP 414.
Usage: python test_etherscan_url_limit.py --api-key YOUR_KEY
Or set ETHERSCAN_API_KEY env variable.
"""
import argparse
import os
import sys
import time
import requests
from eth_abi import encode
ETHERSCAN_API_URL = 'https://api.etherscan.io/v2/api'
BALANCE_SCANNER_ADDRESS = '0x54eCF3f6f61F63fdFE7c27Ee8A86e54899600C92'
TOKENS_BALANCE_SELECTOR = '0xe5da1b68' # tokensBalance(address,address[])
TEST_WALLET = '0x0000000000000000000000000000000000000001'
FAKE_TOKEN = '0x0000000000000000000000000000000000000002'
def encode_tokens_balance_call(wallet: str, num_tokens: int) -> str:
wallet_addr = bytes.fromhex(wallet[2:])
token_addrs = [bytes.fromhex(FAKE_TOKEN[2:])] * num_tokens
encoded_args = encode(['address', 'address[]'], [wallet_addr, token_addrs])
return TOKENS_BALANCE_SELECTOR + encoded_args.hex()
def test_etherscan_call(api_key: str, num_tokens: int, chain_id: int = 1) -> tuple[int, int, str]:
call_data = encode_tokens_balance_call(TEST_WALLET, num_tokens)
params = {
'module': 'proxy',
'action': 'eth_call',
'to': BALANCE_SCANNER_ADDRESS,
'data': call_data,
'tag': 'latest',
'apikey': api_key,
'chainid': str(chain_id),
}
req = requests.Request('GET', ETHERSCAN_API_URL, params=params)
prepared = req.prepare()
url_length = len(prepared.url) if prepared.url else 0
try:
response = requests.get(ETHERSCAN_API_URL, params=params, timeout=30)
return response.status_code, url_length, response.text
except requests.RequestException as e:
return -1, url_length, str(e)
def find_max_tokens(api_key: str, start: int = 10, end: int = 150, chain_id: int = 1) -> None:
print(f'Testing Etherscan eth_call URL limits (chain_id={chain_id})...\n')
print(f'{"Tokens":<10} {"Status":<20} {"URL Length":<12} {"Data Length":<12}')
print('-' * 60)
last_success = 0
first_failure = end + 1
test_values = [10, 25, 50, 75, 100, 110, 120, 130, 140, 150]
for num_tokens in test_values:
if num_tokens > end:
break
call_data = encode_tokens_balance_call(TEST_WALLET, num_tokens)
data_length = len(call_data)
status, url_length, text = test_etherscan_call(api_key, num_tokens, chain_id)
if status == 200:
if 'not supported' in text.lower() or 'upgrade' in text.lower():
status_str = '⚠ needs paid key'
elif 'execution reverted' in text.lower() or 'result' in text.lower():
# execution reverted means the request got through (contract just can't find fake tokens)
status_str = '✓'
last_success = max(last_success, num_tokens)
else:
status_str = '✓'
last_success = max(last_success, num_tokens)
elif status == 414:
status_str = '✗ 414 URI too long'
first_failure = min(first_failure, num_tokens)
elif status == 404:
status_str = '✗ 404'
first_failure = min(first_failure, num_tokens)
elif status == 400:
status_str = '✗ 400'
first_failure = min(first_failure, num_tokens)
elif status == 429:
status_str = '⚠ 429 rate limit'
else:
status_str = f'? {status}'
print(f'{num_tokens:<10} {status_str:<20} {url_length:<12} {data_length:<12}')
time.sleep(0.2)
if last_success > 0 and last_success < first_failure - 1:
print(f'\nBinary search between {last_success} and {first_failure}...\n')
low, high = last_success, first_failure
while low < high - 1:
mid = (low + high) // 2
call_data = encode_tokens_balance_call(TEST_WALLET, mid)
data_length = len(call_data)
status, url_length, text = test_etherscan_call(api_key, mid, chain_id)
is_success = status == 200 and 'not supported' not in text.lower() and 'upgrade' not in text.lower()
if is_success:
status_str = f'{status} ✓'
low = mid
else:
status_str = f'{status} ✗'
high = mid
print(f'{mid:<10} {status_str:<20} {url_length:<12} {data_length:<12}')
time.sleep(0.2)
last_success = low
print('\n' + '=' * 60)
if last_success > 0:
print(f'Maximum safe number of tokens: {last_success}')
print(f'Recommended value (with margin): {max(1, last_success - 10)}')
call_data = encode_tokens_balance_call(TEST_WALLET, last_success)
_, url_length, _ = test_etherscan_call(api_key, last_success, chain_id)
print(f'\nAt {last_success} tokens:')
print(f' - Total URL length: {url_length} chars')
print(f' - Data param length: {len(call_data)} chars')
else:
print('Could not determine max tokens (API may require paid key)')
def main() -> int:
parser = argparse.ArgumentParser(description='Test Etherscan eth_call URL limits')
parser.add_argument('--api-key', help='Etherscan API key')
parser.add_argument('--chain-id', type=int, default=1, help='Chain ID (default: 1)')
parser.add_argument('--start', type=int, default=10, help='Start number of tokens')
parser.add_argument('--end', type=int, default=150, help='End number of tokens')
args = parser.parse_args()
api_key = args.api_key or os.environ.get('ETHERSCAN_API_KEY')
if not api_key:
print('Error: No API key. Use --api-key or set ETHERSCAN_API_KEY env variable.')
return 1
find_max_tokens(api_key, args.start, args.end, args.chain_id)
return 0
if __name__ == '__main__':
sys.exit(main())
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment