etherscan-api
    Preparing search index...

    Examples

    Practical, copy-pasteable snippets for the most common tasks. Every call returns a promise resolving to an EtherscanResponse whose result is typed per endpoint.

    import { init } from 'etherscan-api';

    // Ethereum mainnet (default)
    const api = init('YourApiKey');

    // A testnet or L2, by name…
    const sepolia = init('YourApiKey', 'sepolia');
    const arbitrum = init('YourApiKey', 'arbitrum');

    // …or by numeric chainid (works for any of Etherscan's 60+ chains)
    const linea = init('YourApiKey', 59144);

    // With a custom timeout (ms)
    const slow = init('YourApiKey', 'mainnet', 30_000);

    Retired testnets throw immediately rather than silently hitting mainnet:

    init('YourApiKey', 'goerli');
    // Error: Chain "goerli" is no longer supported: Goerli was deprecated;
    // use Sepolia or Hoodi. ...
    // Single balance — result is a string (wei)
    const { result: wei } = await api.account.balance(
    '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae',
    );

    // Multiple balances — pass an array → result is { account, balance }[]
    const multi = await api.account.balance([
    '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae',
    '0x63a9975ba31b0b9626b34300f7f627147df1f526',
    ]);

    // Normal transactions — result is NormalTransaction[]
    const txs = await api.account.txlist(
    '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae',
    0, 'latest', 1, 100, 'desc',
    );
    for (const tx of txs.result ?? []) {
    console.log(tx.hash, tx.from, '→', tx.to, tx.value);
    }

    // ERC-20 transfers for an address (optionally filtered by token)
    const transfers = await api.account.tokentx(
    '0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae',
    '0x6b175474e89094c44da98b954eedeac495271d0f', // DAI
    );
    // Typed result: EthPrice
    const price = await api.stats.ethprice();
    console.log(price.result?.ethusd);

    // Typed result: GasOracle
    const gas = await api.gastracker.gasoracle();
    console.log('propose gas (gwei):', gas.result?.ProposeGasPrice);

    // Estimated confirmation time (seconds) for a given gas price (wei)
    const eta = await api.gastracker.gasestimate(2_000_000_000);
    const block = await api.proxy.eth_blockNumber();   // result: hex string
    const gasPrice = await api.proxy.eth_gasPrice(); // result: hex string
    const receipt = await api.proxy.eth_getTransactionReceipt(
    '0x1e2910a262b1008d0616a0beb24c1a491d78771baa54a33e66065e03b1f46bc1',
    );
    const logs = await api.log.getLogs(
    '0x33990122638b9132ca29c723bdf037f1c891a925',
    '379224', // fromBlock
    '400000', // toBlock
    '0xf63780e752c6a54a94fc52715dbc5518a3b4c3c2833d301a204226548a2a8545', // topic0
    );
    // ABI (JSON encoded as a string) and source for a verified contract
    const abi = await api.contract.getabi('0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413');
    const src = await api.contract.getsourcecode('0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413');
    console.log(src.result?.[0]?.ContractName);

    // Who created a contract, and in which tx
    const creation = await api.contract.getcontractcreation(
    '0xBB9bc244D798123fDe783fCc1C72d3Bb8C189413',
    );

    Verifying a contract (POST + poll)

    import { EtherscanError } from 'etherscan-api';

    const submit = await api.contract.verifysourcecode({
    contractaddress: '0xabc...',
    sourceCode: '// SPDX-License-Identifier: MIT\npragma solidity ^0.8.24; contract C {}',
    codeformat: 'solidity-single-file',
    contractname: 'C',
    compilerversion: 'v0.8.24+commit.e11b9ed9',
    optimizationUsed: 1,
    runs: 200,
    });

    const guid = submit.result; // string GUID
    if (!guid) throw new Error('Etherscan returned no verification GUID');

    // Etherscan reports every state except "Pass - Verified" with status "0", so
    // "Pending in queue" and "Fail - …" reject with an EtherscanError whose
    // `result` holds the status text. Read it from there.
    async function verificationStatus(guid: string): Promise<string> {
    try {
    return (await api.contract.checkverifystatus(guid)).result ?? '';
    } catch (err) {
    if (err instanceof EtherscanError && typeof err.result === 'string') {
    return err.result;
    }
    throw err;
    }
    }

    // Poll until done
    let status = await verificationStatus(guid);
    while (status === 'Pending in queue') {
    await new Promise((r) => setTimeout(r, 5000));
    status = await verificationStatus(guid);
    }
    console.log(status); // 'Pass - Verified' | 'Fail - Unable to verify' | …

    Logical API failures reject with an EtherscanError (a subclass of Error):

    import { EtherscanError } from 'etherscan-api';

    try {
    await api.contract.getabi('0xNotVerified...');
    } catch (err) {
    if (err instanceof EtherscanError) {
    console.error(err.message); // e.g. "Contract source code not verified"
    console.error(err.status); // raw API status, when present
    }
    }

    When Etherscan answers with a non-2xx HTTP status (a 429 rate limit, a 5xx), the default transport rejects with an EtherscanHttpError. It is a subclass of EtherscanError, so the check above catches it too, and it adds the statusCode and response headers needed for backoff. A rate limit can arrive either way, as a 200 with status: "0" or as a 429:

    import { EtherscanError, EtherscanHttpError } from 'etherscan-api';

    try {
    await api.stats.ethsupply();
    } catch (err) {
    if (err instanceof EtherscanHttpError && err.statusCode === 429) {
    const retryAfter = Number(err.headers['retry-after'] ?? 1); // seconds
    console.error(`rate limited, retry in ${retryAfter}s`);
    } else if (err instanceof EtherscanError) {
    console.error(err.message);
    }
    }

    An empty result (e.g. an address with no transactions) is not an error — Etherscan reports it as status: "0", but the client resolves it with an empty array:

    const res = await api.account.txlist('0xAddressWithNoHistory...');
    res.result; // [] — resolved, not thrown

    The library has no runtime dependencies — requests use Node's built-in https. To use your own networking (a proxy, retries, fetch), pass a transport as the 4th argument. method/body are only set for the POST verification endpoints.

    import { init, type Transport } from 'etherscan-api';

    const request: Transport = async (url, { timeout, method = 'GET', body } = {}) => {
    const res = await fetch(url, {
    method,
    body,
    headers: body ? { 'Content-Type': 'application/x-www-form-urlencoded' } : undefined,
    signal: AbortSignal.timeout(timeout ?? 10_000),
    });
    // fetch resolves on any HTTP status; surface non-2xx as an error.
    if (!res.ok) throw new Error(`Request failed with status code ${res.status}`);
    return res.json();
    };

    const api = init('YourApiKey', 'mainnet', 10_000, request);
    // Live list of supported chains and their chainids (the request carries no
    // API key, though init() still needs one to create the client)
    const chains = await api.usage.chainlist();

    // Your API key's usage and daily limit
    const limit = await api.usage.getapilimit();

    // Resolve a chain name to its id without creating a client
    import { resolveChainId } from 'etherscan-api';
    resolveChainId('base'); // 8453