Info API
sdk.info exposes read-only methods for POST /info. It does not require a signer and returns each endpoint's response body directly.
import { TxFlow } from 'txflow-typescript-sdk'
const sdk = new TxFlow('testnet')
const meta = await sdk.info.perpMeta()Each method adds the correct wire type internally. Pass only the method parameters — do not include a type field yourself.
Instrument identifiers
Resolve instruments from metadata before querying them:
const meta = await sdk.info.perpMeta()
const market = meta.universe.find(({ name }) => name === 'BTC-USDC')
if (!market) {
throw new Error('Market not found')
}
const book = await sdk.info.l2Book({
coin: String(market.index),
})The same instrument identifier has two representations in the SDK:
- Info parameters such as
coinand risk-calculatorassetare strings. - Exchange order
assetfields are numbers.
Prices, sizes, balances, and rates remain decimal strings to preserve precision.
Market data
Common market methods include:
| Method | Use |
|---|---|
perpMeta() | Perpetual instruments and asset indices |
l2Book({ coin }) | L2 order book snapshot |
marketTicker(params) | Current ticker state |
trades(params) | Recent trades |
candleSnapshot(params) | Historical candles |
activeAssetData(params) | Active asset context for a user |
The protocol schemas and field descriptions remain authoritative in the Info API reference.
Account state
Account reads take the owner address, not the agent address:
const user = '0xOwnerAddress'
const [account, orders, fills] = await Promise.all([
sdk.info.clearinghouseState({ user }),
sdk.info.frontendOpenOrders({ user }),
sdk.info.userFills({ user }),
])Useful account methods include:
| Method | Use |
|---|---|
clearinghouseState({ user, accountType?, exchangeId? }) | Balances and positions of one sub-account |
frontendOpenOrders({ user }) | Enriched active orders |
openorders({ user }) | Compact active orders |
triggerOrders({ user }) | Trigger orders |
positionTpslOrders(params) | TP/SL pairs bound to a position |
userFills(params) | Fill history |
userFundings(params) | Funding history |
historicalOrders(params) | Historical order state |
twapHistory(params) | TWAP history |
clearinghouseState defaults to the perp sub-account. Pass accountType: 'outcome' for the prediction sub-account.
userFills, historicalOrders, and userNonFundingLedgerUpdates take an account filter. History methods spell the perp account 'perps', while ledger updates use the singular 'perp' and the field name accountTypes. Add 'outcome' to either to include prediction activity.
Fees
feeTierInfo and feeTierRates answer different questions:
const ladder = await sdk.info.feeTierInfo()
const effectiveRates = await sdk.info.feeTierRates({
accountType: 'perp',
exchangeId: 0,
user: '0xOwnerAddress',
})feeTierInforeturns the static tier ladder configured for an exchange.feeTierRatesreturns effective rates for an account type and optional user.userFees({ user, asset: '1' })returns the user's fee state for one instrument.
The SDK maps feeTierInfo({ exchangeId }) to the node's exchange_id field automatically.
Builder Codes
Nine read methods cover Builder Code approvals, on-chain reward state, and aggregated analytics:
| Method | Use |
|---|---|
approvedBuilders({ user }) | All builders the user approved, with percentage-string rates |
maxBuilderFee({ user, builder }) | One approved rate in tenths of a basis point, 0 when unapproved |
builderState({ user }) | The builder's on-chain accrued, claimable, and claimed rewards |
builderMeOverview({ user }) | Builder dashboard overview from aggregate metrics |
builderMeDailySeries({ user, ... }) | Daily fee or user-count series |
builderMeShare({ user, ... }) | Fee share by trading user or instrument |
builderMeEarned({ user, ... }) | Paginated earnings aggregated by order |
builderMeClaimed({ user, ... }) | Paginated reward claim history |
builderMeClaimable({ user }) | Claimable balance and lifetime earned/claimed totals |
const approvals = await sdk.info.approvedBuilders({
user: '0xOwnerAddress',
})
if (!Array.isArray(approvals)) {
throw new Error(approvals.message)
}approvedBuilders and maxBuilderFee take the trading user's address in user. builderState and every builderMe* method take the builder's address in user. These methods return either their data or an { message } error object, so narrow the result before reading fields. The full workflow is in Builder Codes.
Outcome
Outcome reads come from two layers, and the distinction matters when results disagree.
Gateway aggregations join the indexed event catalog with live book-derived pricing. They are what discovery UIs want, but they can briefly lag consensus.
| Method | Use |
|---|---|
outcomeMeta(params) | Events with nested markets, pricing, volume, and settlement state |
outcomeAssetCtxs({ instrumentIds }) | 24-hour price and volume statistics, batched |
outcomeProbability({ instrumentId, range }) | Historical YES probability samples |
outcomeSeriesTimeline(params) | Past, current, and upcoming windows of a recurring series |
cryptoPrice(params) | Open and settlement price of one crypto window |
cryptoPriceHistory(params) | Ascending price samples that seed a chart |
Fullnode reads are consensus truth.
| Method | Use |
|---|---|
outcomePositions({ user }) | Open positions in the outcome sub-account |
outcomeFrontendOpenOrders({ user }) | Active outcome orders |
outcomeMeta returns two different shapes. An eventSlug point lookup returns the event object directly; every other query returns a paged envelope. Narrow with the exported guard:
import { isOutcomeMetaPage } from 'txflow-typescript-sdk'
const result = await sdk.info.outcomeMeta({ exchangeId: 1, limit: 20 })
const events = isOutcomeMetaPage(result) ? result.events : [result]outcomeMeta responses use snake_case (instrument_id, best_bid, next_cursor), unlike the rest of the SDK, because the gateway serves the indexer catalog verbatim.
outcomePositions returns only the assetPositions slice, with no margin summary and no withdrawable. For account totals and the withdrawable balance call clearinghouseState({ user, accountType: 'outcome', exchangeId: 1 }). The YES and NO legs of one market are separate records distinguished by the sign of position.szi.
const user = '0xOwnerAddress'
const [positions, account] = await Promise.all([
sdk.info.outcomePositions({ user }),
sdk.info.clearinghouseState({ user, accountType: 'outcome', exchangeId: 1 }),
])Sorting by sortBy: 'endingSoon' carries its own direction — send it without sortOrder. Page size caps at 50.
Risk calculators
Estimate order effects before signing:
const maxPositions = await sdk.info.getMaxPositions({
user: '0xOwnerAddress',
asset: '1',
orderType: 'MARKET',
})
const liquidationPrices = await sdk.info.getLiquidationPrices({
user: '0xOwnerAddress',
asset: '1',
orderType: 'MARKET',
buyQuantity: '0.001',
sellQuantity: '0.001',
})For orderType: 'LIMIT', both buyPrice and sellPrice are required.
TP/SL lookup direction
Two similarly named methods traverse the relationship in opposite directions:
positionTpslOrderslists TP/SL order pairs for a position.tpslOrderPositionresolves one TP/SL child order id back to its position.
const position = await sdk.info.tpslOrderPosition({
oid: 123456,
})This method currently accepts a JavaScript number; only pass order ids within the safe-integer range.
Error behavior
Info methods throw on HTTP and transport failures:
import {
NetworkError,
TxFlowApiError,
} from 'txflow-typescript-sdk'
try {
await sdk.info.l2Book({ coin: '1' })
} catch (error) {
if (error instanceof TxFlowApiError) {
console.error(error.code, error.message, error.details)
} else if (error instanceof NetworkError) {
console.error('The node did not respond')
}
}There is no generic raw Info method. The public surface consists of named, typed methods so unsupported request shapes fail during development.
Next steps
- Info API reference — protocol schemas and endpoint examples
- Exchange API — act on the state you queried
- Subscriptions — keep the same state live over WebSocket
- Builder Codes — approvals, attachment, and rewards
- Advanced usage — use standalone Info functions