Integration Quickstart
Add leverage to your platform in 4 API calls.
The Lavarage API lets you embed leveraged trading into any Solana application. You call the API to build an unsigned Solana transaction, the user signs it with their wallet, and you submit it to the network. That's it — no on-chain program knowledge required.
What you get:
- Unsigned VersionedTransactions ready to sign and submit
- Automatic offer matching — no need to find liquidity pools manually
- Support for any SPL token that has an active offer
- Both LONG and SHORT positions are fully supported
Base URL: https://api.lavarage.xyz
Prerequisites
- An API key — apply at https://lavarage.xyz/partners or submit
POST /api/v1/partners/apply - A Solana wallet adapter (or any signing implementation) on the client side
- A Solana RPC endpoint for transaction submission (Helius, Triton, or your own node)
Authentication
Trading and partner management endpoints require an API key in the x-api-key header:
curl https://api.lavarage.xyz/api/v1/positions \
-H "x-api-key: YOUR_API_KEY"To get an API key, apply at https://lavarage.xyz/partners or submit POST /api/v1/partners/apply. Once approved, generate keys via the Partners API:
curl -X POST https://api.lavarage.xyz/api/v1/partners/keys \
-H "x-api-key: YOUR_API_KEY"The raw key is only returned once — store it securely.
Key management:
- List keys:
GET /api/v1/partners/keys - Revoke a key:
DELETE /api/v1/partners/keys/:id(permanent and immediate)
Public Endpoints (No Key Required)
| Endpoint | Description |
|---|---|
GET /health | Service health check |
GET /api/v1/offers | Browse liquidity pools |
GET /api/v1/offers/utilization | Pool utilization metrics |
GET /api/v1/offers/:publicKey | Single offer details |
GET /api/v1/tokens | Token list with prices |
GET /api/v1/tokens/:address | Single token details |
POST /api/v1/offers/match | Match best offer for token pair |
GET /api/v1/offers/top | Top tokens by activity |
GET /api/v1/offers/latest | Latest tokens |
GET /api/v1/referral/* | Referral read endpoints |
Wallet Signature Authentication
Some endpoints (orders, referral writes) require wallet signature authentication via 3 headers:
| Header | Value |
|---|---|
x-wallet-address | Your Solana wallet address (base58) |
x-wallet-signature | Ed25519 signature of the message (base58) |
x-wallet-message | The signed message: lavarage:<wallet>:<timestamp_ms> |
import nacl from 'tweetnacl';
import bs58 from 'bs58';
const wallet = keypair; // Your Solana Keypair
const timestamp = Date.now().toString();
const message = `lavarage:${wallet.publicKey.toBase58()}:${timestamp}`;
const messageBytes = new TextEncoder().encode(message);
const signature = nacl.sign.detached(messageBytes, wallet.secretKey);
const headers = {
'x-wallet-address': wallet.publicKey.toBase58(),
'x-wallet-signature': bs58.encode(signature),
'x-wallet-message': message,
};Security Best Practices
- Never expose API keys in client-side code
- Use environment variables for key storage
- Rotate keys periodically using the Partners API
- Revoke compromised keys immediately
Rate Limits
| Endpoint Group | Limit |
|---|---|
| Transaction endpoints (open, close, split, merge, increase-borrow, add-collateral) | 600 requests / 60s |
| Read endpoints (positions, offers, tokens) | 1800 requests / 60s |
| Public endpoints (no auth) | 30 requests / 60s per IP |
Exceeding the limit returns HTTP 429 with code RATE_LIMIT_EXCEEDED. Implement exponential backoff starting at 1s.
Your First Trade
Step 1: Find a Market
Call POST /api/v1/positions/quote-by-token to get a price preview and confirm a market exists for your token. This does not build a transaction — it just returns quote data.
curl -X POST https://api.lavarage.xyz/api/v1/positions/quote-by-token \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"baseTokenMint": "So11111111111111111111111111111111111111112",
"userPublicKey": "GsbwXfJraMomNxBcjK7xK2xQx5MQgQx4Ld8QkLeNmA3v",
"collateralAmount": "1000000000",
"leverage": 3,
"side": "LONG"
}'Response:
{
"inAmount": "1000000000",
"outAmount": "2950000000",
"priceImpactPct": "0.12",
"otherAmountThreshold": "2920500000",
"inputMint": "So11111111111111111111111111111111111111112",
"outputMint": "So11111111111111111111111111111111111111112",
"slippageBps": 50
}Step 2: Show the Cost
Before building the transaction, fetch the matching pool to display the annualized rate the user will pay. Each pool sets its own rate — there is no single "Lavarage rate." Without this step, users open positions without seeing what they will pay to borrow.
curl -X POST https://api.lavarage.xyz/api/v1/offers/match \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"baseTokenAddress": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
"quoteTokenAddress": "So11111111111111111111111111111111111111112",
"leverage": 3
}'Response:
{
"offer": {
"publicKey": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"apr": "30.00",
"maxLeverage": "5.00",
"availableForOpen": "50000000000",
"baseTokenAddress": "...",
"quoteTokenAddress": "..."
},
"alternatives": [
{ "publicKey": "...", "apr": "32.50", "maxLeverage": "4.00", "availableForOpen": "..." }
]
}offer is the cheapest pool with enough capacity at the requested leverage. alternatives are up to 2 fallback pools.
The apr field is a string (numeric, 2 decimal places) representing the annual percent rate — "30.00" means 30% APR. Display it alongside leverage and slippage in your trade preview:
const aprPercent = parseFloat(offer.apr) // "30.00" → 30
const borrowedAmount = collateralAmount * (leverage - 1)
const dailyInterest = (borrowedAmount * aprPercent) / 100 / 365
console.log(`Borrow rate: ${aprPercent}% APR`)
console.log(`Daily cost: ${dailyInterest.toFixed(4)} ${quoteSymbol}`)The rate is locked at open-time. Pool rate changes affect new positions only, not existing ones.
Tip: To show users a pool picker instead of auto-matching, call
GET /api/v1/offers?orderBy=interest&limit=20and let them choose.
Step 3: Build the Transaction
Call POST /api/v1/positions/open-by-token to build the unsigned transaction. The server resolves the best liquidity pool automatically.
curl -X POST https://api.lavarage.xyz/api/v1/positions/open-by-token \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"baseTokenMint": "So11111111111111111111111111111111111111112",
"userPublicKey": "GsbwXfJraMomNxBcjK7xK2xQx5MQgQx4Ld8QkLeNmA3v",
"collateralAmount": "1000000000",
"leverage": 3,
"side": "LONG",
"slippageBps": 50
}'Response:
{
"transaction": "AQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAkP...",
"positionAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"lastValidBlockHeight": 285432100,
"quote": { "outAmount": "9950000000", "priceImpactPct": 0.12, "slippageBps": 50 }
}The transaction field is a base58-encoded VersionedTransaction.
Step 4: User Signs and Submits
Deserialise the transaction, have the user sign it, and submit to the network.
import { VersionedTransaction, Connection } from '@solana/web3.js'
import bs58 from 'bs58'
// Deserialise
const txBytes = bs58.decode(response.transaction)
const tx = VersionedTransaction.deserialize(txBytes)
// Sign with the user's wallet (Phantom, Solflare, etc.)
const signedTx = await wallet.signTransaction(tx)
// Submit
const connection = new Connection('https://api.mainnet-beta.solana.com')
const signature = await connection.sendRawTransaction(signedTx.serialize())
await connection.confirmTransaction(signature, 'confirmed')
console.log('Position opened:', signature)Step 5: Monitor and Close
Poll GET /api/v1/positions?owner=<wallet>&status=OPEN to monitor open positions, then build a close transaction when ready.
# Get a close quote first
curl -X POST https://api.lavarage.xyz/api/v1/positions/close-quote \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"positionAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"userPublicKey": "GsbwXfJraMomNxBcjK7xK2xQx5MQgQx4Ld8QkLeNmA3v"
}'
# Then build the close transaction
curl -X POST https://api.lavarage.xyz/api/v1/positions/close \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"positionAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
"userPublicKey": "GsbwXfJraMomNxBcjK7xK2xQx5MQgQx4Ld8QkLeNmA3v",
"slippageBps": 50
}'Sign and submit the returned transaction field the same way as Step 4.
Response Format
Transaction-building endpoints return a transaction field (base58-encoded unsigned VersionedTransaction) plus endpoint-specific fields:
| Endpoint | Response shape |
|---|---|
POST /positions/open | { transaction, positionAddress, lastValidBlockHeight, quote } |
POST /positions/open-by-token | { transaction, positionAddress, lastValidBlockHeight, quote } |
POST /positions/close | { transaction, lastValidBlockHeight, quote } |
POST /positions/split | { transaction, lastValidBlockHeight, newPositionAddresses } |
POST /positions/partial-sell | { splitTransaction, closeTransaction, newPositionAddresses, swapQuote } |
POST /positions/repay | { transaction, lastValidBlockHeight } |
POST /positions/partial-repay | { transaction, lastValidBlockHeight } |
The transaction is unsigned — you are responsible for having the user sign it and submitting it to the Solana network. Lavarage never holds private keys for your users.
Versioning
The current API version is v1. All endpoints are prefixed with /api/v1/. Breaking changes will be released under a new version prefix (/api/v2/) with a deprecation period.
What's Next
- Trading API — Full endpoint reference for quotes, opening, closing, and monitoring positions
- Advanced Operations — Split, partial sell, repay, TP/SL orders
- Revenue & Fees — How partner fee sharing works
- Real-Time Data — SSE streams and webhook notifications
- Error Reference — Complete error code table with retry guidance
Updated 3 days ago