GraphQL
GMX provides GraphQL endpoints powered by Subsquid for querying indexed on-chain data on Arbitrum, Avalanche, and MegaETH.
| Network | Status | URL |
|---|---|---|
| Arbitrum One | ✅ Active | https://gmx.squids.live/gmx-synthetics-arbitrum:prod/api/graphql |
| Avalanche C-Chain | ✅ Active | https://gmx.squids.live/gmx-synthetics-avalanche:prod/api/graphql |
| MegaETH | ✅ Active | https://gmx.squids.live/gmx-synthetics-megaeth:prod/api/graphql |
The Botanix endpoint is no longer available because GMX no longer supports Botanix.
Schema changes
This section lists changes to the indexed GraphQL schema, newest first.
2026-10-23 — Funding received and staking power fields added
The indexed schema now records the funding each position execution credits to claimable funding, tracks pending unstake requests in staking power, and exposes the accounts whose unstaked GMX still counts as staked.
| Surface | Change |
|---|---|
TradeAction.fundingFeeReceivedUsd, PositionFeesEntity.fundingFeeReceivedUsd | New nullable fields. They hold the funding that a position execution credited to claimable funding, in USD with 30 decimals, valued at the oracle min price of each token. 0 means the execution credited none. null means the value is unavailable, for example on TradeAction rows that aren't position executions or when a price was missing. |
StakingPower.pendingUnstakeAmount | New non-null field. It holds the amount in RewardRouterV3 unstake requests that haven't been executed or cancelled. That amount doesn't accrue staking power. |
StakingPower.historicalMaxStaked, NetworkStakingPower.totalCurrentStaked | Meaning changed. Both now hold staked minus pending: historicalMaxStaked is the account's highest staked balance net of pendingUnstakeAmount since its last power reset, and totalCurrentStaked is that net balance summed across accounts. |
StakingPowerExemption | New entity available through stakingPowerExemptions, stakingPowerExemptionById, and stakingPowerExemptionsConnection. Its id is the account address. It applies to a fixed list of Arbitrum accounts whose unstaked GMX still counts as staked, up to the GMX they had staked at a snapshot block. heldGmx is the unstaked GMX held by the account's listed holders, exemptedGmx is the part still counted as staked, and remainingAllowance is the snapshot amount not yet exempted. |
StakingPower.currentStakedBalance still holds the full balance, including amounts in pending unstake requests and, for an exempt account, exemptedGmx. Subtract pendingUnstakeAmount from it before you compare it with historicalMaxStaked or add it up to compare with totalCurrentStaked.
Treat a null fundingFeeReceivedUsd as unavailable, not as zero.
2026-10-08 — Position lifecycle IDs added
The indexed schema now links trade history and orders to a position lifecycle: one position from the increase that opens it to the decrease that fully closes it. A position key, derived from the account, market, collateral token, and direction, stays the same when an account closes a position and later reopens it, so the lifecycle ID is what separates those cycles. TradeAction.positionLifecycleId and the PositionLifecycle entity were first added on June 30, 2026. This update adds Order.positionLifecycleId, links pending increase requests to the position they add to, and reindexes history on Arbitrum, Avalanche, and MegaETH, so older rows carry the ID too.
| Surface | Change |
|---|---|
PositionLifecycle | New entity available through positionLifecycles, positionLifecycleById, and positionLifecyclesConnection. Its id is the position key. isOpen says whether the position is open, currentLifecycleId holds the open lifecycle, lastClosedLifecycleId holds the most recently closed one, or null if the indexer has recorded no close, and updatedAtBlock is the block of the last change. Rows are never deleted, so a fully closed position keeps its row. |
TradeAction.positionLifecycleId | New indexed field in the format positionKey:openingOrderKey, where openingOrderKey is the key of the increase order that opened the position. Filter on it with positionLifecycleId_eq. |
Order.positionLifecycleId | New field, set on increase orders only. Decrease and swap orders leave it null. The indexer captures it when the order is created and finalizes it when the order executes. |
A lifecycle starts when an increase executes on a position with zero size and ends when a decrease brings the size back to zero. At the close, the indexer moves the ID from currentLifecycleId to lastClosedLifecycleId and sets isOpen to false. Each TradeAction row gets the ID that applies when its event happens, so rows of the same order can carry different IDs:
- Executed decreases, liquidations, and decrease-order requests carry the open lifecycle, or the lifecycle that just closed after a full close. A Take-Profit or Stop-Loss order cancelled after the close stays in the closed cycle. A decrease order created before its position opens, such as a TP/SL submitted with the opening order, can have its
OrderCreatedrow stamped with the position's previous lifecycle, ornullfor a first open, while its later rows carry the new one. - An increase request created on an open position carries that position's lifecycle. One created while the position is flat gets a provisional ID built from its own order key. When the order executes, the indexer rewrites its request rows to the lifecycle the order joined, so an add that executes after another order opened the position moves to the opener's lifecycle. An increase order created while flat and cancelled before it executes keeps its provisional ID, which no executed row shares.
- The ID can be
nullwhen the indexer can't resolve the position at order creation or when the position was opened before the indexed range.
To read one position's history, look up its lifecycle by position key, then filter trade actions by the lifecycle ID. When isOpen is false, currentLifecycleId is null and lastClosedLifecycleId points to the most recent closed cycle, or is null when the indexer has recorded no closed cycle for that position key, for example when its orders never opened a position or when the position opened before the indexed range. If both IDs are null, the indexer has no lifecycle ID to pass to the second query.
# Find the lifecycle of one position by its position key
query PositionLifecycle($positionKey: String!) {
positionLifecycleById(id: $positionKey) {
isOpen
currentLifecycleId
lastClosedLifecycleId
}
}
# Trade history for one lifecycle, from open to close
query LifecycleTradeActions($lifecycleId: String!) {
tradeActions(where: { positionLifecycleId_eq: $lifecycleId }, orderBy: timestamp_ASC, limit: 100) {
eventName
orderType
orderKey
sizeDeltaUsd
pnlUsd
timestamp
transactionHash
}
}
2026-09-01 — GLV market allocation snapshots added
The indexed schema now records how each GLV vault's value is split across its GM markets, once per hour. Query the new GlvMarketAllocationSnapshot entity through glvMarketAllocationSnapshots, glvMarketAllocationSnapshotById, and glvMarketAllocationSnapshotsConnection. Each row covers one GLV vault and one of its markets for one hour, with the id glvAddress:marketAddress:snapshotTimestamp.
| Field | What it holds |
|---|---|
snapshotTimestamp | Start of the hour, in Unix seconds |
glvAddress, marketAddress | The GLV vault and the GM market |
blockNumber, blockTimestamp | Block the values were read at |
marketTokenBalanceAmount | GM tokens of the market held by the GLV vault (18 decimals) |
marketTokenBalanceUsd | That balance valued at the GM token's max price, as GMX values a GLV vault (30 decimals) |
glvValue | Sum of marketTokenBalanceUsd across the markets valued in that hour (30 decimals) |
marketTokenBalancePerGlvValue | The market's share of glvValue: marketTokenBalanceUsd / glvValue, scaled by 1e30 |
marketTokenMaxPrice, marketTokenMinPrice | GM token prices used for the snapshot |
marketTokenSupply, marketPoolValue | Total GM token supply of the market (18 decimals) and that supply valued at the max price, that is, the whole market in USD (30 decimals) |
When an hour has no indexed events, the indexer repeats the previous hour's rows under the new snapshotTimestamp but keeps their original blockNumber and blockTimestamp. Compare blockTimestamp with snapshotTimestamp to tell a carried-over row from a fresh read. A market without a price or market info at snapshot time is skipped, so glvValue covers only the markets valued in that hour.
2026-08-18 — Funding history and trade-action coverage expanded
The indexed schema now exposes additional hourly funding inputs and identifies funding-fee settlement executions in trade history.
| Surface | Change |
|---|---|
FundingBalanceOiSnapshot | New hourly entity available through fundingBalanceOiSnapshots and fundingBalanceOiSnapshotById. It shares its id with the corresponding FundingRateSnapshot and exposes raw USD and token open interest, collateral-token splits, index-token price inputs, the open interest used for funding balance, and signed and absolute net exposure. |
Pure-swap TradeAction fields | swapImpactUsd and swapFeeUsd now aggregate the complete swap path. executionAmountOut is populated from the final swap event. If a required swap event is missing, the affected fields remain null; a genuine zero remains 0. |
FundingRateSnapshot and marketsNetRatesByPeriod | Pre-v2.2c history now falls back to the legacy single min/max funding-factor keys when both per-side keys are unset. Legacy configuration events apply the value to both sides. |
TradeAction.isFundingFeeSettle | New indexed boolean. Funding-fee settlement executions now create TradeAction rows with this field set to true; older rows can be null. |
2026-07-21 — Referral flow and positive funding PnL clarified
Affiliate analytics now distinguish traders who leave an affiliate because they graduated to a GMX direct-discount code from traders who were otherwise lost.
| Surface | Change |
|---|---|
AffiliateTraderStatsByHour | Added tradersGraduated |
affiliateStats points | Added tradersGraduated |
affiliateStats summary | Added tradersGraduated and tradersGraduatedDelta |
tradersNet is calculated as tradersGained - tradersGraduated - tradersLost. The AccountStat.positiveFundingFeesUsd field is the signed PnL contribution from funding received through ClaimableFundingUpdated, whether claimed or not. It is excluded from realizedFees, so add it to PnL once rather than treating it as another realized fee.
2026-07-17 — TWAP grouping metadata migrated
The indexer now reads TWAP grouping metadata from the first valid gmxo entry in Order.dataList. It falls back to decoding the legacy synthetic uiFeeReceiver format for older orders. The public numberOfParts and twapGroupId fields remain the integration surface; you don't need to decode either metadata format yourself.
2026-07-15 — Order UI fee snapshots added
Order and TradeAction now expose uiFeeFactor, the factor snapshotted when the order was created. The field is nullable because historical rows created before the snapshot was added don't contain it. Treat null as unavailable historical data, not as a zero factor.
2026-07-14 — GLV user earnings added
The schema now tracks GLV fee earnings by account and exposes a public glvUserEarnings(account: String!) resolver.
| Entity or resolver | Added surface |
|---|---|
Glv | glvTokenSupply, cumulativeFeeUsdPerGlvToken |
CollectedFeesInfo | cumulativeFeeUsdPerGlvToken |
GlvTokenBalance | Current balance, realized cumulative income, and entry snapshot for one account and GLV |
GlvTokenBalanceChange | Ordered balance-change history used to reconstruct the 7-day window |
glvUserEarnings | glvAddress, lifetimeFeeUsd, and recent7dFeeUsd for each GLV held by an account |
The resolver calculates lifetime earnings from realized income plus the account's unrealized share of the current fee accumulator. Its 7-day value uses hourly snapshots at the window edge and clamps a negative result to zero.
2026-06-23 — Referral code update support
The GraphQL schema now includes the referral entities needed to evaluate GMX-managed protocol referral code updates. These additions support the backend workflow that moves eligible traders to organic volume-discount codes or graduated direct-discount codes.
Expanded referral fields.
| Entity | Added fields |
|---|---|
TraderReferralTradeStatsByHour | affiliateRewardsUsd |
Additional referral entities.
| Entity | What it provides |
|---|---|
TraderFirstReferral | First-assignment timestamp for a trader and referral code pair |
ReferralTier | Tier configuration with totalRebate and discountShare |
ReferrerTier | Current tier id for a referrer address |
The private referralCodeUpdates resolver is used by GMX-maintained services and is not a public integration surface. Public referral dashboards should continue to use affiliateStats, traderReferralStats, and the referral entities described below.
2026-05-28 — Trade PnL, market info, and funding snapshots expanded
The GraphQL schema and indexer-derived analytics now expose a more detailed split between realized PnL, swap fees, discounts, market funding state, and hourly rate snapshots.
Expanded fields.
| Entity | Added fields |
|---|---|
AccountStat | realizedSwapFees, realizedSwapImpact |
PositionChange | swapFeeUsd |
TradeAction | swapFeeUsd, traderDiscountAmount |
PositionFeesEntity | traderDiscountAmount |
MarketInfo | minCollateralFactorForLiquidation, savedFundingFactorPerSecond, fundingUpdatedAt, useOpenInterestInTokensForBalance |
The account PnL summary server extension also includes a fuller debug breakdown for fees, price impact, discounts, and swap impact. Use these fields when you need to distinguish base realized PnL from fee and impact components instead of treating trade-history PnL as one all-in number.
Hourly rate snapshots now replay the funding formula from indexed market state, including MarketInfo, savedFundingFactorPerSecond, fundingUpdatedAt, open interest, and relevant DataStore settings. They are still historical indexer snapshots, not realtime market state.
2026-03-31 — Referral analytics added
The GraphQL schema now exposes referral analytics for both affiliates and traders. Five new entities track referral code ownership and hourly trade statistics, and two new query resolvers aggregate that data into time-windowed summaries with period-over-period comparisons.
New entities.
| Entity | What it provides |
|---|---|
ReferralCodeOwner | Maps a referral code to its owner address, with updatedAtTimestamp, updatedAtBlock, and updatedTxnHash |
TraderReferral | Records which referralCode and affiliate a trader is associated with, plus update metadata |
AffiliateReferralTradeStatsByHour | Hourly trade stats for an affiliate: volumeUsd, tradesCount, and rebatesUsd |
TraderReferralTradeStatsByHour | Hourly trade stats for a trader using a referral code: volumeUsd, discountsUsd, and affiliateRewardsUsd |
AffiliateTraderStatsByHour | Hourly net trader flow for an affiliate: tradersGained, tradersLost, and tradersNet |
New query resolvers.
These are custom server-extension resolvers, not standard entity queries. Call them by name with a where argument:
| Resolver | Input fields | What it returns |
|---|---|---|
affiliateStats | affiliate (required), from?, to? | Time-windowed volume, trade count, rebates, and trader flow for an affiliate, with optional period comparison |
traderReferralStats | trader (required), from?, to? | Time-windowed volume and discounts for a trader using a referral code, with optional period comparison |
Both resolvers align timestamps to hourly buckets and choose a bucket size automatically based on the requested window length.
Example queries.
# Affiliate dashboard: volume, rebates, and trader flow for a 7-day window
query AffiliateStats($affiliate: String!) {
affiliateStats(where: { affiliate: $affiliate, from: 1743292800, to: 1743897600 }) {
affiliate
from
to
bucketSizeSeconds
hasComparison
summary {
volumeUsd
volumeUsdDelta
rebatesUsd
rebatesUsdDelta
tradersNet
tradersNetDelta
}
points {
timestamp
volumeUsd
rebatesUsd
tradersGained
tradersLost
tradersNet
}
}
}
# Trader dashboard: volume and discounts earned through a referral code
query TraderReferralStats($trader: String!) {
traderReferralStats(where: { trader: $trader, from: 1743292800, to: 1743897600 }) {
trader
from
to
bucketSizeSeconds
summary {
volumeUsd
discountsUsd
}
points {
timestamp
volumeUsd
discountsUsd
}
}
}
2026-03-10 - Staking power and account analytics added
The GraphQL schema now exposes staking power analytics plus expanded daily account aggregates for PnL and capital-tracking queries.
New entities.
| Entity | What it provides |
|---|---|
StakingPower | Per-account staking power state, including accumulatedPower, currentStakedBalance, historicalMaxStaked, lastPowerResetAt, and powerResetCount |
NetworkStakingPower | Network-wide staking power totals through totalAccumulatedPower, totalCurrentStaked, and lastUpdateTimestamp |
Expanded analytics fields.
| Entity | Added fields |
|---|---|
AccountStat | account, period, dayTimestamp, netCapitalDelta, maxNetCapitalRunningDelta |
Position | maxCapital |
These additions let you query staking-power history and daily account-level capital changes without replaying raw position-change events yourself.
Example query.
query AccountAnalytics($account: String!) {
stakingPower(id: $account) {
accumulatedPower
currentStakedBalance
historicalMaxStaked
lastPowerResetAt
powerResetCount
}
accountStats(
where: { account_eq: $account, period_eq: "1d" }
orderBy: dayTimestamp_DESC
limit: 7
) {
dayTimestamp
netCapitalDelta
maxNetCapitalRunningDelta
volume
realizedPnl
}
}
2026-02-24 — Transaction entity removed
The Transaction entity type has been removed from the GraphQL schema. This change is live on all main endpoints. A backward-compatible endpoint is available until March 1, 2026:
https://gmx.squids.live/gmx-synthetics-arbitrum@786bd0/api/graphql
Field changes. Entities that previously referenced transaction: Transaction! now expose a flat transactionHash: String! field. The timestamp field that was nested inside Transaction is now a top-level field on each entity.
| Entity | Old field | New field |
|---|---|---|
TradeAction | transaction: Transaction! | transactionHash: String! |
ClaimAction | transaction: Transaction! | transactionHash: String! |
Order | createdTxn: Transaction! | createdTxnHash: String! |
Order | cancelledTxn: Transaction | cancelledTxnHash: String |
Order | executedTxn: Transaction | executedTxnHash: String |
SwapFeesInfo | transaction: Transaction! | transactionHash: String! |
SwapInfo | transaction: Transaction! | transactionHash: String! |
PositionFeesEntity | transaction: Transaction! | transactionHash: String! |
Distribution | transaction: Transaction! | transactionHash: String! |
Sort field changes. Sort values that referenced the transaction relation are replaced with direct field sorts:
| Old sort value | New sort value |
|---|---|
transaction_timestamp_DESC | timestamp_DESC |
transaction_timestamp_ASC | timestamp_ASC |
Example migration. A TradeAction query before and after:
# Before
tradeActions(limit: 50, orderBy: transaction_timestamp_DESC) {
eventName
transaction {
timestamp
hash
}
}
# After
tradeActions(limit: 50, orderBy: timestamp_DESC) {
eventName
timestamp
transactionHash
}