MetaLend Rewards API

Reward aggregation and claim data

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/metalend-rewards-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Discovery needs no key. Ratings and market analysis are Pro.

Get an API key

Free tier, no email required.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

metalend-rewards-api-openapi.yml Raw ↑
openapi: 3.0.3
info:
  title: MetaLend Rebalancing AI Agent Guide Rewards API
  version: 0.1.0
  description: "API for managing cross-protocol cross-chain DeFi rebalancing operations exposing: <ul>\n  <li><b>Auth</b> – challenge/verify flow for JWT issuance.</li>\n  <li><b>Project settings</b> – fetch current project settings.</li>\n  <li><b>User configuration</b> – register or update token-level user-rebalancer settings and fetch current wallet configs.</li>\n  <li><b>Deposits</b> – create deposit intents, receive tracking IDs, and poll status through PROCESSING -> BRIDGING (cross-chain only) -> SUCCESS/FAILED/EMERGENCY_WITHDRAW. New deposits may return <b>503</b> with <b>DEPOSITS_PAUSED</b> during maintenance.</li>\n  <li><b>Withdrawals</b> – submit signed withdrawal requests, receive tracking IDs, and monitor status PROCESSING -> SUCCESS/FAILED.</li>\n  <li><b>Balances</b> – query wallet-level balances and withdraw details, as well as balances in bridge progress.</li>\n  <li><b>Rewards</b> – aggregated rewards and claim details per wallet.</li>\n  <li><b>Pools</b> – list supported protocols (Aave, Morpho, Euler, etc.), their pools, APY breakdowns, and TVL snapshots.</li>\n  <li><b>Transaction Costs</b> – current deposit/withdraw on-behalf transaction fees and minimum amounts by chain.</li>\n  <li><b>AI Agent Guide</b> – Markdown flow guide at <code>/SKILL.md</code> with signing formats, wizard requirements, and known integration gotchas.</li>\n</ul> All endpoints require an <b>X-API-Key</b>. <b>JWT authentication</b> is required on <b>deposit, withdrawal, and config update</b> operations.\n<b>Obtain API key by <a href=\"https://developer.metalend.tech/\" target=\"_blank\" rel=\"noopener noreferrer\">creating a new project</a>.</b>\nAll requests must include the Origin header. Use Origin: https://app.metalend.tech unless you have been assigned a different allowed origin."
  contact:
    name: MetaLend API Support
    email: support@metalend.tech
  termsOfService: https://metalend-inc.gitbook.io/litepaper/terms-of-service
  x-guidance: AI agents MUST read https://api.metalend.tech/SKILL.md before implementing any MetaLend operation. It contains critical signing formats, required headers, wizard flows for config setup, and known failure modes.
  x-default-api-key: a7f2b8c4d9e1f3a6b5c8d2e7f1a4b9c3d6e8f2a5b7c1d4e9f3a6b8c2d5e7f1a4
  x-api-key-guidance: Use x-default-api-key as X-API-Key for agent testing/integration. If you are the project owner, create or use your own project API key from https://developer.metalend.tech instead.
servers:
- url: https://api.metalend.tech
  description: Production server
security:
- ApiKeyAuth: []
  OriginHeader: []
tags:
- name: Rewards
  description: Reward aggregation and claim data
paths:
  /v1/rewards/{walletAddress}:
    get:
      summary: Get rewards
      description: "Retrieves aggregated rewards data for the specified wallet address across \nsupported reward sources (currently Merkle-based rewards).\n\nAfter a successful claim on a specific chain, pass `reloadChain` with the human-readable chain name (e.g. `base`, `arbitrum`) to force a Merkl cache refresh for that chain.\n\n**How to claim rewards — the `claimTransaction` is pre-built, just send it.**\n\nEach `RewardItem` with claimable rewards includes a non-null `claimTransaction` \nfield. The backend has already constructed the full ABI-encoded calldata for the \n`claimRewards(address,address[],uint256[],bytes32[][])` function — including the \ndistributor address, token addresses, amounts, and merkle proofs.\n\nTo execute the claim, send a transaction from the user's wallet with exactly \nthese values:\n- `to`: `claimTransaction.to` — the rewards distributor contract address\n- `data`: `claimTransaction.calldata` — fully encoded, ready to submit as-is\n- `chainId`: `claimTransaction.chainId` — the chain to send it on\n\n**You do NOT need to encode anything yourself.** Do not use `params`, `abi`, or \n`method` to construct the calldata — those are informational only. Use `calldata` \ndirectly as the transaction `data` field.\n\nExample (viem):\nawait walletClient.sendTransaction({\n  to: claimTransaction.to,\n  data: claimTransaction.calldata,\n  chainId: claimTransaction.chainId,\n})\n\n**Rate limiting**: 1 request per 3 seconds per client IP. Excess requests receive HTTP `429` with `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers (see `RateLimitExceeded` response).\n"
      operationId: getRewards
      tags:
      - Rewards
      parameters:
      - name: walletAddress
        in: path
        required: true
        description: User's Ethereum wallet address
        schema:
          type: string
          pattern: ^0x[a-fA-F0-9]{40}$
        example: '0x1234567890123456789012345678901234567890'
      - name: reloadChain
        in: query
        required: false
        description: 'After claiming rewards on a specific chain, pass the chain name to force Merkl cache refresh for that chain. Omit for normal cached fetch.

          '
        schema:
          type: string
          enum:
          - ethereum
          - base
          - polygon
          - arbitrum
          - avalanche
          - optimism
          - linea
        example: base
      responses:
        '200':
          description: Rewards retrieved successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetRewardsResponse'
        '400':
          description: Validation error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              examples:
                walletAddressBlank:
                  $ref: '#/components/examples/WalletAddressBlank'
                invalidAddressFormat:
                  $ref: '#/components/examples/InvalidAddressFormat'
                invalidAddressCheckSum:
                  $ref: '#/components/examples/InvalidAddressCheckSum'
        '401':
          $ref: '#/components/responses/UnauthorizedNoHeader'
        '429':
          $ref: '#/components/responses/RateLimitExceeded'
        '500':
          description: Internal error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiError'
              examples:
                userDoesNotExist:
                  $ref: '#/components/examples/UserDoesNotExist'
                rebalancerDoesNotExistForWallet:
                  $ref: '#/components/examples/RebalancerDoesNotExistForWallet'
      x-agentcash-auth:
        mode: free
components:
  schemas:
    RewardSource:
      type: object
      required:
      - source
      - rewardItems
      properties:
        source:
          type: string
          description: Reward source name
        rewardItems:
          type: array
          items:
            $ref: '#/components/schemas/RewardItem'
    GetRewardsResponse:
      type: object
      required:
      - walletAddress
      - timestamp
      - totalClaimedUsd
      - totalAvailableUsd
      - rewards
      properties:
        walletAddress:
          type: string
          description: Wallet address
        timestamp:
          type: string
          description: ISO timestamp
        totalClaimedUsd:
          type: string
          description: Total claimed rewards in USD
        totalAvailableUsd:
          type: string
          description: Total available rewards in USD
        rewards:
          type: array
          items:
            $ref: '#/components/schemas/RewardSource'
    Token:
      type: object
      required:
      - symbol
      - name
      - address
      - decimals
      properties:
        symbol:
          type: string
          description: Token symbol
          enum:
          - USDC
          - MUSD
          - USDT
          - RLUSD
          example: USDC
        name:
          type: string
          description: Token full name
          example: USD Coin
        address:
          type: string
          pattern: ^0x[a-fA-F0-9]{40}$
          example: '0x1234567890123456789012345678901234567890'
          description: Token contract address
        decimals:
          type: integer
          description: Token decimals
          example: 6
        version:
          type: string
          nullable: true
          description: Token contract version
    VestingInfo:
      type: object
      required:
      - totalAllocatedRaw
      - totalAllocatedFormatted
      - unlockedRaw
      - unlockedFormatted
      - lockedRaw
      - lockedFormatted
      - vestingStartTime
      - vestingEndTime
      - vestingDuration
      - vestingGranularity
      - initialUnlockBps
      - bpsDenominator
      - progressPercentage
      - daysElapsed
      - daysTotal
      - stepsCompleted
      - stepsTotal
      properties:
        totalAllocatedRaw:
          type: string
        totalAllocatedFormatted:
          type: string
        unlockedRaw:
          type: string
        unlockedFormatted:
          type: string
        lockedRaw:
          type: string
        lockedFormatted:
          type: string
        vestingStartTime:
          type: integer
        vestingEndTime:
          type: integer
        vestingDuration:
          type: integer
        vestingGranularity:
          type: integer
        initialUnlockBps:
          type: integer
        bpsDenominator:
          type: integer
        progressPercentage:
          type: string
        daysElapsed:
          type: integer
        daysTotal:
          type: integer
        stepsCompleted:
          type: integer
        stepsTotal:
          type: integer
    RewardChainInfo:
      type: object
      required:
      - id
      - name
      - icon
      - endOfDisputePeriod
      - explorers
      properties:
        id:
          type: integer
        name:
          type: string
        icon:
          type: string
        endOfDisputePeriod:
          type: integer
        explorers:
          type: array
          items:
            $ref: '#/components/schemas/ChainExplorer'
    RewardItem:
      type: object
      required:
      - token
      - chain
      - claimedRaw
      - claimedFormatted
      - amount
      - claimedUsd
      - pendingRaw
      - pendingFormatted
      - availableRaw
      - availableFormatted
      - availableUsd
      - distributorAddress
      properties:
        token:
          $ref: '#/components/schemas/Token'
        chain:
          $ref: '#/components/schemas/RewardChainInfo'
        claimedRaw:
          type: string
        claimedFormatted:
          type: string
        amount:
          type: string
        claimedUsd:
          type: string
        pendingRaw:
          type: string
        pendingFormatted:
          type: string
        availableRaw:
          type: string
          description: 'Available (unclaimed) reward amount in raw token units.  When this is "0", claimTransaction will be null and no claim button  should be shown. When > "0", claimTransaction is non-null and ready to execute.

            '
        availableFormatted:
          type: string
        availableUsd:
          type: string
        distributorAddress:
          type: string
        proof:
          type: array
          nullable: true
          items:
            type: string
        vestingInfo:
          $ref: '#/components/schemas/VestingInfo'
          nullable: true
        claimTransaction:
          $ref: '#/components/schemas/ClaimTransaction'
          nullable: true
          description: 'Pre-built transaction to claim this reward item on-chain. Non-null when  `availableRaw` > "0". To claim: send a transaction with  `to = claimTransaction.to`, `data = claimTransaction.calldata`,  `chainId = claimTransaction.chainId`. No additional encoding needed.

            '
    ClaimTransactionParams:
      type: object
      required:
      - distributor
      - tokens
      - amounts
      - proofs
      properties:
        distributor:
          type: string
        tokens:
          type: array
          items:
            type: string
        amounts:
          type: array
          items:
            type: string
        proofs:
          type: array
          items:
            type: array
            items:
              type: string
    ClaimTransaction:
      type: object
      required:
      - to
      - chainId
      - chain
      - method
      - params
      - abi
      - calldata
      properties:
        to:
          type: string
          description: 'The rewards distributor contract address. Use as the `to` field of the transaction.

            '
        chainId:
          type: integer
          description: 'The EVM chain ID to send the transaction on. Ensure the wallet is on this chain  before calling sendTransaction.

            '
        chain:
          type: string
          description: Human-readable chain name (informational, e.g. "ethereum", "linea").
        method:
          type: string
          description: 'The contract function signature — informational only.  The value is "claimRewards(address,address[],uint256[],bytes32[][])". Do NOT use this to build the transaction — use `calldata` directly.

            '
        params:
          $ref: '#/components/schemas/ClaimTransactionParams'
          description: 'The decoded parameters passed to claimRewards — informational only. Do NOT use these to re-encode the calldata — use `calldata` directly.

            '
        abi:
          type: string
          description: 'The ABI fragment for the claimRewards function — informational only. Do NOT use this to re-encode the calldata — use `calldata` directly.

            '
        calldata:
          type: string
          description: 'Fully ABI-encoded transaction data, ready to submit as-is. Use this as the `data` field of sendTransaction.  This includes the function selector and all encoded arguments  (distributor, token addresses, amounts, merkle proofs). Example: walletClient.sendTransaction({ to, data: calldata, chainId })

            '
    ApiError:
      type: object
      required:
      - code
      - internalCode
      - message
      properties:
        code:
          type: string
          description: Error category identifier
        internalCode:
          type: string
          description: More specific error identifier
        message:
          type: string
          description: Human-readable error message
        requestId:
          type: string
          format: uuid
          description: Unique request identifier for tracking
        retryAfterSeconds:
          type: integer
          description: Seconds to wait before retrying (for rate limiting)
    ChainExplorer:
      type: object
      required:
      - chainId
      - id
      - type
      - url
      properties:
        chainId:
          type: integer
        id:
          type: string
        type:
          type: string
        url:
          type: string
  responses:
    UnauthorizedNoHeader:
      description: No authorization header provided
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            unauthorizedNoHeader:
              $ref: '#/components/examples/UnauthorizedNoHeader'
    RateLimitExceeded:
      description: 'Too many requests for this endpoint. Limits are enforced per client IP for the public API routes backed by `RebalancerResource` (and related resources using the same filter).


        Response headers (when throttled):

        - `Retry-After`: seconds to wait before retrying (matches the rate-limit window duration for that endpoint).

        - `X-RateLimit-Limit`: maximum requests allowed in the window (e.g. `1`).

        - `X-RateLimit-Remaining`: remaining requests in the window (`0` when throttled).

        - `X-RateLimit-Reset`: Unix timestamp (seconds) when the limit window resets.


        `internalCode` in the JSON body is `THROTTLE_PER_IP` for IP-scoped limits (other values may apply for different scopes in the backend).

        '
      headers:
        Retry-After:
          description: Seconds to wait before retrying this endpoint.
          schema:
            type: integer
            example: 3
        X-RateLimit-Limit:
          description: Maximum number of requests allowed in the current window for this endpoint.
          schema:
            type: integer
            example: 1
        X-RateLimit-Remaining:
          description: Remaining requests in the current window.
          schema:
            type: integer
            example: 0
        X-RateLimit-Reset:
          description: Unix timestamp (seconds) when the current limit window resets.
          schema:
            type: integer
            format: int64
            example: 1713264123
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          examples:
            rateLimitExceededPerIp:
              summary: Rate limit exceeded (per client IP)
              value:
                code: RATE_LIMIT_EXCEEDED
                internalCode: THROTTLE_PER_IP
                message: Rate limit exceeded. Maximum 1 requests per 3 seconds allowed. Please try again later.
                requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
                retryAfterSeconds: 3
  examples:
    InvalidAddressCheckSum:
      summary: Invalid address checksum
      value:
        code: ILLEGAL_ARGUMENT_ERROR
        internalCode: INVALID_ADDRESS_CHECKSUM
        message: Invalid Ethereum address checksum. Please ensure the address is properly checksummed (EIP-55)
        requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
        retryAfterSeconds: null
    RebalancerDoesNotExistForWallet:
      summary: Rebalancer does not exist for wallet
      value:
        code: INTERNAL_ERROR
        internalCode: REBALANCER_DOES_NOT_EXIST_FOR_WALLET
        message: Rebalancer does not exist for wallet address '0x1234567890123456789012345678901234567890'
        requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
        retryAfterSeconds: null
    InvalidAddressFormat:
      summary: Invalid address format
      value:
        code: ILLEGAL_ARGUMENT_ERROR
        internalCode: INVALID_ADDRESS_FORMAT
        message: 'Invalid address format. Expected: 0x followed by 40 hexadecimal characters.'
        requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
        retryAfterSeconds: null
    UnauthorizedNoHeader:
      summary: No authorization header
      value:
        code: AUTHENTICATION_FAILED
        internalCode: NO_AUTH_INFO
        message: No authentication information provided
        requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
        retryAfterSeconds: null
    WalletAddressBlank:
      summary: Wallet address blank
      value:
        code: ILLEGAL_ARGUMENT_ERROR
        internalCode: BLANK_ADDRESS
        message: Wallet address cannot be blank
        requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
        retryAfterSeconds: null
    UserDoesNotExist:
      summary: User does not exist
      value:
        code: INTERNAL_ERROR
        internalCode: USER_DOES_NOT_EXIST
        message: User does not exist for wallet address '0x1234567890123456789012345678901234567890'
        requestId: 2c2f3dc1-d19a-49cb-b695-37d025e551b3
        retryAfterSeconds: null
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: X-API-Key
      description: API key required for all endpoints.
    JwtAuth:
      type: apiKey
      in: header
      name: Authorization
      description: 'JWT issued by POST /v1/auth/verify. Pass as Authorization: Bearer <token>.


        x-agent-hint: agentcash and other agentic wallets strip the Authorization header for their own SIWX flow. Use X-MetaLend-JWT: Bearer <token> instead — the backend accepts both headers. Affected endpoints: POST /v1/deposits, POST /v1/withdrawals, PUT /v1/config.

        '
    OriginHeader:
      type: apiKey
      in: header
      name: Origin
      description: 'Required for CORS requests from browsers. Must match the allowed origins configured for the API key. Example: Origin: https://app.metalend.tech. Localhost is always allowed but must be specified.

        '
externalDocs:
  description: Litepaper
  url: https://metalend-inc.gitbook.io/litepaper