Comprehensive guidance for integrating Jupiter APIs (Swap, Lend, Perps, Trigger, Recurring, Tokens, Price, Portfolio, Prediction Markets, Send, Studio, Lock, Routing). Use for endpoint selection, integration flows, error handling, and production hardening.
71
—
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Single skill for all Jupiter APIs, optimized for fast routing and deterministic execution.
Base URL: https://api.jup.ag
Auth: x-api-key from developers.jup.ag (required for Jupiter REST endpoints)
Use when:
Do not use when:
buy, sell, trade assume a DeFi domain).Triggers: swap, quote, gasless, best route, buy, sell, trade, convert, token exchange, jupiter api, jup.ag, ultra, metis, ultra swap, ultra api, ultra-api.jup.ag, lend, borrow, earn, yield, apy, deposit, liquidation, perps, leverage, long, short, position, futures, margin trading, limit order, trigger, price condition, dca, recurring, scheduled swaps, token metadata, token search, verification, shield, price, valuation, price feed, portfolio, positions, holdings, prediction markets, market odds, event market, invite transfer, send, clawback, create token, studio, claim fee, vesting, distribution lock, unlock schedule, dex integration, rfq integration, routing engine, status page, health check, service health, accumulate, auto-buy
import { Connection, Keypair, VersionedTransaction } from '@solana/web3.js';
const API_KEY = process.env.JUPITER_API_KEY!; // from developers.jup.ag
if (!API_KEY) throw new Error('Missing JUPITER_API_KEY');
const BASE = 'https://api.jup.ag';
const headers = { 'x-api-key': API_KEY };
async function jupiterFetch<T>(path: string, init?: RequestInit): Promise<T> {
const res = await fetch(`${BASE}${path}`, {
...init,
headers: { ...headers, ...init?.headers },
});
if (res.status === 429) throw { code: 'RATE_LIMITED', retryAfter: Number(res.headers.get('Retry-After')) || 10 };
if (!res.ok) {
const raw = await res.text();
let body: any = { message: raw || `HTTP_${res.status}` };
try {
body = raw ? JSON.parse(raw) : body;
} catch {
// keep text fallback body
}
throw { status: res.status, ...body };
}
return res.json();
}
// Sign and send any Jupiter transaction
async function signAndSend(
txBase64: string,
wallet: Keypair,
connection: Connection,
additionalSigners: Keypair[] = []
): Promise<string> {
const tx = VersionedTransaction.deserialize(Buffer.from(txBase64, 'base64'));
tx.sign([wallet, ...additionalSigners]);
const sig = await connection.sendRawTransaction(tx.serialize(), {
maxRetries: 0,
skipPreflight: true,
});
return sig;
}| User intent | API family | First action |
|---|---|---|
| Swap/quote | Swap | GET /swap/v2/order -> sign -> POST /swap/v2/execute |
| Lend/borrow/yield | Lend | POST /lend/v1/earn/deposit or /withdraw |
| Leverage/perps | Perps | On-chain via Anchor IDL (no REST API yet) |
| Limit orders | Trigger | JWT auth -> POST /trigger/v2/orders/price |
| DCA/recurring buys | Recurring | POST /recurring/v1/createOrder -> sign -> POST /recurring/v1/execute |
| Token search | Tokens | GET /tokens/v2/search?query={mint} |
| Token verification/metadata update | Use jupiter-vrfd skill | Defer — not handled by this skill |
| Price lookup | Price | GET /price/v3?ids={mints} |
| Portfolio/positions | Portfolio | GET /portfolio/v1/positions/{address} |
| Prediction market integration | Prediction Markets | GET /prediction/v1/events -> POST /prediction/v1/orders |
| Invite send/clawback | Send | POST /send/v1/craft-send -> sign -> send to RPC |
| Token creation/fees | Studio | POST /studio/v1/dbc-pool/create-tx -> upload -> submit |
| Vesting/distribution | Lock | On-chain program LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn |
| DEX/RFQ integration | Routing | Choose DEX (AMM trait) vs RFQ (webhook) path |
Use each block as a minimal execution contract. Fetch the linked refs for full request/response shapes, TypeScript interfaces, and parameter details.
Base URL: https://api.jup.ag/swap/v2
Triggers: swap, quote, gasless, best route
Fee: Variable by pair — 0 bps (Jupiter tokens/pegged), 2 bps (SOL-Stable), 5 bps (LST-Stable), 10 bps (most pairs), 50 bps (tokens < 24h). Referral fees: 50-255 bps (Jupiter retains 20%).
Rate Limit: 50 req/10s base, scales with 24h execute volume (see Rate Limits)
Endpoints: /order (GET), /execute (POST), /build (GET, Metis-only raw instructions)
Routing: 4 routers compete — Metis (metis), JupiterZ (jupiterz), Dflow (dflow), OKX (okx). The router response field returns one of these values. swapType is aggregator (Metis, Dflow, or OKX) or rfq (JupiterZ). Response mode field: "ultra" (all routers, default params) or "manual" (restricted by optional params). /build uses Metis only.
Gasless: Three paths — automatic (Jupiter-covered), JupiterZ (MM-covered), integrator-payer (payer param, Metis-only routing). Eligibility varies by balance, trade size, and parameters used. See Gasless docs for current thresholds and disqualifying params.
Gotchas:
referralAccount + referralFee disable JupiterZ only (Metis/Dflow/OKX remain); payer (integrator gasless) restricts routing to Metis only (disables JupiterZ, Dflow, and OKX); receiver does NOT restrict routing, but must differ from taker (receiver=taker returns 400 "Receiver cannot be same as taker")./build transactions cannot use /execute — self-manage via RPC.Migrating from an older integration? Use the jupiter-swap-migration skill.
Refs: Overview | Order & Execute | Build | Gasless | Migration | OpenAPI
Fees are documented inline on the Order & Execute and Build pages; router competition and the parameter routing-impact matrix are on the Overview and Order & Execute pages.
Common error codes returned by /swap/v2/execute with recommended actions:
| Code | Category | Meaning | Retryable | Action |
|---|---|---|---|---|
0 | Success | Transaction confirmed | — | — |
-1 | Execute | Missing/expired cached order | Yes | Re-quote and retry |
-2 | Execute | Invalid signed transaction | No | Fix transaction signing |
-3 | Execute | Invalid message bytes | No | Fix serialization |
-1000 | Aggregator | Failed landing attempt | Yes | Re-quote with adjusted params |
-1001 | Aggregator | Unknown error | Yes | Retry with backoff |
-1002 | Aggregator | Invalid transaction | No | Fix transaction construction |
-1003 | Aggregator | Transaction not fully signed | No | Ensure all required signers |
-1004 | Aggregator | Invalid block height | Yes | Re-quote (stale blockhash) |
-2000 | RFQ | Failed landing | Yes | Re-quote and retry |
-2001 | RFQ | Unknown error | Yes | Retry with backoff |
-2002 | RFQ | Invalid payload | No | Fix request payload |
-2003 | RFQ | Quote expired | Yes | Re-quote and retry |
-2004 | RFQ | Swap rejected | Yes | Re-quote, possibly different route |
429 | Rate limit | Rate limited | Yes | Exponential backoff, wait 10s window |
On success, /execute returns { status: "Success", code: 0, signature, inputAmountResult, outputAmountResult, slot, totalInputAmount, totalOutputAmount }. On failure it returns status: "Failed" with a non-zero code and an error string. inputAmountResult/outputAmountResult are the actual on-chain amounts; reconcile against your quote.
https://api.jup.ag/lend/v1lend, borrow, earn, liquidationjup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9, Borrow jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi@jup-ag/lend (TypeScript)/earn/deposit (POST), /earn/withdraw (POST), /earn/mint (POST), /earn/redeem (POST), /earn/deposit-instructions (POST), /earn/withdraw-instructions (POST), /earn/tokens (GET), /earn/positions (GET), /earn/earnings (GET)VersionedTransaction.@jup-ag/lend and @jup-ag/lend-read, use the jupiter-lend skill.perps, leverage, long, short, positionhttps://api.jup.ag/trigger/v2limit order, trigger, price conditionx-api-key (all requests) + Authorization: Bearer <jwt> (order mutations). JWT obtained via challenge-response: POST /auth/challenge → sign challenge with wallet → POST /auth/verify → receive token. JWT expiry does NOT affect open orders — they continue executing./auth/challenge (POST, body: walletPubkey + type), /auth/verify (POST, body: type + walletPubkey + base58 signature), /vault (GET), /vault/register (GET), /deposit/craft (POST), /orders/price (POST create, PATCH update), /orders/price/cancel/{orderId} (POST, initiates withdrawal), /orders/price/confirm-cancel/{orderId} (POST, submits signed withdrawal + cancelRequestId), /orders/history (GET, wallet implicit via JWT)single (one directional trigger), oco (take-profit + stop-loss pair), otoco (entry trigger + OCO). triggerCondition: "above" or "below".GET /vault/register (register if new; returns 409 "Vault already registered" if it exists, which is fine), POST /deposit/craft (returns transaction + requestId; the body MUST include orderType: "price" and orderSubType (single/oco/otoco)), sign deposit tx, then POST /orders/price with depositRequestId + depositSignedTx.POST /cancel/{orderId} returns transaction + requestId; sign, then POST /confirm-cancel/{orderId} with signedTransaction + cancelRequestId.id (not orderId); order history objects use orderState/rawState (no status field).https://api.jup.ag/recurring/v1dca, recurring, scheduled swaps/createOrder (POST), /cancelOrder (POST), /execute (POST), /getRecurringOrders (GET)params.time for order scheduling; price-based ordering is not supported.https://api.jup.ag/tokens/v2token metadata, token search, shield/search?query={q} (GET, comma-separate mints, max 100), /tag?query={tag} (GET, verified or lst), /{category}/{interval} (GET, categories: toporganicscore, toptraded, toptrending; intervals: 5m, 1h, 6h, 24h), /recent (GET)isVerified (boolean) plus organicScore (0-100) and organicScoreLabel (high/medium/low).audit.isSus — a boolean present ONLY when true (a safe token has no isSus key at all), so check it defensively as token.audit?.isSus === true, never read it directly. Verified live on flagged tokens (dev holds ~100% of supply); those also carry isVerified: null and organicScoreLabel: "low". Absence of isSus is NOT proof of safety — not all risky tokens carry the flag.audit fields the live API returns: mintAuthorityDisabled, freezeAuthorityDisabled, topHoldersPercentage, devBalancePercentage, devMigrations, devMints (all nullable, any may be absent; devMigrations is returned but missing from the current OpenAPI schema).https://api.jup.ag/price/v3price, valuation, price feed/price/v3?ids={mints} (GET, comma-separated)usdPrice, blockId, decimals, priceChange24h, liquidity, createdAt (some tokens add conditional fields like launchpad, stockData, scaledUiConfig). Price API V3 has NO confidenceLevel field (that was V2).null placeholder). Fail closed when a requested mint is missing from the response for safety-sensitive actions. Use blockId to check price recency.https://api.jup.ag/portfolio/v1portfolio, positions, holdings/positions/{address} (GET), /positions/{address}?platforms={ids} (GET), /platforms (GET), /staked-jup/{address} (GET)multiple, liquidity, trade, leverage, borrowlend.https://api.jup.ag/prediction/v1prediction markets, market odds, event marketJuprjznTrTSp2UFa3ZBUFgwdAmtZCq4MQCwysN55USD), USDC/events (GET, returns {data, pagination}), /events/search (GET), /markets/{marketId} (GET), /orderbook/{marketId} (GET, returns {yes, no, yes_dollars, no_dollars}), /orders (POST, requires isBuy boolean + ownerPubkey), /orders/status/{orderPubkey} (GET), /positions (GET, ?ownerPubkey=), /positions/{positionPubkey} (DELETE), /positions/{positionPubkey}/claim (POST), /history (GET), /leaderboards (GET)position.claimable before claiming. Winners get $1/contract.provider, marketId, result, resolveAt, outcomes, clobTokenIds live at the top level, not nested under metadata.contractsMicro (1,000,000 = 1 contract) or contractsDecimal, not the legacy whole-number contracts.https://api.jup.ag/send/v1invite transfer, send, clawback/craft-send (POST), /craft-clawback (POST), /pending-invites (GET), /invite-history (GET)https://api.jup.ag/studio/v1create token, studio, claim fee/dbc-pool/create-tx (POST), /dbc-pool/submit (POST, multipart/form-data), /dbc-pool/addresses/{mint} (GET), /dbc/fee (POST), /dbc/fee/create-tx (POST)/dbc-pool/submit/dbc-pool/submit (not externally) for token to get a Studio page on jup.ag. Error codes: 403 = not authorized for pool, 404 = proxy account not found.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjnvesting, distribution lock, unlock schedulecli/src/bin/instructions directory.dex integration, rfq integration, routing enginejupiter-amm-interface crate. Critical: No network calls in implementation (accounts are pre-batched and cached). Ref impl: github.com/jup-ag/rust-amm-implementation/jupiter/rfq/quote (POST, 250ms), /jupiter/rfq/swap (POST), /jupiter/rfq/tokens (GET). Reqs: 95% fill rate, 250ms response, 55s expiry. SDK: github.com/jup-ag/rfq-webhook-toolkitSwap API (dynamic, volume-based):
| 24h Execute Volume | Requests per 10s window |
|---|---|
| $0 | 50 |
| $10,000 | 51 |
| $100,000 | 61 |
| $1,000,000 | 165 |
Quotas recalculate every 10 minutes. Pro plan does NOT increase Swap API limits.
Other APIs: Managed at portal level. Check portal rate limits.
On HTTP 429: Exponential backoff with jitter: delay = min(baseDelay * 2^attempt + random(0, jitter), maxDelay). Wait for 10s sliding window refresh. Do NOT burst aggressively.
x-api-key is missing or invalid./execute accepts same signedTransaction + requestId for up to 2 min without duplicate execution.requestId, API family, endpoint, latency, status, and error code.retry, adjust params, insufficient balance, rate limited).interface JupiterResult<T> {
ok: boolean;
result?: T;
error?: { code: string | number; message: string; retryable: boolean };
}
async function jupiterAction<T>(action: () => Promise<T>): Promise<JupiterResult<T>> {
try {
const result = await action();
return { ok: true, result };
} catch (error: any) {
const code = error?.code ?? error?.status ?? 'UNKNOWN';
// Rate limit — retry with backoff
if (code === 429 || code === 'RATE_LIMITED') {
return { ok: false, error: { code: 'RATE_LIMITED', message: 'Rate limited', retryable: true } };
}
// Swap execute errors (negative codes)
if (typeof code === 'number' && code < 0) {
const retryable = [-1, -1000, -1001, -1004, -2000, -2001, -2003, -2004].includes(code);
return { ok: false, error: { code, message: error?.error ?? 'Execute failed', retryable } };
}
// Program errors (positive codes like 6001 = slippage)
if (typeof code === 'number' && code > 0) {
return { ok: false, error: { code, message: error?.error ?? 'Program error', retryable: false } };
}
return { ok: false, error: { code, message: error?.message ?? 'UNKNOWN_ERROR', retryable: false } };
}
}
async function withRetry<T>(action: () => Promise<T>, maxRetries = 3): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const result = await jupiterAction(action);
if (result.ok) return result.result!;
if (!result.error?.retryable || attempt === maxRetries) throw result.error;
const delay = Math.min(1000 * 2 ** attempt + Math.random() * 500, 10000);
await new Promise(r => setTimeout(r, delay));
}
throw new Error('Retry exhausted');
}Production-ready code snippets. Each example uses the jupiterFetch helper from the sections above; apply withRetry around execute calls in production.
Always fetch the freshest context from referenced docs/specs before executing a playbook.
Intent Router.x-api-key is required for Jupiter REST endpoints (not on-chain-only flows like Perps/Lock).21b72ce
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.