TypeScript SDK
Use the first-party Starkscan TypeScript client when you want typed explorer reads in application code.
Use the first-party Starkscan TypeScript client when you want typed explorer reads in application code.
Use the TypeScript SDK when you want typed Starkscan reads in application code without rebuilding the HTTP contract yourself.
Current label: alpha. The package wraps certified and beta REST routes, and
the published alpha has passed live hosted status smoke. It remains alpha until
publishing controls and public-source metadata are complete. Use the same
STARKSCAN_API_KEY and /api base that REST, CLI, and hosted MCP use.
For npm package provenance, Socket links, and exact-version pinning rules, use Package trust.
Use the live explorer when you want to see the same entities first:
npm install @starkscan/sdk@alpha
pnpm add @starkscan/sdk@alpha
bun add @starkscan/sdk@alpha
Current public channel today: @alpha
Install the
@alphachannel (or pin an exact0.1.0-alpha.x) as shown above. An untaggednpm install @starkscan/sdkresolves to a fail-closed placeholder whosecreateStarkscanClient()throws on use — always include the channel tag or an exact version.
Release channels:
alpha: fastest-moving public channelbeta: stabilization channellatest: current generally recommended channel once promotednpm install ./starkscan-sdk-<version>.tgz
Use the tarball flow only when you need controlled distribution or release verification. Public npm publishing uses the same @starkscan/sdk package name and API surface, so you can switch install channels without changing your application code.
For hosted external deployments, set STARKSCAN_BASE_URL to the deployment’s /api base, for example https://<your-starkscan-host>/api.
The SDK keeps using the normal /v1/* route paths under that configured base.
import { createStarkscanClient } from "@starkscan/sdk";
const starkscan = createStarkscanClient({
apiKey: process.env.STARKSCAN_API_KEY!,
baseUrl: process.env.STARKSCAN_BASE_URL!,
chainId: "SN_MAIN",
});
const status = await starkscan.status();
const block = await starkscan.block("1234");
const totalSupply = await starkscan.tokenTotalSupply("0xtoken");
const balance = await starkscan.tokenBalanceOf("0xtoken", "0xowner");
Use block reads when your application starts from a block number or block hash and needs canonical block contents.
const block = await starkscan.block(8279910, 5);
const txs = await starkscan.blockTransactions(8279910, undefined, 25);
for (const item of txs.items) {
console.log(item.txIndex, item.txHash, item.finalityStatus);
}
starkscan.block accepts a block number, block hash, or "latest". blockTransactions requires a concrete block number; resolve "latest" or a block hash with starkscan.block(...) first, then pass the returned blockNumber with nextCursor. The hosted SDK does not expose per-block event or receipt pages; use transaction detail reads after resolving the block transaction list.
const wallets = ["0xwalletA", "0xwalletB"];
const activity = await Promise.all(
wallets.map((wallet) => starkscan.addressActivity(wallet, undefined, 50)),
);
const transactions = await Promise.all(
wallets.map((wallet) => starkscan.addressTransactions(wallet, undefined, 50)),
);
const holdings = await Promise.all(
wallets.map((wallet) => starkscan.addressTokenHoldings(wallet)),
);
const flows = await starkscan.tokenTransfers("0xtoken", {
addresses: wallets,
limit: 100,
});
Use that pattern when you are monitoring a handful of wallets and need recent activity, recent transactions, current holdings, and token-scoped inflow/outflow reads from the same client.
For the full external starter, including the shared env contract and the matching REST and CLI flows, use Monitor 10 wallets.
const totalSupply = await starkscan.tokenTotalSupply("0x0123...");
const pendingSupply = await starkscan.tokenTotalSupply("0x0123...", "pending");
const balance = await starkscan.tokenBalanceOf("0x0123...", "0x0456...");
const transfers = await starkscan.tokenTransfers("0x0123...", {
addresses: ["0x0456...", "0x0789..."],
fromBlock: 7_800_000,
toBlock: 7_802_500,
});
const transfers = await starkscan.tokenTransfers("0x0123...", {
addresses: ["0xwalletA", "0xwalletB"],
fromBlock: 7_800_000,
toBlock: 7_801_000,
limit: 100,
});
for (const item of transfers.items) {
console.log(item.timestampIso, item.txHash, item.rawValue);
}
const events = await starkscan.contractEvents("0xcontract", {
topic0: "0x99cd8bde557814842a3121e8ddfd433a539b8c9f14bf31ebf108d12e6196e9",
fromBlock: 7_800_000,
toBlock: 7_800_500,
limit: 100,
});
for (const item of events.items) {
console.log(item.blockNumber, item.txHash, item.logIndex, item.topic0);
}
Use contractEvents when you need the canonical paginated event stream for one contract before applying protocol-specific decoding or position logic. Keep filters server-side first: contract address, exact topic0..topic3, block range, then cursor pagination with nextCursor.
const batch = await starkscan.txDetails(["0xabc...", "0xdef..."], {
logLimitPerTx: 32,
});
for (const tx of batch.items) {
console.log(
tx.txHash,
tx.blockNumber,
tx.logs.length,
tx.tokenTransfers.length,
);
}
Use txDetails when you already have an ordered tx hash list and want bounded Starkscan transaction previews in one batch. Check logsTruncated and tokenTransfersTruncated before treating child arrays as exhaustive.
It keeps: