Troubleshooting
Use this page when API reads do not match what you expect. Most issues fall into one of four buckets: invalid request parameters, stale snapshots, using the wrong surface for the job, or expecting immediate read-after-write consistency.
400 Bad Request
Both read endpoints and order preparation return 400 for requests that can't be accepted.
Common read cases:
/ordersand/positionsrequire a valid account address./markets/tickersvalidatesaddressesandsymbolsfilters./markets/trading-capacityrequires a validsymboland adirectionoflongorshort./ratesvalidatesperiodandaverageBy./apy,/yield/gm-pools, and/performance/*validate their filters./yield/gm-poolsrejects malformed pool addresses./yield/gm-user-earningsrequires a validaccountaddress./tradessinceanduntil, and/trades/searchfromTimestampandtoTimestamp, must be whole Unix seconds from0to2147483647. A fractional or millisecond value returnsInvalid since timestamp: whole seconds expected(with the field's name)./stats/*rejects an unknownnetworks,versions, ormetricvalue, and afromortothat isn't whole Unix seconds. Repeat a list parameter for each value (networks=arbitrum&networks=avalanche); a comma-separated list is rejected. A filter that matches no configured source, such asnetworks=megaeth&versions=v1, returnsNo configured source matches the requested networks and versions.
For schema validation failures, such as a missing required parameter or a value outside an enum, the message field is an object keyed by parameter name instead of a string.
POST /orders/txns/prepare can also return an application error:
INSUFFICIENT_LIQUIDITYmeans a Market Increase exceeds current JIT-aware trading capacity. Reduce the requested size or refresh capacity before preparing again. A resting increase can return the same code invalidationWarningsinstead because liquidity will be checked again when it triggers.INSUFFICIENT_COLLATERALmeans collateral remaining after fees would be below the on-chain minimum. Add collateral or change the order. For TWAP increases, every part must satisfy the minimum.
validationWarnings are non-blocking, but clients need to surface them before signing. If you receive a blocking 400, validate the query or change the order inputs before retrying; repeating the unchanged request won't succeed.
503 Service Unavailable
A 503 from the GMX API means the request couldn't be served at that moment. Unlike a 400, the same request can succeed later, so retry with backoff.
/yield/gm-user-earningsreturns503while earnings data is warming up on a deployment, or on a deployment where it isn't available./stats/*returns503on a deployment that doesn't serve protocol stats. The production hosts route/v1/statsto the Arbitrum deployment; if you still get a503, call an Arbitrum host directly./pairs/solanareturns503on a deployment that doesn't compute the GMX on Solana pair feed, and while its snapshot is still being built or is older than 15 minutes. Only the Arbitrum deployment computes it, and aggregators read it athttps://solana.gmxapi.io/v1/pairs.POST /orders/txns/prepareand other routes return503when the request runs past the60second server timeout.POST /gmx-account/withdraw/cross-chain/submitreturns503when the relay times out or is unavailable. The outcome is unknown because the relay may already hold the operation, so don't sign a new withdrawal.POST /gmx-account/withdraw/cross-chain/statususually reportsrelay_unknownfor the request, and that status can't change until a submit gets ataskId, so you must resend the same signed body with the samerequestIdon the same base URL. A resend can't execute the withdrawal twice. After a resend gets ataskId, status staysrelay_unknownuntil the relay reports a final result. You can resend untilrelayParams.deadline, one hour after prepare. After that, a resend returns400, status staysrelay_unknown, and the withdrawal can no longer execute. Once a block's timestamp is past the deadline, readdigestsonrelayRouterAddress, on the network of the base URL you used, for the EIP-712 hash of the typed data, and prepare a new withdrawal only if it returnsfalse. The503body has nocodeorrequestId, so keep therequestIdfrom the prepare response. With SDK v2, useprepareCrossChainWithdraw,signCrossChainWithdraw, andsubmitCrossChainWithdrawwhen you need to be able to resend, becauseexecuteCrossChainWithdrawdoesn't return the signed body after a failed submit. A4xxon a first submit means nothing was relayed, but a4xxon a resend doesn't tell you what happened to the earlier attempt.- A
503whose body hascode: "BACKEND_UNAVAILABLE"means the request reached a GMX API pool that doesn't serve that route. Retry, and if it persists, report it with theX-Trace-Idresponse header. - On the
.aibase URLs, a503from the API reaches the client as a504with an HTML body; handle it like a503. The exception is the generic/relay/*routes: only the.iobase URLs serve them, so don't retry them on.ai, and send them to the.iobase URL of the same network.
On the .io base URLs, for the earnings, stats, Solana pair feed, and withdrawal submit cases, the response body is the generic {"message":"Internal Server Error"}, so use the route and the X-Trace-Id header, not the message, to tell the cases apart.
A value looks stale
Start by checking whether you are reading from a cached or indexed surface.
- Use Oracle API
/markets/infowhen you need near-live market state. - Use Oracle API
/marketswhen a10second cache window is acceptable. - Use GraphQL for historical activity, not for the latest write-path state.
- Use a single composite read where possible instead of stitching unrelated polls together.
For GMX API market values, inspect each /v1/markets/values row's updatedAt field. It is a Unix timestamp in milliseconds and uses the older timestamp from the main market-values refresh and the virtualInventoryForPositionsInTokens refresh. A null value means at least one component has no successful refresh timestamp.
The default backend pulls market values every 5 seconds and marks a row stale for monitoring after 15 seconds. This threshold does not evict the row:
- A total pull failure preserves the previous values and timestamps, which continue to age.
- A market-specific failed refresh can preserve the previous values while setting
updatedAttonull. - Disabled markets are excluded from backend values-staleness accounting.
Compare updatedAt with a freshness limit appropriate for your application. Don't treat an HTTP 200 response as proof that every value came from the latest pull.
For GMX API protocol stats (/stats/*), check the response metadata before trusting a total:
meta.completenessispartialwhen any source in scope isn'tfresh. Stale sources still count toward the sums; missing sources are left out of them.meta.sources[]reports each source'shealth(fresh,stale, ormissing),lagSeconds, andlastError. A source becomesstalewhen its data lags by more than900seconds, or1800seconds for the GMX on Solana source.- Timeseries points with
provisional: truecover days that can still change, and so can the four days before the current day. tvl.poolsValuedAtandtvl.valuedAtreport when TVL was valued.
For /yield/gm-user-earnings, window.endTimestamp reports when the earnings were calculated.
If you need one coherent account snapshot, prefer one positions call with includeRelatedOrders: true over multiple loosely coordinated reads.
A write succeeded, but the API still does not show it
That is usually a surface mismatch or an operation that hasn't reached a final state.
- SDK v2 and GMX API
/orders/txns/*endpoints prepare, submit, and track signed order intents. Persist theirrequestIdand poll order status on the same GMX API host that served prepare. Thegmxapi.ioandgmxapi.aipeers don't share order records, so status on the other peer returns404with the messageTransaction not found. Don't fail over to the peer for thatrequestId; prepare the order again if the original host can't continue the chain. See Idempotency and race conditions. - The low-level GMX Relay surface accepts already-built relay-router calldata through
POST /relay/submit. Persist the returnedtaskIdand poll it throughPOST /relay/status. - SDK v1 and direct contracts submit transactions from your wallet or backend RPC client. Wait for the transaction receipt, then poll the read surface you care about.
Generic relay statuses are pending, executed, reverted, failed, and unknown. Treat unknown as inconclusive rather than failed: keep the operation pending until your own timeout, and don't resubmit the same signed calldata blindly. A 404 immediately after an accepted submission can also be transient while status becomes readable.
For a reverted relay operation, inspect reason and the top-level revertData when present. revertData contains public on-chain revert bytes and can be decoded by the client. If submission fails before returning a taskId, capture the X-Trace-Id response header; it may be the only support identifier.
Don't assume the first follow-up HTTP or GraphQL read will reflect final state. Relay processing, transaction inclusion, keeper execution, and indexing can complete at different times.
Positions and orders do not line up
If your UI shows position state together with linked orders, fetch them from the same logical snapshot.
Recommended pattern:
import { GmxApiSdk } from "@gmx-io/sdk/v2";
const apiSdk = new GmxApiSdk({ chainId: 42161 });
const positions = await apiSdk.fetchPositionsInfo({
address: "0x9f7198eb1b9Ccc0Eb7A07eD228d8FbC12963ea33",
includeRelatedOrders: true,
});
Use a separate account-wide orders read only when you need a dedicated orders screen.
There is no documented public SLA
The current manual docs do not publish a public SLA for these surfaces. Build your client as if network errors, timeouts, or stale snapshots can happen.
Recommended client behavior:
- Use endpoint-specific fallback URLs where they are documented.
- Add retries with backoff for safe read operations.
- Keep write confirmation logic separate from read polling logic.
- Log the exact surface you queried so you can distinguish GMX API, Oracle API, GraphQL, and SDK-backed reads during incident review.
- Capture the
X-Trace-Idresponse header for GMX API failures. It is exposed to browser clients through CORS; include it with anyrequestIdortaskIdin support reports.
Next steps
- Use the Integration guide for workflow selection.
- Use Fallback URLs for Oracle API public read failover.