Boros API Best Practices
This guide provides recommendations and common patterns for integrating with the Boros API efficiently.
Bulk Order Placement
When placing multiple orders simultaneously, always use the bulk order endpoint instead of making multiple single order requests.
Why Use Bulk Orders?
| Approach | Gas Cost | Atomicity | Network Overhead |
|---|---|---|---|
| Multiple single orders | High (N transactions) | None (partial failures possible) | High |
| Bulk orders | Low (1 transaction) | Atomic (all or nothing) | Low |
Benefits
- Lower Gas Fees — Multiple operations batched into a single transaction
- Atomic Execution — All operations execute together, eliminating partial execution risks
- Better Performance — Reduces network overhead and speeds up execution
- Complex Strategies — Combine cancellations and new orders in one operation
Unlike single order placement (placeSingleOrder), bulk orders do NOT match with the AMM. They are placed directly into the order book. If you need to take liquidity from both the order book and the AMM, use single order placement with IOC or FOK time-in-force.
Examples
Transaction Submission
direct-call vs bulk-direct-call
The Send Txs Bot provides two submission endpoints:
| Endpoint | Use case |
|---|---|
POST /v2/agent/direct-call | Submit a single calldata |
POST /v3/agent/bulk-direct-call | Submit one or more calldatas with sequential execution |
When to use bulk-direct-call: The calldata generation endpoints (e.g., POST /calldata/place-orders) may return multiple calldatas in the calldatas array — for example, [exit-market, place-order] when autoExitMarket=true. You must submit all calldatas together using bulk-direct-call to ensure correct execution order.
If you use direct-call with only the first element of a multi-calldata response, only the exit-market transaction executes — your order will not be placed. Always check the length of the calldatas array and use bulk-direct-call when there are multiple entries.
autoExitMarket Parameter
When calling POST /calldata/place-orders, the autoExitMarket parameter (default: true) controls whether the API automatically generates an exit-market calldata for expired/unused markets:
autoExitMarket=true→ API may return 1 or 2 calldatas:[exit-market (optional), place-order]. The last element is always the place-order calldata.autoExitMarket=false→ API always returns exactly 1 calldata (place-order only). Use this if you manage market entry/exit yourself.
skipReceipt Parameter
Controls whether the Send Txs Bot waits for block confirmation:
skipReceipt=false(default): Waits for the transaction to be included in a block, then returnsstatusanderrorfields. Higher latency but gives immediate confirmation.skipReceipt=true: Returns thetxHashimmediately after broadcasting. Lower latency but you must track the transaction status yourself.
Market Management
Exit Unused Markets
If you have entered markets that are no longer in use, exit them to reduce gas costs on future operations.
When you enter a market, the system tracks your participation for margin calculations. Each entered market adds overhead to the margin tracker, making it more expensive in gas for every subsequent transaction.
Important limitations:
- Maximum 10 entered markets per
marketAcc(market account). This limit is per combination of (address, accountId, tokenId) — i.e., per collateral type. - Maximum active limit orders per
marketAccper market (configurable per market viamaxOpenOrders, typically 100) - Entering more markets = heavier margin calculations = higher gas costs
If you trade many markets over time (e.g., weekly expiry markets), you will hit the 10-market limit. You must exit expired/matured markets to free up slots for new ones. The autoExitMarket parameter in the place-orders API can handle this automatically.
Exiting unused markets helps:
- Reduce gas consumption on all subsequent transactions
- Free up slots for new markets (10 market limit)
- Improve transaction performance
When to Exit Markets
- After closing all positions in a market
- When you no longer plan to trade a specific market
- During account cleanup/maintenance
- Before market expiry (for dated markets)
To exit a market, you must first:
- Close all positions (zero position size)
- Cancel all open orders
- Have no pending settlements
Isolated-Only Markets
Some markets are isolated-only, meaning they cannot be traded from a cross-margin account. For these markets:
- Deposit to the isolated account — You must transfer collateral specifically to the isolated market account, not the cross-margin account
- Use the correct
marketAcc— Pack themarketAccwith the specificmarketIdinstead ofCROSS_MARKET_ID
import { MarketAccLib } from "@pendle/sdk-boros";
// For isolated-only markets, use the specific marketId
const isolatedMarketAcc = MarketAccLib.pack(
walletAddress,
accountId,
tokenId,
marketId // Use the specific market ID, NOT CROSS_MARKET_ID
);
To fund an isolated account, either:
- Direct deposit: Deposit directly to the isolated market using the
marketIdin the deposit calldata - Cash transfer: Transfer from cross-margin to isolated using the
/calldata/cash-transferendpoint withisDeposit: true
Example: Top Up Isolated Account
Gas Usage and Estimation
Monitor Gas Balance
Boros uses a gas account system for executing agent-signed transactions. Always monitor your gas balance to avoid failed transactions.
Key endpoints:
GET /accounts/gas-balance— Check current balanceGET /accounts/gas-consumption-history— Review usage patternsGET /calldata/vault-pay-treasury— Top up gas balance (sensitive, root-signed)
Arbitrum Gas Spikes
Arbitrum gas prices can spike significantly during high network activity. Consider:
- Monitor gas prices before submitting transactions
- Maintain buffer in your gas account for unexpected spikes
- Check gas consumption history to understand your usage patterns
Example: Top Up Gas Account
Error Handling
Error Response Formats
The Open API uses a structured error format:
{
"errorCode": "INVALID_MARKET_ID",
"message": "Market with ID 999 not found",
"data": {}
}
The Send Txs Bot and Stop Order services use a legacy format:
{
"statusCode": 400,
"message": "Invalid signature"
}
Handling Common Errors
| Error | Cause | Solution |
|---|---|---|
| Insufficient margin | Not enough collateral for the position | Deposit more, reduce size, or close other positions |
| Agent expired | Agent approval has expired | Re-approve agent via GET /calldata/approve-agent |
| Invalid signature | Signature doesn't match the expected signer | Verify you're signing with the correct agent key |
| Market not entered | Trying to trade on a market you haven't entered | Use GET /calldata/enter-exit-markets to enter first |
| Order limit reached | Exceeded maxOpenOrders for the market | Cancel some existing orders before placing new ones |
| Market limit reached | Over 10 entered markets | Exit unused markets to free up slots |
Retry Strategy
- Don't retry immediately on validation errors (bad parameters, insufficient margin) — fix the root cause
- Retry with backoff on transient errors (network timeouts, 503s)
- Check transaction status before retrying submissions to the Send Txs Bot — the original may have succeeded
Simulation Before Execution
Always use the simulation endpoints before executing trades, especially for:
- Large orders — preview margin impact and fees
- Close positions — verify the counter-order parameters
- Deposits/withdrawals — check that the operation won't cause under-margining
Simulations are free (no gas) and return the projected account state. The key simulation endpoints:
| Endpoint | Use case |
|---|---|
GET /simulations/place-order | Preview order placement |
GET /simulations/close-active-position | Preview closing a position |
GET /simulations/deposit | Preview deposit effect |
GET /simulations/withdraw | Preview withdrawal effect |
GET /simulations/cash-transfer | Preview cross ↔ isolated transfer |
Real-Time Data
Market Data Latency
Not all REST endpoints have the same latency characteristics. For latency-sensitive indicators, use the correct endpoint:
| Data | Best endpoint | Latency |
|---|---|---|
midApr, bestBid, bestAsk, ammImpliedApr, markApr | GET /open-api/v1/markets/{marketId} | Real-time |
| Order book depth | GET /core/orderbooks/{marketId} | < 1 second |
| AMM state | GET /core/amm/{ammId} | < 1 second |
| Market list / overview | GET /open-api/v1/markets | May have several seconds of delay |
For market making or any use case requiring live mid/mark rates, always use the single market endpoint (GET /v1/markets/{marketId}) rather than the bulk markets list. The bulk endpoint may have a delay of several seconds on sensitive indicators.
Combined Order Book (AMM + Order Book)
The order book displayed in the Boros UI and returned by GET /v2/markets/order-books (with AMM enabled) is a combined order book that merges:
- Order book liquidity: Discrete maker orders at specific tick levels
- AMM liquidity: Continuous liquidity from the AMM, discretized into tick levels for display
Important: The AMM has a minimum and maximum APR range. When the market rate moves outside this range, the AMM stops providing liquidity. In this case, the combined order book may show a wider spread since only order book liquidity remains.
Note: Some markets may have no AMM at all. In these markets, all liquidity comes from the order book only, and placeSingleOrder behaves the same as bulkOrders (no AMM to route through). Check the market's ammId — if absent or null, there is no AMM.
The AMM implied rate should normally be within the range (bestBid - ε, bestAsk + ε) where ε is a small amount to prevent arbitrage between the AMM and the order book.
Use WebSocket for Streaming
Don't poll REST endpoints for real-time data. Use WebSocket for:
- Order book updates:
orderbook:MARKET_ID:TICK_SIZE - Trade events:
market-trade:MARKET_ID - Market statistics:
statistics:MARKET_ID - Account changes:
account:ADDRESS:ACCOUNT_ID
Caching Strategy
| Data | Cache duration | Refresh trigger |
|---|---|---|
| Assets list | Long (hours) | Rarely changes |
| Market list | Medium (minutes) | New markets added infrequently |
| Market state / order book | Don't cache | Use WebSocket |
| Account positions | Don't cache | Use WebSocket account channel |
| Gas balance | Short (seconds) | After each transaction |
Security
Key Management
- Never expose your root private key in client-side code or logs
- Use the agent system — the agent key has limited permissions and cannot withdraw funds
- Rotate agent keys periodically by revoking the old agent and approving a new one
- Set reasonable expiry times on agent approvals
Transaction Verification
- Always simulate before executing to verify expected outcomes
- Check the
toaddress in calldata responses matches the expected Router contract - Verify transaction status after submission — don't assume success
Summary
| Best Practice | Why |
|---|---|
| Use bulk orders for multiple operations | Lower gas, atomic execution |
Use bulk-direct-call for multi-calldata responses | Ensures all calldatas (exit-market + place-order) are submitted |
Set autoExitMarket=false if managing markets yourself | Avoids unexpected multi-calldata responses |
| Exit unused/expired markets (max 10 per account) | Reduce gas overhead, free up slots |
Use GET /v1/markets/{marketId} for real-time rates | Guaranteed real-time midApr, markApr, bestBid, bestAsk |
Use correct marketAcc for isolated-only markets | Required for trading isolated-only markets |
| Monitor gas balance | Avoid failed transactions |
| Simulate before executing | Preview margin impact and catch errors |
| Use WebSocket for real-time data | Lower latency, less overhead than polling |
| Handle errors by format (Open API vs legacy) | Different services use different formats |
| Never expose root key; use agents | Security best practice |
For complete working examples, visit: https://github.com/pendle-finance/boros-api-examples