Moralis API Integration
Moralis is a managed Web3 data API: NFT data, token balances, transaction history, contract events. Main value isn't that "you can integrate in 10 minutes" but that Moralis indexes historical data—querying Transfer events across contract lifetime through their API is cheaper than parsing blocks yourself via getLogs.
Initialization and Authentication
import Moralis from 'moralis';
await Moralis.start({ apiKey: process.env.MORALIS_API_KEY });
API key should be on backend, not in browser. Free tier: 40,000 compute units/day. Most requests cost 2-25 CU.
Key Endpoints
NFT Collection Data
import { EvmChain } from '@moralisweb3/common-evm-utils';
// NFTs in user wallet
const nfts = await Moralis.EvmApi.nft.getWalletNFTs({
address: userAddress,
chain: EvmChain.ETHEREUM,
tokenAddresses: [COLLECTION_ADDRESS], // optional — filter by collection
});
// Floor price and collection stats
const stats = await Moralis.EvmApi.nft.getNFTCollectionStats({
address: COLLECTION_ADDRESS,
chain: EvmChain.ETHEREUM,
});
console.log(stats.result.floorPriceUsd);
console.log(stats.result.totalTrades);
Token Balances
// ERC-20 balances
const tokenBalances = await Moralis.EvmApi.token.getWalletTokenBalances({
address: userAddress,
chain: EvmChain.ETHEREUM,
});
tokenBalances.result.forEach(token => {
console.log(`${token.symbol}: ${token.displayValue}`); // already normalized by decimals
});
// Native balance (ETH/BNB/etc)
const nativeBalance = await Moralis.EvmApi.balance.getNativeBalance({
address: userAddress,
chain: EvmChain.ETHEREUM,
});
Transaction History
const transfers = await Moralis.EvmApi.token.getWalletTokenTransfers({
address: userAddress,
chain: EvmChain.ETHEREUM,
contractAddresses: [TOKEN_ADDRESS],
limit: 100,
});
// Pagination via cursor
if (transfers.hasNext()) {
const nextPage = await transfers.next();
}
Token Data and Price
const tokenPrice = await Moralis.EvmApi.token.getTokenPrice({
address: TOKEN_ADDRESS,
chain: EvmChain.ETHEREUM,
exchange: 'uniswapv3', // specific DEX or auto
});
console.log(tokenPrice.result.usdPrice);
console.log(tokenPrice.result.nativePrice?.value); // price in ETH
Streams API (Real-time Events)
Moralis Streams is an alternative to self-managed WebSocket subscription. Moralis listens to blockchain, you get webhooks:
const stream = await Moralis.Streams.add({
chains: [EvmChain.ETHEREUM],
description: 'Track transfers',
tag: 'token-transfers',
webhookUrl: 'https://yourserver.com/webhooks/moralis',
includeContractLogs: true,
abi: ERC20_ABI,
topic0: ['Transfer(address,address,uint256)'],
filter: { eq: ['address', TOKEN_ADDRESS] },
advancedOptions: [{ topic0: 'Transfer(address,address,uint256)', filter: { gte: ['value', '1000000000000000000'] } }],
});
await Moralis.Streams.addAddress({
id: stream.result.id,
address: [TOKEN_ADDRESS],
});
On your webhook endpoint:
app.post('/webhooks/moralis', async (req, reply) => {
const body = req.body;
// Verify signature
const providedSignature = req.headers['x-signature'];
const generatedSignature = crypto
.createHmac('sha3-256', process.env.MORALIS_API_KEY!)
.update(JSON.stringify(body))
.digest('hex');
if (providedSignature !== generatedSignature) {
return reply.code(401).send('Invalid signature');
}
// Process events
body.logs.forEach((log: any) => {
// log.decoded contains parsed arguments
});
return reply.send({ received: true });
});
Limitations and Alternatives
Moralis doesn't instantly index custom contracts. For newly deployed contracts, historical data may appear with delay of several hours. For real-time events of new contracts—Streams works from the moment of addition, but historical data before that must be loaded separately via getLogs.
For complex analytics queries (aggregation, joins across multiple collections), Moralis lags behind The Graph or custom ClickHouse.
Timeline Estimates
NFT portfolio viewer with balances + history—1 day. Adding Streams webhook for real-time notifications + integration with main app—2-3 days.







