Skip to main content

What is data normalization?

Data normalization automatically converts provider-specific response formats into a consistent, unified schema. This eliminates the need to write provider-specific parsing logic or handle format variations across different data sources. Use data normalization when you need:
  • Consistent data formats regardless of provider
  • Zero provider-specific parsing logic
  • Seamless provider switching without code changes
  • Predictable data structures across all endpoints
Data normalization is enabled by default for all Uniblock users. All responses follow the same schema regardless of underlying provider.

How it works

Different providers return blockchain data in different formats. Uniblock normalizes all responses into a consistent schema:
  1. Provider response - Raw data received from blockchain provider
  2. Schema mapping - Uniblock maps provider fields to unified schema
  3. Type conversion - Converts data types (string/number/formatted)
  4. Field enrichment - Adds missing fields with computed values
  5. Unified response - Returns consistent format to your application
Result: Your code works with any provider without format-specific logic.

Normalization examples

Token balances

Different providers return token balances in incompatible formats:
ProviderBalance formatDecimalsFormatted value
Alchemy"1000000000" (string)Separate fieldNot included
Moralis1000000000 (number)Separate fieldNot included
Covalent"1000.00" (formatted)Included in valueAlready formatted
Uniblock normalized response:
{
  "balance": "1000000000",
  "decimals": 6,
  "balanceFormatted": "1000.00"
}
Result: All three formats unified into single, predictable structure.

Transaction timestamps

Providers use different timestamp formats:
ProviderTimestamp formatExample
AlchemyUnix timestamp (seconds)1640995200
MoralisISO 8601 string"2022-01-01T00:00:00Z"
CovalentUnix timestamp (milliseconds)1640995200000
Uniblock normalized response:
{
  "timestamp": 1640995200,
  "timestampISO": "2022-01-01T00:00:00Z",
  "blockNumber": 13916166
}
Result: Both Unix and ISO formats provided for flexibility.

NFT metadata

NFT metadata structures vary significantly across providers: Alchemy format:
{
  "contract": { "address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D" },
  "id": { "tokenId": "1" },
  "title": "Bored Ape #1",
  "metadata": {
    "image": "ipfs://QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ",
    "attributes": [{ "trait_type": "Background", "value": "Orange" }]
  }
}
Moralis format:
{
  "token_address": "0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d",
  "token_id": "1",
  "name": "Bored Ape #1",
  "image": "ipfs://QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ",
  "metadata": "{\"attributes\":[{\"trait_type\":\"Background\",\"value\":\"Orange\"}]}"
}
Uniblock normalized response:
{
  "contractAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
  "tokenId": "1",
  "name": "Bored Ape #1",
  "description": "The Bored Ape Yacht Club is a collection of 10,000 unique Bored Ape NFTs",
  "image": "ipfs://QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ",
  "imageUrl": "https://ipfs.io/ipfs/QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ",
  "attributes": [
    {
      "traitType": "Background",
      "value": "Orange"
    }
  ]
}
Result: Consistent structure with both IPFS and HTTP URLs, parsed attributes, and camelCase fields.

Real-world scenarios

Scenario 1: Multi-provider portfolio tracker

Challenge: Building a portfolio tracker that aggregates data from multiple providers, each with different response formats. Without normalization:
// Provider-specific parsing logic required
function parseBalance(provider, response) {
  if (provider === 'alchemy') {
    return {
      balance: response.tokenBalance,
      decimals: response.decimals,
      formatted: formatBalance(response.tokenBalance, response.decimals),
    };
  } else if (provider === 'moralis') {
    return {
      balance: response.balance.toString(),
      decimals: response.decimals,
      formatted: formatBalance(response.balance, response.decimals),
    };
  } else if (provider === 'covalent') {
    return {
      balance: parseFormattedBalance(response.balance),
      decimals: extractDecimals(response.balance),
      formatted: response.balance,
    };
  }
}
With Uniblock normalization:
// Single parsing logic works for all providers
const response = await fetch('https://api.uniblock.dev/uni/v1/token/balances', {
  headers: { 'x-api-key': 'YOUR_API_KEY' },
});

const data = await response.json();
// Always has: balance, decimals, formattedBalance
console.log(data.formattedBalance); // Works regardless of provider
Result: 90% less code, zero provider-specific logic.

Scenario 3: NFT marketplace integration

Challenge: Displaying NFT collections from multiple providers with consistent metadata. Request:
curl --location \
'https://api.uniblock.dev/uni/v1/nft/collection?chainId=1&contractAddress=0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D&limit=20' \
--header 'x-api-key: YOUR_API_KEY'
Normalized response:
{
  "nfts": [
    {
      "contractAddress": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
      "tokenId": "1",
      "name": "Bored Ape #1",
      "description": "The Bored Ape Yacht Club...",
      "image": "ipfs://QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ",
      "imageUrl": "https://ipfs.io/ipfs/QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ",
      "owner": "0x789...",
      "attributes": [
        { "traitType": "Background", "value": "Orange" },
        { "traitType": "Eyes", "value": "Bored" }
      ]
    }
  ]
}
Result: Consistent NFT structure with resolved IPFS URLs and parsed attributes.

Normalized field types

Uniblock ensures consistent data types across all providers:

Address fields

Always returned as:
  • Checksummed Ethereum addresses
  • Lowercase for consistency in comparisons
  • Validated format
{
  "address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"
}

Numeric values

Always returned as:
  • String for large numbers (prevents precision loss)
  • Formatted decimal version included
  • Original decimals preserved
{
  "balance": "1000000000000000000",
  "formattedBalance": "1.0",
  "decimals": 18
}

Timestamps

Always returned as:
  • Unix timestamp (seconds)
  • ISO 8601 string
  • Block number for reference
{
  "timestamp": 1640995200,
  "timestampISO": "2022-01-01T00:00:00Z",
  "blockNumber": 13916166
}

Boolean values

Always returned as:
  • Native boolean type (not strings)
  • Consistent naming (isActive, hasMetadata)
{
  "isVerified": true,
  "hasMetadata": false
}

Key benefits

Zero parsing logic

No provider-specific code needed. Single schema works everywhere.

Seamless provider switching

Switch providers without changing your application code.

Type safety

Consistent data types prevent runtime errors and type mismatches.

Reduced development time

Build faster without handling format variations across providers.

Field mapping reference

Common provider field variations and their normalized equivalents:
Data typeProvider variationsUniblock normalized
Token balancetokenBalance, balance, valuebalance
Decimalsdecimals, decimal, token_decimaldecimals
Contract addresscontract, token_address, contractAddresscontractAddress
Token IDtokenId, token_id, id.tokenIdtokenId
Transaction hashhash, tx_hash, transactionHashhash
Block numberblockNumber, block_number, blockblockNumber
Timestamptimestamp, block_timestamp, timeStamptimestamp
Gas usedgasUsed, gas_used, gasgasUsed
NFT imageimage, image_url, metadata.imageimage, imageUrl
NFT attributesattributes, traits, metadata.attributesattributes

Handling edge cases

Uniblock normalization handles common edge cases automatically:

Null and missing values

Provider behavior:
  • Some providers return null
  • Others omit the field entirely
  • Some return empty strings
Uniblock normalization:
{
  "balance": "0",
  "balanceFormatted": "0.0",
  "metadata": null
}
Result: Consistent null handling and default values.

IPFS URLs

Provider behavior:
  • Some return ipfs:// URIs
  • Others return gateway URLs
  • Some return raw CIDs
Uniblock normalization:
{
  "image": "ipfs://QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ",
  "imageUrl": "https://ipfs.io/ipfs/QmRRPWG96cmgTn2qSzjwr2qvfNEuhunv6FNeMFGa9bx6mQ"
}
Result: Both IPFS URI and HTTP gateway URL provided.

Large numbers

Provider behavior:
  • Some return numbers (lose precision for large values)
  • Others return strings
  • Some return scientific notation
Uniblock normalization:
{
  "balance": "1000000000000000000",
  "balanceFormatted": "1.0",
  "decimals": 18
}
Result: String format preserves precision, formatted version for display.

Case sensitivity

Provider behavior:
  • Some use camelCase
  • Others use snake_case
  • Some use PascalCase
Uniblock normalization:
{
  "contractAddress": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  "tokenId": "1",
  "blockNumber": 13916166
}
Result: Consistent camelCase across all fields.

Best practices

Use normalized fields directly - Don’t re-parse or transform normalized responses. They’re already in optimal format.
Rely on formatted values - Use balanceFormatted, valueFormatted for display instead of manual formatting.
Trust type consistency - Normalized responses have consistent types. No need for type guards or conversions.
Use both timestamp formats - timestamp for calculations, timestampISO for display and logging.

Monitoring normalization

Track normalization metrics in the Uniblock dashboard:
  • Normalization success rate - Percentage of responses successfully normalized
  • Field mapping coverage - Which provider fields are being mapped
  • Type conversion errors - Rare cases where normalization fails
  • Provider format changes - Alerts when providers change response formats
Use these metrics to:
  • Ensure consistent data quality
  • Identify provider format changes early
  • Understand which providers require most normalization
  • Validate data integrity across providers

Next steps


Common pitfalls

Don’t re-parse normalized data - Normalized responses are already in optimal format. Additional parsing adds complexity and potential errors.
Avoid provider-specific assumptions - Don’t assume data comes from specific provider. Normalization abstracts provider details.
Use string types for large numbers - JavaScript numbers lose precision for values > 2^53. Always use string balance field, not numeric conversions.
Don’t mix normalized and raw data - If using Uniblock, use normalized responses exclusively. Mixing with raw provider data creates inconsistencies.