Skip to main content
When working with tokens that use bonding curves (like pump.fun on Solana or clanker on Base), you often need to find the bonding curve contract address to interact with the pool before it migrates to a traditional AMM. This cookbook shows you two methods to retrieve bonding curve addresses using Mobula’s API.

Two Methods to Find Bonding Curves

The simplest way - directly get the bondingCurveAddress field:
curl -X GET "https://api.mobula.io/api/2/token/details?address=TOKEN_ADDRESS" \
  -H "Authorization: YOUR_API_KEY"

Method 2: Using token/markets

List ALL pools/markets for a token, then find the bonding curve by matching the pool type with the token’s source:
curl -X GET "https://api.mobula.io/api/2/token/markets?address=TOKEN_ADDRESS" \
  -H "Authorization: YOUR_API_KEY"

Understanding Bonding Curves

A bonding curve is a pricing mechanism where token price increases as more tokens are purchased. Many protocols use bonding curves to bootstrap liquidity for new tokens before eventually migrating to traditional AMM pools once a threshold is reached.

Key Concepts

source: The protocol that launched the token (e.g., “pumpfun”, “clanker”, “moonshot”) type: The pool type identifier (e.g., “pumpfun”, “clanker”, “uniswap-v2”) Bonding Curve Pool: When a token’s source matches a pool’s type, that pool is the bonding curve. For example:
  • Token source: “pumpfun” → Pool type: “pumpfun” = Bonding curve
  • Token source: “clanker” → Pool type: “clanker” = Bonding curve

Key Response Fields

FieldTypeDescription
sourcestringProtocol that launched the token
typestringPool type identifier
bondingCurveAddressstring | nullContract address of the bonding curve pool
bondedbooleanWhether token completed bonding and migrated
bondingPercentagenumberProgress towards bonding completion (0-100%)

Method 1: Using token/details (Simplest)

TypeScript

import { MobulaClient } from '@mobula/sdk';

const client = new MobulaClient({
  apiKey: process.env.MOBULA_API_KEY
});

async function getBondingCurveFromDetails(
  tokenAddress: string,
  blockchain?: string
) {
  const response = await client.fetchTokenDetails({
    address: tokenAddress,
    blockchain: blockchain
  });
  
  const token = response.data;
  
  return {
    bondingCurveAddress: token.bondingCurveAddress,
    bonded: token.bonded,
    bondingPercentage: token.bondingPercentage,
    source: token.source,
    poolAddress: token.poolAddress
  };
}

// Example: pump.fun token on Solana
const result = await getBondingCurveFromDetails(
  'HLwEJQVCQMVdRhFg1YKKgNAnCwUk4eboMTtdBtpump',
  'solana'
);

console.log(result);
// {
//   bondingCurveAddress: "7xKp...abc123",
//   bonded: false,
//   bondingPercentage: 67.5,
//   source: "pumpfun",
//   poolAddress: "7xKp...abc123"
// }

Python

import requests

API_KEY = 'YOUR_API_KEY'
BASE_URL = 'https://api.mobula.io/api/2'

def get_bonding_curve_from_details(token_address: str, blockchain: str = None) -> dict:
    params = {'address': token_address}
    if blockchain:
        params['blockchain'] = blockchain
    
    response = requests.get(
        f'{BASE_URL}/token/details',
        params=params,
        headers={'Authorization': API_KEY}
    )
    response.raise_for_status()
    token = response.json().get('data', {})
    
    return {
        'bonding_curve_address': token.get('bondingCurveAddress'),
        'bonded': token.get('bonded'),
        'bonding_percentage': token.get('bondingPercentage'),
        'source': token.get('source'),
        'pool_address': token.get('poolAddress')
    }

# Example
result = get_bonding_curve_from_details(
    'HLwEJQVCQMVdRhFg1YKKgNAnCwUk4eboMTtdBtpump',
    'solana'
)
print(result)

Method 2: Using token/markets (Advanced)

This method lists ALL pools and finds the bonding curve by matching pool type with token source.

TypeScript

async function getBondingCurveFromMarkets(
  tokenAddress: string,
  blockchain?: string
) {
  const response = await client.fetchTokenMarkets({
    address: tokenAddress,
    blockchain: blockchain,
    limit: 25
  });
  
  const markets = response.data;
  
  if (markets.length === 0) {
    return { error: 'No markets found' };
  }
  
  // Get token source from any market (all markets have same base token)
  const tokenSource = markets[0].base.source;
  
  // Find bonding curve: pool type matches token source
  const bondingCurve = markets.find(m => m.type === tokenSource);
  
  if (!bondingCurve) {
    return {
      status: 'no-bonding-curve',
      message: `No bonding curve found (source: ${tokenSource})`
    };
  }
  
  return {
    bondingCurveAddress: bondingCurve.address,
    bondingPercentage: bondingCurve.bondingPercentage,
    bonded: bondingCurve.bonded,
    source: tokenSource,
    poolType: bondingCurve.type,
    liquidityUSD: bondingCurve.liquidityUSD,
    exchange: bondingCurve.exchange.name
  };
}

// Example: pump.fun token
const result = await getBondingCurveFromMarkets(
  'HLwEJQVCQMVdRhFg1YKKgNAnCwUk4eboMTtdBtpump',
  'solana'
);

console.log(result);
// {
//   bondingCurveAddress: "7xKp...abc123",
//   bondingPercentage: 67.5,
//   bonded: false,
//   source: "pumpfun",
//   poolType: "pumpfun",
//   liquidityUSD: 45000.50,
//   exchange: "Pump.fun"
// }

Python

def get_bonding_curve_from_markets(token_address: str, blockchain: str = None) -> dict:
    params = {'address': token_address, 'limit': 25}
    if blockchain:
        params['blockchain'] = blockchain
    
    response = requests.get(
        f'{BASE_URL}/token/markets',
        params=params,
        headers={'Authorization': API_KEY}
    )
    response.raise_for_status()
    markets = response.json().get('data', [])
    
    if not markets:
        return {'error': 'No markets found'}
    
    # Get token source from first market
    token_source = markets[0]['base'].get('source')
    
    # Find bonding curve: pool type matches token source
    bonding_curve = next((m for m in markets if m.get('type') == token_source), None)
    
    if not bonding_curve:
        return {
            'status': 'no-bonding-curve',
            'message': f'No bonding curve found (source: {token_source})'
        }
    
    return {
        'bonding_curve_address': bonding_curve['address'],
        'bonding_percentage': bonding_curve.get('bondingPercentage'),
        'bonded': bonding_curve.get('bonded'),
        'source': token_source,
        'pool_type': bonding_curve.get('type'),
        'liquidity_usd': bonding_curve.get('liquidityUSD'),
        'exchange': bonding_curve['exchange']['name']
    }

# Example
result = get_bonding_curve_from_markets(
    'HLwEJQVCQMVdRhFg1YKKgNAnCwUk4eboMTtdBtpump',
    'solana'
)
print(result)

How It Works

Understanding the Matching Logic

When you query token/markets, you get ALL pools for a token:
{
  "data": [
    {
      "address": "7xKp...abc123",
      "type": "pumpfun",              // Pool type
      "bonded": false,
      "bondingPercentage": 67.5,
      "base": {
        "source": "pumpfun",          // Token source
        "address": "HLwEJQ...",
        "symbol": "MYTOKEN"
      }
    },
    {
      "address": "8yBp...def456",
      "type": "raydium-v4",           // Different pool type
      "bonded": true,
      "base": {
        "source": "pumpfun",          // Same token source
        "address": "HLwEJQ...",
        "symbol": "MYTOKEN"
      }
    }
  ]
}
The bonding curve pool is where type matches source:
  • Pool 1: type: "pumpfun" = source: "pumpfun" ✅ This is the bonding curve!
  • Pool 2: type: "raydium-v4"source: "pumpfun" ❌ This is a migrated pool

Response Structure (token/details)

{
  "data": {
    "address": "HLwEJQVCQMVdRhFg1YKKgNAnCwUk4eboMTtdBtpump",
    "symbol": "MYTOKEN",
    "source": "pumpfun",
    "bondingCurveAddress": "7xKp...abc123",
    "bonded": false,
    "bondingPercentage": 67.5,
    "poolAddress": "7xKp...abc123",
    "priceUSD": 0.000123
  }
}

Response Structure (token/markets)

{
  "data": [
    {
      "address": "7xKp...abc123",
      "type": "pumpfun",
      "blockchain": "Solana",
      "bonded": false,
      "bondingPercentage": 67.5,
      "exchange": {
        "name": "Pump.fun"
      },
      "liquidityUSD": 45000.50,
      "volume24hUSD": 125000.00,
      "base": {
        "address": "HLwEJQVCQMVdRhFg1YKKgNAnCwUk4eboMTtdBtpump",
        "symbol": "MYTOKEN",
        "source": "pumpfun"
      }
    }
  ]
}

Protocol-Specific Examples

Pump.fun (Solana)

async function getPumpfunBondingCurve(mintAddress: string) {
  const response = await client.fetchTokenMarkets({
    address: mintAddress,
    blockchain: 'solana'
  });
  
  // Find pool where type matches source "pumpfun"
  const pumpfunMarket = response.data.find(m => m.type === 'pumpfun');
  
  return pumpfunMarket ? {
    bondingCurveAddress: pumpfunMarket.address,
    progress: pumpfunMarket.bondingPercentage,
    bonded: pumpfunMarket.bonded,
    liquiditySOL: pumpfunMarket.liquidityUSD / 150
  } : null;
}

Clanker (Base)

async function getClankerBondingCurve(tokenAddress: string) {
  const response = await client.fetchTokenMarkets({
    address: tokenAddress,
    blockchain: 'base'
  });
  
  // Find pool where type matches source "clanker"
  const clankerMarket = response.data.find(m => m.type === 'clanker');
  
  return clankerMarket ? {
    bondingCurveAddress: clankerMarket.address,
    progress: clankerMarket.bondingPercentage,
    bonded: clankerMarket.bonded
  } : null;
}

Moonshot (Solana)

async function getMoonshotBondingCurve(mintAddress: string) {
  const response = await client.fetchTokenMarkets({
    address: mintAddress,
    blockchain: 'solana'
  });
  
  // Find pool where type matches source "moonshot"
  const moonshotMarket = response.data.find(m => m.type === 'moonshot');
  
  return moonshotMarket ? {
    bondingCurveAddress: moonshotMarket.address,
    progress: moonshotMarket.bondingPercentage,
    bonded: moonshotMarket.bonded
  } : null;
}

Generic Function for Any Protocol

async function getBondingCurveBySource(
  tokenAddress: string,
  blockchain?: string
) {
  const response = await client.fetchTokenMarkets({
    address: tokenAddress,
    blockchain: blockchain
  });
  
  const markets = response.data;
  if (markets.length === 0) return null;
  
  // Get token source
  const source = markets[0].base.source;
  
  // Find pool matching the source
  const bondingCurve = markets.find(m => m.type === source);
  
  return bondingCurve ? {
    bondingCurveAddress: bondingCurve.address,
    source: source,
    poolType: bondingCurve.type,
    bonded: bondingCurve.bonded,
    bondingPercentage: bondingCurve.bondingPercentage,
    exchange: bondingCurve.exchange.name
  } : null;
}

Advanced: Monitor Bonding Progress

async function monitorBondingProgress(tokenAddress: string, blockchain: string) {
  setInterval(async () => {
    const response = await client.fetchTokenDetails({
      address: tokenAddress,
      blockchain: blockchain
    });
    
    const token = response.data;
    
    if (token.bonded) {
      console.log('Token has completed bonding!');
      console.log(`Migrated from ${token.source} to new pool`);
      return;
    }
    
    console.log(`Source: ${token.source}`);
    console.log(`Progress: ${token.bondingPercentage?.toFixed(2)}%`);
    console.log(`Bonding curve: ${token.bondingCurveAddress}`);
    
    if (token.bondingPercentage && token.bondingPercentage > 95) {
      console.log('Warning: Bonding almost complete!');
    }
  }, 10000);
}

Source to Pool Type Mapping

Common bonding curve protocols and their identifiers:
ProtocolSource ValuePool Type ValueChains
Pump.funpumpfunpumpfunSolana
MoonshotmoonshotmoonshotSolana
ClankerclankerclankerBase
FlapflapflapBase, BNB
NadfunnadfunnadfunBase
ManamanamanaBase
Raydium Launchlabraydium-launchlabraydium-launchlabSolana
BoopboopboopSolana

Best Practices

Use token/details for simplicity: If you only need the bonding curve address, use token/details which directly returns bondingCurveAddress.
Match source with type: When using token/markets, find the pool where type matches the token’s source - that’s your bonding curve.
Bonding curve addresses become inactive: Once bonded: true, the bonding curve pool is no longer active. Token has migrated to a new pool.
Multiple pools are normal: token/markets returns ALL pools for a token (bonding curve + migrated pools). The bonding curve is just one of them.

Troubleshooting

Issue: No pool matches token source

Problem: No pool where type equals token source Solutions:
  1. Token may have completed bonding (check bonded: true)
  2. Token may not have originated from a bonding curve
  3. Use token/details to check bondingCurveAddress directly
  4. Verify token address is correct
Example:
// Token source: "pumpfun" but no pool with type: "pumpfun"
// This means token has completed bonding
const markets = response.data;
const source = markets[0].base.source; // "pumpfun"
const bondingCurve = markets.find(m => m.type === source); // null
// Check markets[0].bonded === true

Issue: bondingCurveAddress is null

Problem: token/details returns bondingCurveAddress: null Solutions:
  1. Token has completed bonding - check bonded: true
  2. Token didn’t launch via bonding curve
  3. Check poolAddress for current active pool

Issue: Multiple pools with same type

Problem: Multiple pools found with matching type Solution: This is unusual but possible. Choose based on:
  • Pool with bonded: false (active bonding curve)
  • Highest liquidityUSD
  • Most recent createdAt date

Comparison: token/details vs token/markets

Featuretoken/detailstoken/markets
Simplicity✅ Direct bondingCurveAddress fieldRequires finding pool where type matches source
Data ReturnedSingle token infoAll pools/markets for token
Use CaseQuick bonding curve lookupNeed all pool details, volumes, liquidity
PerformanceFaster (single token)Slower (lists all pools)
Best ForSimple bonding curve address lookupMarket analysis, comparing pools


Get Started

Create a free Mobula API key and start finding bonding curve addresses today!

Need Help?