JetForge

Trade Solana fair-launch tokens on JetForge: list active markets, get live bonding-curve quotes, check wallet balances and build unsigned buy or sell transactions for your own wallet to sign. No API key; no tool can move funds.

Hosted MCP Server

npx add-mcp 'https://jetforge.io/api/mcp'

Installs into Claude Code, Codex, Cursor and more

Documentation

Public REST API v1

Integrate JetForge bonding curve markets into your app, trading bot, or aggregator. Fetch live market data, get real-time quotes, and prepare unsigned Solana transactions. No API key or authentication required, and it can be called from any website. AI agents can connect through MCP; tools can read the OpenAPI spec.

● Live Report an issue →

Base URL

https://jetforge.io/api/v1

Auth

None required

Rate limit

300 req / min per IP

Returns the API status, current Solana slot, on-chain program ID, and network (devnet / mainnet). Use this to verify connectivity before making trade calls.

curl https://jetforge.io/api/v1/health
{
  "status": "ok",
  "version": "1.0.0",
  "network": "mainnet",
  "programId": "JetvDoU56t5vU6vo4RCkYmFUCatnQpLeXNWsuzxA4Cj",
  "slot": 312847651,
  "docs": "https://jetforge.io/docs/api"
}

List all active (non-graduated) JetForge markets with live on-chain bonding curve state — price, reserves, and graduation progress. Suitable for building market screeners or aggregator feeds.

Query Parameters

limitnumberoptionalNumber of markets to return. Default 50, max 200.

sortstringoptionalSort order: new (by creation time), trending (by volume), or graduating (closest to 85 SOL graduation).

curl "https://jetforge.io/api/v1/markets?limit=5&sort=trending"
{
  "markets": [
    {
      "marketId": "EKpQ...cjm",
      "name": "WIF",
      "baseSymbol": "WIF",
      "quoteSymbol": "SOL",
      "baseMint": "EKpQ...cjm",
      "bondingCurveAddress": "7xQ1...pda",
      "creator": "9xDe...xyz",
      "decimals": 6,
      "price": 0.00000003,
      "priceSOL": 0.00000003,
      "virtualSolReserves": "30000000000",
      "virtualTokenReserves": "947058823529412",
      "realSolReserves": "1294200000",
      "realTokenReserves": "42731050000000",
      "graduated": false,
      "progress": 1.5,
      "imageUrl": "https://...",
      "createdAt": "2026-05-24T10:00:00Z"
    }
  ],
  "count": 1,
  "network": "mainnet"
}

Get a real-time trade quote directly from the on-chain bonding curve state. Returns expected output, fee, price impact, and minimum output after slippage. No transaction is sent.

Query Parameters

mintstringrequiredToken mint address (base58).

sidestringrequiredTrade direction: buy or sell.

amountnumberrequiredFor buy: SOL amount in lamports (1 SOL = 1,000,000,000). For sell: token amount in base units (6 decimals).

slippageBpsnumberoptionalSlippage tolerance in basis points. Default 300 (3%).

# Buy quote: 0.1 SOL (100,000,000 lamports)
curl "https://jetforge.io/api/v1/quote?mint=Acbazrpvf9kMMcsUCVf7J6hcUwsF3jFJRQ7DsWE6qjet&side=buy&amount=100000000"

# Sell quote: 1,000 tokens (1,000,000,000 base units at 6 decimals)
curl "https://jetforge.io/api/v1/quote?mint=Acbazrpvf9kMMcsUCVf7J6hcUwsF3jFJRQ7DsWE6qjet&side=sell&amount=1000000000"
{
  "quote": {
    "marketId": "EKpQ...cjm",
    "bondingCurveAddress": "7xQ1...pda",
    "side": "buy",
    "inputAmount": "100000000",
    "inputAmountHuman": "0.100000 SOL",
    "outputAmount": "3464437480000",
    "outputAmountHuman": "3,464,437.48 tokens",
    "price": 0.000000029,
    "priceImpactPct": 0.655,
    "estimatedFeeLamports": "1000000",
    "minimumOutput": "3354163955600",
    "slippageBps": 300,
    "solCharged": "100000000",
    "partialFill": false,
    "curveState": {
      "virtualSolReserves": "30000000000",
      "virtualTokenReserves": "947058823529412",
      "realSolReserves": "1294200000",
      "realTokenReserves": "42731050000000"
    }
  }
}

Build a fully-formed, unsigned Solana transaction for a buy or sell on the JetForge bonding curve. The server constructs the instruction, sets the fee payer and recent blockhash, then returns the transaction as base64. You sign it with your own wallet and broadcast it — JetForge never touches your private key.

Request Body (JSON)

mintstringrequiredToken mint address.

sidestringrequired"buy" or "sell".

amountstringrequiredLamports for buy, token base units for sell. Pass as string to avoid precision loss.

walletPublicKeystringrequiredCaller's wallet public key (base58). Used as fee payer.

slippageBpsnumberoptionalSlippage tolerance in basis points. Default 300.

curl -X POST https://jetforge.io/api/v1/trade/prepare \
  -H "Content-Type: application/json" \
  -d '{
    "mint": "Acbazrpvf9kMMcsUCVf7J6hcUwsF3jFJRQ7DsWE6qjet",
    "side": "buy",
    "amount": "100000000",
    "walletPublicKey": "YOUR_WALLET_PUBLIC_KEY",
    "slippageBps": 300
  }'
{
  "transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADe...base64==",
  "message": "Sign and broadcast this transaction to buy on JetForge",
  "quote": {
    "marketId": "EKpQ...cjm",
    "side": "buy",
    "inputAmount": "100000000",
    "outputAmount": "3464437480000",
    "fee": "1000000",
    "minimumOutput": "3354163955600",
    "slippageBps": 300,
    "solCharged": "100000000",
    "partialFill": false,
    "blockhash": "8ZyJ...",
    "lastValidBlockHeight": 312847751
  },
  "instructions": {
    "description": "Sign with your wallet and sendRawTransaction",
    "code": [
      "const tx = Transaction.from(Buffer.from(response.transaction, 'base64'));",
      "const signed = await wallet.signTransaction(tx);",
      "const sig = await connection.sendRawTransaction(signed.serialize());",
      "await connection.confirmTransaction(sig, 'confirmed');"
    ]
  }
}

Get the SOL balance and all SPL token positions for any Solana wallet. Optionally filter to specific mints. Useful for building portfolio views or checking a bot wallet's holdings before trading.

Query Parameters

mintsstringoptionalComma-separated list of mint addresses to filter. Omit to return all token accounts.

# All positions
curl "https://jetforge.io/api/v1/wallet/YOUR_WALLET_ADDRESS"

# Filter to specific tokens
curl "https://jetforge.io/api/v1/wallet/YOUR_WALLET_ADDRESS?mints=Acbazrpvf9kMMcsUCVf7J6hcUwsF3jFJRQ7DsWE6qjet"
{
  "wallet": "9xDe...xyz",
  "solBalance": "2450000000",
  "solBalanceSOL": 2.45,
  "tokenCount": 2,
  "tokens": [
    {
      "mint": "EKpQ...cjm",
      "balance": "3464437480000",
      "balanceUi": 3464437.48,
      "decimals": 6,
      "ataAddress": "4rT2...ata"
    }
  ]
}

Connect an AI Agent (MCP)

AI agents can use JetForge through its Model Context Protocol server. It uses Streamable HTTP and needs no API key.

Server URL

https://jetforge.io/api/mcp

Claude Code

claude mcp add --transport http jetforge https://jetforge.io/api/mcp

Other MCP clients

{
  "mcpServers": {
    "jetforge": {
      "type": "http",
      "url": "https://jetforge.io/api/mcp"
    }
  }
}

Tools

list_markets

Active tokens with live price, reserves and graduation progress.

get_quote

Price a buy or sell from the live curve, with fee, price impact and minimum output.

prepare_trade

Build an unsigned transaction for the agent's wallet to sign and broadcast.

get_wallet

SOL and token balances of any wallet.

No tool can move funds. prepare_trade only returns an unsigned transaction, and your agent's own wallet signs and broadcasts it. Never give a private key to an MCP server, including this one. Limit how much the agent's wallet holds, and check each transaction before it's signed.

SDK Examples

Full buy flow in TypeScript: get a quote, prepare the unsigned transaction, inspect it, then sign and broadcast. The inspection checks the fee payer, that only the JetForge, compute-budget and associated-token programs appear, the token mint, the amount and the slippage floor. Never sign a transaction you haven't checked, from any API.

Website (React + wallet adapter)

// React (Next.js / Vite) — inside your <WalletProvider> + <ConnectionProvider>.
import { useCallback } from "react";
import { useConnection, useWallet } from "@solana/wallet-adapter-react";
import { ComputeBudgetProgram, PublicKey, Transaction } from "@solana/web3.js";
import { ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";

const BASE = "https://jetforge.io/api/v1";
const JETFORGE_PROGRAM = new PublicKey("JetvDoU56t5vU6vo4RCkYmFUCatnQpLeXNWsuzxA4Cj");
const MAX_MICROLAMPORTS = 1_000_000; // refuse silly priority fees

/** fetch + JSON that throws on HTTP errors instead of passing { error } along. */
async function api<T>(path: string, init?: RequestInit): Promise<T> {
  const res = await fetch(\`${BASE}${path}\`, init);
  const body = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(body.error ?? \`HTTP ${res.status}\`);
  return body as T;
}

/**
 * Check the server-built transaction BEFORE signing it: fee payer, programs,
 * mint, amount and slippage floor. Never sign a transaction you haven't checked.
 */
function inspect(tx: Transaction, o: { wallet: PublicKey; mint: PublicKey; amount: bigint; minOut: bigint }) {
  if (!tx.feePayer?.equals(o.wallet)) throw new Error("Fee payer is not your wallet");
  const allowed = [JETFORGE_PROGRAM, ComputeBudgetProgram.programId, ASSOCIATED_TOKEN_PROGRAM_ID];
  const jf = tx.instructions.filter((ix) => ix.programId.equals(JETFORGE_PROGRAM));
  for (const ix of tx.instructions) {
    if (!allowed.some((p) => p.equals(ix.programId))) throw new Error(\`Unexpected program ${ix.programId}\`);
    if (ix.programId.equals(ComputeBudgetProgram.programId) && ix.data[0] === 3) {
      const microLamports = ix.data.readBigUInt64LE(1); // SetComputeUnitPrice
      if (microLamports > BigInt(MAX_MICROLAMPORTS)) throw new Error("Priority fee too high");
    }
  }
  if (jf.length !== 1) throw new Error("Expected exactly one JetForge instruction");
  const [ix] = jf;
  if (!ix.keys.some((k) => k.pubkey.equals(o.mint))) throw new Error("Instruction is for a different token");
  // data = 8-byte discriminator | amount (u64 LE) | minimum output (u64 LE)
  if (ix.data.readBigUInt64LE(8) !== o.amount) throw new Error("Amount differs from your request");
  if (ix.data.readBigUInt64LE(16) < o.minOut) throw new Error("Slippage protection is weaker than quoted");
}

export function useJetForgeBuy() {
  const { connection } = useConnection();
  const { publicKey, signTransaction } = useWallet();

  return useCallback(async (mint: string, lamports: bigint, slippageBps = 300) => {
    if (!publicKey || !signTransaction) throw new Error("Connect a wallet first");

    // 1. Quote
    const { quote } = await api<any>(\`/quote?mint=${mint}&side=buy&amount=${lamports}\`);
    const expectedOut = BigInt(quote.outputAmount);
    const minOut = (expectedOut * BigInt(10_000 - slippageBps)) / 10_000n;

    // 2. Unsigned transaction, built by the server
    const prepared = await api<any>("/trade/prepare", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({
        mint,
        side: "buy",
        amount: lamports.toString(),
        walletPublicKey: publicKey.toBase58(),
        slippageBps,
      }),
    });
    const tx = Transaction.from(Buffer.from(prepared.transaction, "base64"));

    // 3. Inspect, then sign and send
    // (the price can move between the quote and this call; allow 1% drift on top of the slippage)
    inspect(tx, { wallet: publicKey, mint: new PublicKey(mint), amount: lamports, minOut: (minOut * 99n) / 100n });
    const signed = await signTransaction(tx);
    const sig = await connection.sendRawTransaction(signed.serialize());
    await connection.confirmTransaction(
      { signature: sig, blockhash: tx.recentBlockhash!, lastValidBlockHeight: prepared.quote.lastValidBlockHeight },
      "confirmed"
    );
    return sig;
  }, [connection, publicKey, signTransaction]);
}

// In a component:
//   const buy = useJetForgeBuy();
//   const sig = await buy("Acbazrpvf9kMMcsUCVf7J6hcUwsF3jFJRQ7DsWE6qjet", 100_000_000n); // 0.1 SOL

Bot (Node.js + keypair)

// Node.js bot — signs with a local keypair file. Keep that key on the bot's
// machine only, fund it with what the bot may spend, and never commit it.
import { readFileSync } from "node:fs";
import { ComputeBudgetProgram, Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
import { ASSOCIATED_TOKEN_PROGRAM_ID } from "@solana/spl-token";

const BASE = "https://jetforge.io/api/v1";
const JETFORGE_PROGRAM = new PublicKey("JetvDoU56t5vU6vo4RCkYmFUCatnQpLeXNWsuzxA4Cj");
const connection = new Connection(process.env.RPC_URL ?? "https://api.mainnet-beta.solana.com", "confirmed");
const wallet = Keypair.fromSecretKey(Uint8Array.from(JSON.parse(readFileSync(process.env.KEYPAIR_PATH!, "utf8"))));

async function api(path: string, init?: RequestInit) {
  const res = await fetch(\`${BASE}${path}\`, init);
  const body = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(body.error ?? \`HTTP ${res.status}\`);
  return body;
}

async function buy(mint: string, lamports: bigint, slippageBps = 300) {
  const { quote } = await api(\`/quote?mint=${mint}&side=buy&amount=${lamports}\`);
  const minOut = (BigInt(quote.outputAmount) * BigInt(10_000 - slippageBps)) / 10_000n;

  const prepared = await api("/trade/prepare", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ mint, side: "buy", amount: lamports.toString(), walletPublicKey: wallet.publicKey.toBase58(), slippageBps }),
  });
  const tx = Transaction.from(Buffer.from(prepared.transaction, "base64"));

  // Inspect before signing (same checks as the React example)
  if (!tx.feePayer?.equals(wallet.publicKey)) throw new Error("Fee payer is not this wallet");
  const allowed = [JETFORGE_PROGRAM, ComputeBudgetProgram.programId, ASSOCIATED_TOKEN_PROGRAM_ID];
  if (tx.instructions.some((ix) => !allowed.some((p) => p.equals(ix.programId)))) throw new Error("Unexpected program");
  const jf = tx.instructions.filter((ix) => ix.programId.equals(JETFORGE_PROGRAM));
  if (jf.length !== 1 || !jf[0].keys.some((k) => k.pubkey.equals(new PublicKey(mint)))) throw new Error("Bad JetForge instruction");
  if (jf[0].data.readBigUInt64LE(8) !== lamports) throw new Error("Amount differs");
  if (jf[0].data.readBigUInt64LE(16) < (minOut * 99n) / 100n) throw new Error("Slippage protection too weak");

  tx.sign(wallet);
  const sig = await connection.sendRawTransaction(tx.serialize());
  await connection.confirmTransaction(
    { signature: sig, blockhash: tx.recentBlockhash!, lastValidBlockHeight: prepared.quote.lastValidBlockHeight },
    "confirmed"
  );
  return sig;
}

buy("Acbazrpvf9kMMcsUCVf7J6hcUwsF3jFJRQ7DsWE6qjet", 100_000_000n) // 0.1 SOL
  .then((sig) => console.log("TX:", sig))
  .catch((e) => { console.error("Trade failed:", e.message); process.exit(1); });

Error Responses

400

Bad Request

Missing or invalid parameters.

404

Not Found

Mint not found on JetForge.

429

Too Many Requests

Over 300 requests/min from your IP. Wait and retry; see the RateLimit-* headers.

500

Server Error

RPC or internal error. Retry with backoff.

{ "error": "Human-readable error message" }

On-Chain Details

Program ID

JetvDoU56t5vU6vo4RCkYmFUCatnQpLeXNWsuzxA4Cj

Treasury

FpRb8wNaSg1zcezV3gm7L2rFAv8mNw7HH6Vhuauxkd6K

Fee

1% per trade — split: 40% creator / 40% treasury / 20% buyback

Graduation

85 SOL real reserves → auto-migrate to Raydium

AMM

Constant-product bonding curve (k = virtualSol × virtualTokens)