Skip to main content
BETAThe Prediction Market API is currently in beta and subject to breaking changes as we continue to improve the product. If you have any feedback, please reach out in Discord.
Jupiter Forecast is a native prediction market on Solana. The current markets are 15-minute Bitcoin up/down rounds: buy the side you think BTC will land on, settled from the Chainlink BTC/USD price. Forecast runs on the existing Prediction API under the bisonfi provider, and its outcome tokens can also be traded directly through the Swap API.

Markets

Each Forecast event is one 15-minute BTC round with two binary markets:
Market IDWins when
BISON-<marketPda>-UPBTC’s price at close is at or above its open
BISON-<marketPda>-DOWNBTC’s price at close is below its open
Each side is a Token-2022 mint (outcomeMint) worth $1 if it wins and $0 if it loses. Rounds are scheduled by the issuer rather than rotating continuously, so poll /events to find live ones. A round accepts orders only between its openTime and closeTime: a closed round returns bisonfi_market_not_tradable, while an open round with no routable liquidity returns no_route.

Two ways to integrate

PathEndpointsUse it when
Prediction API (recommended)/prediction/v1/orders + /prediction/v1/executeYou want Jupiter to track positions, P&L, and settlement.
Swap API/swap/v2/order + /swap/v2/executeYou manage your own position tracking and trade the outcome tokens directly.
Both paths trade the same tokens: a Prediction API order executes as an atomic USDC-to-outcome-token swap, which is why the outcomeMint is also tradable through the Swap API. Deposits are USDC only. The Prediction API path enforces a 5 to 250 USDC order size at build time; the direct Swap API path has no such cap.

Prerequisites

import { Keypair, VersionedTransaction } from "@solana/web3.js";

const PREDICTION_API = "https://api.jup.ag/prediction/v1";
const SWAP_API = "https://api.jup.ag/swap/v2";
const API_KEY = process.env.JUPITER_API_KEY!;
const USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const wallet = Keypair.fromSecretKey(/* your secret key */);

Prediction API path

Every order, buy or sell, is built by the API, signed by you, then submitted to /execute with the build’s execution.context. This helper covers the sign-and-execute half:
async function signAndExecute(build: any) {
  if (!build.transaction) throw new Error(`build failed: ${build.code} ${build.message}`);

  const tx = VersionedTransaction.deserialize(Buffer.from(build.transaction, "base64"));
  tx.sign([wallet]);

  const res = await fetch(`${PREDICTION_API}/execute`, {
    method: "POST",
    headers: { "content-type": "application/json", "x-api-key": API_KEY },
    body: JSON.stringify({
      signedTransaction: Buffer.from(tx.serialize()).toString("base64"),
      context: build.execution.context,
    }),
  });
  const result = await res.json(); // { status: "Success", signature, error: null }
  if (result.status !== "Success" || result.error) {
    throw new Error(`execute failed: ${JSON.stringify(result)}`);
  }
  return result.signature;
}

Discover markets

Filter events by the bisonfi provider, with markets inline:
async function getForecastMarkets() {
  const res = await fetch(
    `${PREDICTION_API}/events?provider=bisonfi&category=crypto&tag=15m&includeMarkets=true`,
    { headers: { "x-api-key": API_KEY } }
  );
  const { data } = await res.json();
  return data
    .flatMap((event: any) => event.markets ?? [])
    .filter((market: any) => market.provider === "bisonfi" && market.tradable);
}

Buy

const build = await fetch(`${PREDICTION_API}/orders`, {
  method: "POST",
  headers: { "content-type": "application/json", "x-api-key": API_KEY },
  body: JSON.stringify({
    ownerPubkey: wallet.publicKey.toBase58(),
    marketId: market.marketId, // from getForecastMarkets(), e.g. BISON-<marketPda>-UP
    isBuy: true,
    isYes: true,               // always true for Forecast; the market ID selects the side
    depositAmount: "5000000",  // micro-USDC (6 decimals): 5 USDC
    depositMint: USDC_MINT,
  }),
}).then((r) => r.json());

const signature = await signAndExecute(build);
Forecast orders settle atomically (executionModel: "atomic_swap"), so there is no keeper order to poll: the position appears in GET /positions as soon as /execute returns. See Create Order for the full build response.

Sell

DELETE returns a sell transaction in the same shape, so it reuses the helper. You can sell only while the round is tradable, down to a minimum of 0.01 tokens (smaller dust returns bisonfi_position_too_small). There is no claim step for Forecast: after a round resolves, winning tokens settle automatically and are redeemed on the issuer side.
const build = await fetch(`${PREDICTION_API}/positions/${positionPubkey}`, {
  method: "DELETE",
  headers: { "content-type": "application/json", "x-api-key": API_KEY },
  body: JSON.stringify({ ownerPubkey: wallet.publicKey.toBase58() }),
}).then((r) => r.json());

const signature = await signAndExecute(build);
Read holdings with GET /positions?ownerPubkey=<wallet> (filter to Forecast by a marketId starting BISON-); see Position Data & History for the fields.
Amount fields (contractsMicro, valueUsd, depositAmount) are strings in base units (1000000 = 1 contract or $1.00) and can exceed JavaScript’s safe-integer range. Parse them with BigInt or a decimal library, never parseFloat or Number.

Swap API path

To manage your own position tracking, trade the outcome token directly as a swap from USDC into market.outcomeMint, using the standard Order & Execute flow. Use a high slippageBps because outcome prices swing sharply near expiry. To exit, swap the outcome token back to USDC.
const params = new URLSearchParams({
  inputMint: USDC_MINT,
  outputMint: market.outcomeMint,
  amount: "5000000",
  taker: wallet.publicKey.toBase58(),
  slippageBps: "10000",
});
const order = await fetch(`${SWAP_API}/order?${params}`, {
  headers: { "x-api-key": API_KEY },
}).then((r) => r.json());
if (!order.transaction) throw new Error(`swap order failed: ${order.error}`);

const tx = VersionedTransaction.deserialize(Buffer.from(order.transaction, "base64"));
tx.sign([wallet]);

const result = await fetch(`${SWAP_API}/execute`, {
  method: "POST",
  headers: { "content-type": "application/json", "x-api-key": API_KEY },
  body: JSON.stringify({
    signedTransaction: Buffer.from(tx.serialize()).toString("base64"),
    requestId: order.requestId,
  }),
}).then((r) => r.json());

Reference

Verified on-chain configuration for Forecast (mainnet-beta):
ItemValue
Issuer program2sVcg2dBSUzXkmdZ8M5cp1LbnzDrWJmr6hktkHwB8nY3
Config PDA8LczfBkVZJhGnTYH8nQke2YC3b83GFZ8qZtfuMRe6AN6
Deposit / settlement mintUSDC EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Outcome token programToken-2022 TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
BTC Chainlink feed0x00039d9e45394f473ab1f050a1b963e6b05351e52d71e507509ada0c95ed75b8

Execute Order

API reference and “try it” panel for /execute.

About Prediction

How events, markets, positions, and settlement work.

Order & Execute

The Swap API flow behind the direct outcome-token path.

Position Data & History

Read holdings, P&L, and trade history.