Golang client for Blockscout explorers.
- REST API v2 with typed requests/responses and
context.Context - Typed query params for filters, search, pagination, advanced filters, and account abstraction
- Zero runtime dependencies; transport uses the Go standard library
- Legacy Etherscan-compatible API surface with context-aware variants
- ETH RPC helper methods
While there are several explorers available to blockchain projects, most are closed systems (ie Etherscan, Etherchain). Blockscout provides a much needed open-source alternative.
go get github.com/PositiveSecurity/blockscout-go-api
Supported Go version: Go 1.22 or newer.
Versions after v0.1.0 contain source-incompatible API corrections and are not backwards compatible with v0.1.0.
Create a REST API v2 client:
package main
import (
"context"
"fmt"
"log"
blockscout "github.com/PositiveSecurity/blockscout-go-api"
"github.com/PositiveSecurity/blockscout-go-api/client"
)
func main() {
api := blockscout.New(blockscout.EthMainnetBase)
addr, err := api.GetAddress(context.Background(), "0xd8da6bf26964af9d7eed9e03e53415d37aa96045")
if err != nil {
log.Fatal(err)
}
fmt.Println(addr.CoinBalance)
params := &client.ListAddressTransactionsParams{
Filter: "from",
}
txs, err := api.ListAddressTransactions(context.Background(), addr.Hash, params)
if err != nil {
log.Fatal(err)
}
if txs.HasNextPage() {
if err := txs.ApplyNextPage(params); err != nil {
log.Fatal(err)
}
}
}Use blockscout.New as the main public entry point. The client package remains available for request/response types such as client.ListTransactionsParams.
Token naming note: legacy Etherscan-compatible GetToken is kept for backwards compatibility and returns common.TokenInfo. Use GetTokenV2 for REST API v2 token details.
Typed filters and pagination can be reused without hand-building query strings:
itemsCount := 10
params := &client.ListTransactionsParams{
Filter: "validated",
ItemsCount: &itemsCount,
}
page, err := api.ListTransactions(ctx, params)
if err != nil {
log.Fatal(err)
}
if page.HasNextPage() {
if err := page.ApplyNextPage(params); err != nil {
log.Fatal(err)
}
nextPage, err := api.ListTransactions(ctx, params)
if err != nil {
log.Fatal(err)
}
_ = nextPage
}For inspection-heavy shapes, typed and raw helpers can be used side by side:
contract, err := api.GetSmartContract(ctx, "0x...")
rawContract, err := api.GetSmartContractRaw(ctx, "0x...")
abi, err := api.GetSmartContractABIEntries(ctx, "0x...")
rawABI, err := api.GetSmartContractABIRaw(ctx, "0x...")
_, _, _, _ = contract, rawContract, abi, rawABISmart-contract verification:
resp, err := api.VerifySmartContractViaFlattenedCode(ctx, "0x...", client.VerifyFlattenedCodeRequest{
CompilerVersion: "v0.8.17+commit.8df45f5f",
LicenseType: client.LicenseMIT,
SourceCode: "pragma solidity ^0.8.17; contract Token {}",
ContractName: "Token",
})- Addresses, transactions, blocks, tokens, smart contracts, verification, stats, and search
- Pagination helpers via
PaginatedResponse[T],HasNextPage,NextQuery, andApplyNextPage - Raw helpers for smart-contract JSON/ABI plus parsed ABI entries
- CSV downloads for supported address, token-holder, advanced-filter, and Celo reward endpoints
- Backend/config, withdrawals, advanced filters, account abstraction, and main-page endpoints
- Watchlist transactions, NFT metadata refetch helpers, and unified v1 search
- Chain-specific endpoints for Beacon deposits, Arbitrum, Optimism, Scroll, Celo, MUD, Polygon zkEVM, ZKsync, Zilliqa, and execution-node transactions
KnownExplorers is intentionally small: 20 popular mainnet Blockscout-compatible explorers with chain ID, name, base URL, and derived API URLs. Pass any other Blockscout-compatible URL directly to blockscout.New(baseURL).
- The client does not retry requests automatically and does not apply built-in rate-limit backoff.
- Use
context.Context,client.WithTimeout, or a customhttp.Client/RoundTripperto enforce request deadlines and retry policy. WithTimeoutclones the configuredhttp.Clientbefore settingTimeout, so a caller-provided client is not mutated.- Blockscout rate limits and 429 behavior can vary by hosted instance; callers should handle 429/5xx responses according to the target explorer's policy.
- A configured client can be used concurrently for requests.
- Configure the client before concurrent use; do not mutate
URL,URLv2, or call URL setter methods while requests are in flight.
make checkWithout make, run the local checks directly:
gofmt -l .
go vet ./...
go run honnef.co/go/tools/cmd/staticcheck@v0.7.0 ./...
go test ./...On Windows without make, the same local gate is available as:
.\scripts\check.ps1Run the live e2e runner:
make e2eThe default test suite is local and does not require a live explorer. Optional live smoke tests are gated behind BLOCKSCOUT_LIVE_TESTS=1; the broader cmd/blockscout-e2e runner can probe a real Blockscout instance and write a pass/fail/skip report with sampled response shapes. See cmd/blockscout-e2e/README.md for all runner flags and release-readiness profiles.
The old API is still available:
package main
import (
"fmt"
"log"
"github.com/PositiveSecurity/blockscout-go-api"
"github.com/PositiveSecurity/blockscout-go-api/client"
"github.com/PositiveSecurity/blockscout-go-api/common"
)
func main() {
var api client.BlockScoutAPIClient
// set your url api
api.SetBlockScoutApiUrl(blockscout.EthMainnet)
api.SetBlockScoutApiUrlV2(blockscout.EthMainnetV2)
// get the current block number
block, err := api.GetCurrentBlockRpcApi()
if err != nil {
log.Fatal(err)
}
fmt.Println(block)
// convert *big.Int to uint64
blocknum, err := common.BigIntToUint64(block)
if err != nil {
log.Fatal(err)
}
// get the ETH balance of vitalik.eth on the current block number
addr := "0xd8da6bf26964af9d7eed9e03e53415d37aa96045"
balance, err := api.GetEthBalance(addr, blocknum)
if err != nil {
log.Fatal(err)
}
fmt.Println(balance)
// get the latest indexed account balance via the legacy balance action
latestBalance, err := api.GetAccountBalance(addr)
if err != nil {
log.Fatal(err)
}
fmt.Println(latestBalance)
// api v2
body, err := api.GetBlockInfo("0")
if err != nil {
log.Fatal(err)
}
pretty, err := common.PrettyJSON(body)
if err != nil {
log.Fatal(err)
}
fmt.Println(pretty)
}