> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gains.trade/llms.txt
> Use this file to discover all available pages before exploring further.

# Listing all assets

> Fetch every asset listed on Gains, with its pair index, asset class, leverage bounds, and spread.

There is no dedicated `/assets` endpoint. The list of assets listed on Gains is the `pairs` array of the chain-specific trading backend, and everything needed to describe an asset (asset class, leverage bounds, spread, listing status) comes from the same payload.

You do not need the whole payload for this. `/trading-variables/<keys>` returns only the keys you ask for.

## Endpoint

```bash theme={null}
curl "https://backend-arbitrum.gains.trade/trading-variables/pairs,groups,pairInfos"
```

| Request                                     | Response size | Use it for                                          |
| ------------------------------------------- | ------------- | --------------------------------------------------- |
| `/trading-variables/pairs`                  | \~36 KB       | ticker, asset class index, spread                   |
| `/trading-variables/pairs,groups,pairInfos` | \~270 KB      | the above plus leverage bounds and listing status   |
| `/trading-variables`                        | \~2.7 MB      | everything, including collateral, fee, and OI state |

Sizes measured on Arbitrum in August 2026.

No authentication is required. Rate limits apply, so cache the result rather than fetching it per user action.

### Networks

Same path on every trading backend:

* Arbitrum: `https://backend-arbitrum.gains.trade`
* Base: `https://backend-base.gains.trade`
* Polygon: `https://backend-polygon.gains.trade`
* MegaETH: `https://backend-megaeth.gains.trade`
* Arbitrum Sepolia (testnet): `https://backend-sepolia.gains.trade`

Each network serves its own list. Pair indexes have matched across networks so far, but the set of tradeable assets does not: treat each network's own payload as authoritative instead of reusing another network's list.

<Warning>
  Only the keys returned by `GET /trading-variables-keys` are selectable. An unrecognized key is silently ignored, and a request where every key is unrecognized returns the full payload. `GET /trading-variables/isForexOpen` returns 2.7 MB, not one boolean.
</Warning>

## Response

```json theme={null}
{
  "pairs": [
    { "from": "BTC", "to": "USD", "spreadP": "100000000", "groupIndex": "0", "feeIndex": "13" },
    { "from": "ETH", "to": "USD", "spreadP": "100000000", "groupIndex": "0", "feeIndex": "13" }
  ],
  "groups": [
    { "name": "crypto", "minLeverage": "1100", "maxLeverage": "200000" },
    { "name": "forex", "minLeverage": "10000", "maxLeverage": "1000000" }
  ],
  "pairInfos": {
    "maxLeverages": [0, 0],
    "pairDepthBands": [],
    "pairFactors": []
  }
}
```

Every numeric field is fixed-precision. `pairs` and `groups` serialize theirs as strings, `pairInfos.maxLeverages` as numbers, so parse both sides with `Number()`:

| Field                                          | Precision                                | Example                              |
| ---------------------------------------------- | ---------------------------------------- | ------------------------------------ |
| `pairs[].spreadP`                              | % in 1e10                                | `"100000000"` is 0.01%               |
| `groups[].minLeverage`, `groups[].maxLeverage` | multiplier in 1e3                        | `"1100"` is 1.1x, `"200000"` is 200x |
| `pairInfos.maxLeverages[]`                     | multiplier in 1e3, `0` means no override | `4000` is 4x                         |

`pairs[].groupIndex` indexes `groups`, and `pairs[].feeIndex` indexes the `fees` array (request the `fees` key if you need fee parameters).

## Pair index

The pair index is the array position. It is not a field on the object.

Every other surface keys off it: `pairIndex` on trades and orders, `/api/holding-rates/{collateralIndex}/{pairIndex}`, and the `closes[pairIndex]` array of the price feed. Read the index before you sort or filter, and never renumber the array.

The array is append-only. Delisted assets keep their slot forever, so the raw list is a history of everything ever listed, not the current offering.

## Filtering out delisted assets

An asset is currently tradeable when its effective max leverage is at least its group min leverage. This is the same rule the SDK applies in `getMarketLeverageRestrictions`:

1. `effectiveMax = maxLeverages[pairIndex] === 0 ? group.maxLeverage : maxLeverages[pairIndex]`
2. the asset is delisted when `effectiveMax < group.minLeverage`

Delisting is expressed by setting the pair override far below the group minimum, so a delisted asset stays in the array with a `maxLeverages` entry such as `1` (0.001x). In August 2026 this filter kept 180 of the 490 entries served by the Arbitrum backend.

## Example

```typescript theme={null}
type Asset = {
  pairIndex: number;
  name: string;
  from: string;
  to: string;
  assetClass: string;
  minLeverage: number;
  maxLeverage: number;
  spreadP: number;
};

async function listAssets(backendUrl: string): Promise<Asset[]> {
  const response = await fetch(`${backendUrl}/trading-variables/pairs,groups,pairInfos`);
  const { pairs, groups, pairInfos } = await response.json();

  return pairs
    .map((pair, pairIndex) => {
      const group = groups[Number(pair.groupIndex)];
      const minLeverage = Number(group.minLeverage) / 1e3;
      const override = Number(pairInfos.maxLeverages[pairIndex]) / 1e3;

      return {
        pairIndex,
        name: `${pair.from.split("_")[0]}/${pair.to}`,
        from: pair.from,
        to: pair.to,
        assetClass: group.name,
        minLeverage,
        maxLeverage: override === 0 ? Number(group.maxLeverage) / 1e3 : override,
        spreadP: Number(pair.spreadP) / 1e10,
      };
    })
    .filter((asset) => asset.maxLeverage >= asset.minLeverage);
}

const assets = await listAssets("https://backend-arbitrum.gains.trade");
```

The same list from the shell:

```bash theme={null}
curl -s "https://backend-arbitrum.gains.trade/trading-variables/pairs,groups,pairInfos" \
  | jq -r '
    . as $tv
    | [.pairs | to_entries[]
       | . as {key: $i, value: $p}
       | $tv.groups[$p.groupIndex | tonumber] as $g
       | (($tv.pairInfos.maxLeverages[$i] | tonumber) / 1000) as $override
       | (($g.minLeverage | tonumber) / 1000) as $min
       | (if $override == 0 then (($g.maxLeverage | tonumber) / 1000) else $override end) as $max
       | select($max >= $min)
       | [$i, ($p.from | split("_")[0]) + "/" + $p.to, $g.name, $max]]
    | .[] | @tsv'
```

<Info>
  `transformGlobalTradingVariables` from [@gainsnetwork/sdk](https://www.npmjs.com/package/@gainsnetwork/sdk) does the precision conversion and index assignment for you, and returns `pairs[].pairIndex` already populated. Use it when you already depend on the SDK.
</Info>

### Ticker suffixes

A `from` value can carry a numeric suffix, such as `GOOGL_1`. That happens when an asset is listed again in a new slot while its original slot stays in the array as delisted. Strip the suffix for display, and keep the raw value when matching against backend data.

## Asset classes

`groups[].name` is the asset class: `crypto`, `altcoins`, `crypto-degen`, `forex`, `forex-minor`, `forex-exotic`, `stocks-1` to `stocks-3`, `indices`, `commodities-1` and `commodities-2`.

Numbered suffixes are risk buckets with their own leverage and fee parameters, not separate asset classes. Strip the suffix to get the user-facing class.

## Market hours

Assets outside crypto only trade while their market is open. The flags `isForexOpen`, `isStocksOpen`, `isIndicesOpen`, and `isCommoditiesOpen` are part of the full payload and are not selectable keys, so read them from `GET /trading-variables`.

## Keeping the list fresh

Listings change rarely, but the payload is refreshed continuously and carries `lastRefreshed` and `refreshId`. Cache the derived asset list and refresh it on the `tradingVariables` WebSocket event described in [Backend](/developer/integrators/backend#event-stream), or poll every few minutes.
