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

# Tokens endpoint

> List routable Stellar tokens with identifiers, token family, decimals, SAC peer, and route degree.

List routable Stellar tokens from the current Aggregator snapshot.

## The endpoint

| Field        | Value                            |
| ------------ | -------------------------------- |
| Method       | `GET`                            |
| Path         | `/api/v1/tokens`                 |
| Base URL     | `https://stellar-swap.xoxno.com` |
| Auth         | None                             |
| Availability | Public                           |
| Rate limit   | 100 requests per second per IP   |

## Request params

None. The endpoint returns the full token catalog from the current snapshot.

## Request

<Tabs>
  <Tab title="cURL">
    ```bash theme={"system"}
    curl "https://stellar-swap.xoxno.com/api/v1/tokens"
    ```
  </Tab>

  <Tab title="TypeScript">
    ```ts theme={"system"}
    const res = await fetch("https://stellar-swap.xoxno.com/api/v1/tokens");
    if (!res.ok) throw new Error(`tokens failed: ${res.status}`);

    const tokens = await res.json();
    const routable = tokens.filter((token: { degree: number }) => token.degree > 0);
    console.log(routable.length);
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"system"}
    import httpx

    res = httpx.get("https://stellar-swap.xoxno.com/api/v1/tokens", timeout=10)
    res.raise_for_status()

    tokens = res.json()
    routable = [token for token in tokens if token["degree"] > 0]
    print(len(routable))
    ```
  </Tab>

  <Tab title="Rust">
    ```rust theme={"system"}
    use reqwest::Client;
    use serde::Deserialize;

    #[derive(Deserialize)]
    struct TokenEntry {
        id: String,
        kind: String,
        decimals: u8,
        #[serde(rename = "sacPeer")]
        sac_peer: Option<String>,
        degree: usize,
    }

    #[tokio::main]
    async fn main() -> Result<(), Box<dyn std::error::Error>> {
        let tokens: Vec<TokenEntry> = Client::new()
            .get("https://stellar-swap.xoxno.com/api/v1/tokens")
            .send()
            .await?
            .error_for_status()?
            .json()
            .await?;

        let routable = tokens.iter().filter(|token| token.degree > 0).count();
        println!("{routable}");
        Ok(())
    }
    ```
  </Tab>
</Tabs>

## Success response

```json theme={"system"}
[
  {
    "id": "CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA",
    "kind": "soroban",
    "decimals": 7,
    "sacPeer": null,
    "code": null,
    "degree": 42
  },
  {
    "id": "USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN",
    "kind": "classic",
    "decimals": 7,
    "sacPeer": "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
    "code": "USDC",
    "degree": 5
  }
]
```

## Fields

| Field      | Type           | Description                                                             |
| ---------- | -------------- | ----------------------------------------------------------------------- |
| `id`       | string         | Canonical token identifier: Soroban C-strkey, `XLM`, or `CODE:GISSUER`. |
| `kind`     | string         | `native`, `classic`, or `soroban`.                                      |
| `decimals` | number         | Decimal precision used for amount conversion.                           |
| `sacPeer`  | string or null | Soroban Asset Contract peer for Classic assets, when known.             |
| `code`     | string or null | Classic asset code, such as `USDC`.                                     |
| `degree`   | number         | Count of graph edges connected to the token in the current snapshot.    |

## Errors

| Status | Cause                                                      | Fix                                      |
| ------ | ---------------------------------------------------------- | ---------------------------------------- |
| `429`  | Per-IP rate limit exceeded.                                | Back off and queue requests client-side. |
| `503`  | Snapshot is unavailable or stale beyond service readiness. | Retry with backoff.                      |

## Edge cases

| Case                        | Behavior                                               | Client handling                                        |
| --------------------------- | ------------------------------------------------------ | ------------------------------------------------------ |
| `degree=0`                  | Token exists in the snapshot but has no routable edge. | Hide it from swap selectors or show it as unavailable. |
| Classic asset has `sacPeer` | The token can map to a Soroban Asset Contract.         | Use the SAC peer for Soroban-only flows.               |
| Alias collision             | Asset codes are not globally unique.                   | Store the canonical `id`, not only `code`.             |
| Snapshot changes            | Newly indexed pools can change the catalog.            | Cache for short windows and refresh before quoting.    |

## Verify

Before quoting:

* The selected token has `degree > 0`.
* Amount conversion uses that token's `decimals`.
* Soroban flows use a C-strkey or SAC peer when needed.
* Direct-user Classic flows keep the full `CODE:GISSUER` identifier.

## Related guides

* [Quote endpoint](/stellar-aggregator/quote)
* [Stellar execution planes](/stellar-aggregator/execution-planes)
* [Token amounts](/resources/token-amounts)
* [Quote freshness](/resources/quote-freshness)
