Docs

Fetch Rewards Data

Fetch a user's Morpho Vault and Market rewards from the Merkl API, with caching and error handling.

This tutorial shows how to fetch a user's Morpho Vault and Market rewards from the Merkl API.

What You'll Learn

  • How to query user-specific rewards from Merkl API
  • Handle caching and errors for user-specific reward data

Prerequisites

  • Basic understanding of Reward Programs
  • Familiarity with REST APIs
  • A tool for making API requests (curl, fetch, or axios)

API Landscape Overview

For current user rewards, use the Merkl API:

APIUse CaseRewards TypeData Provided
Morpho REST APIKnown token metadataToken dataFor a known token, use GET /v0/tokens/{token-selector}
Merkl APIUser claimable rewardsMerkl onlyUser balances, claim data, real-time rewards
Legacy RewardsUser claimable rewardsURD onlyDeprecated. Use rewards-legacy.morpho.org

Recommended Strategy:

  1. Use Merkl API for user-specific claimable rewards.
  2. For legacy URD rewards, direct users to rewards-legacy.morpho.org

Fetching User-Specific Rewards

Merkl API (Current Programs)

To get a user's claimable Merkl rewards:

Endpoint:

GET https://api.merkl.xyz/v4/users/{address}/rewards?chainId={chainId}

Example:

curl "https://api.merkl.xyz/v4/users/0x1234...5678/rewards?chainId=1"

Response Structure:

[
  {
    "chain": { "id": 1, "name": "Ethereum" },
    "rewards": [
      {
        "token": { "address": "0xREWARD_TOKEN", "symbol": "MORPHO", "decimals": 18 },
        "amount": "2000000000000000000",
        "claimed": "1000000000000000000",
        "pending": "0",
        "proofs": ["0xproof1", "0xproof2"]
      }
    ]
  }
]

The response is an array of per-chain objects, each with a rewards array. A token's currently-claimable balance is amount - claimed (both are cumulative). pending is reward not yet in a live merkle root.

TypeScript Example:

async function fetchMerklRewards(userAddress: string, chainId: number) {
  const response = await fetch(
    `https://api.merkl.xyz/v4/users/${userAddress.toLowerCase()}/rewards?chainId=${chainId}`
  );
  const data = await response.json();

  // The endpoint returns an array of per-chain objects; flatten to a list of rewards.
  const rewards = data.flatMap((entry) => entry.rewards);

  // Aggregate the unclaimed balance per token (amount - claimed)
  const claimableByToken: Record<string, bigint> = {};

  for (const reward of rewards) {
    const unclaimed = BigInt(reward.amount) - BigInt(reward.claimed);
    claimableByToken[reward.token.address] =
      (claimableByToken[reward.token.address] || 0n) + unclaimed;
  }

  return claimableByToken;
}

Legacy Rewards (Pre-Merkl)

Performance Considerations

Caching

User Merkl Rewards:

  • Cache for 5-10 minutes
  • Updates every 8 hours, but users expect fresh data

Parallel Fetching

When fetching rewards for multiple users, independent Merkl requests can run in parallel.

Error Handling

async function fetchRewardsSafely(userAddress: string) {
  try {
    const merkl = await fetchMerklRewards(userAddress);
    return merkl;
  } catch (error) {
    console.error("Merkl API error:", error);
    return {}; // Fallback to empty
  }
}

API Rate Limits

Be mindful of rate limits:

APIRate LimitNotes
Morpho API5k / 5 minUse responsibly
Merkl API10 request / secondCache responses

Best Practice: Cache aggressively and fetch only when needed.

Next Steps

Resources