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

# Prices

> Live price updates over WebSocket for a symbol list you choose.

```
wss://api.ngnmarket.com/v1/ws/prices
```

This pushes the current price for a set of symbols you choose, with the same fields you'd get from [`GET /companies/:symbol`](/api-reference/companies/detail): full OHLCV (open, day's high and low, current price, volume), value traded, market cap, and change percentages across several periods. This is the only channel that requires a query parameter.

This accepts a required comma-separated, case-insensitive list of tickers:

```
wss://api.ngnmarket.com/v1/ws/prices?api_key=ngm_live_YOUR_KEY&symbols=DANGCEM,GTCO,MTNN
```

<Note>
  This is a connect-time filter, not a live subscribe or unsubscribe protocol. There's no message you send after connecting to add or remove symbols.

  To change your watchlist, close the connection and reconnect with a new `symbols` list. NGX data refreshes roughly every 20 minutes, so reconnecting costs you nothing meaningful.
</Note>

How many symbols one connection can request depends on your plan. See [Limits](/websocket/limits). Requesting more than your plan allows fails the connection with `SYMBOL_LIMIT_REACHED`.

<CodeGroup>
  ```javascript Browser theme={null}
  const symbols = ['DANGCEM', 'GTCO', 'MTNN'];
  const ws = new WebSocket(
    `wss://api.ngnmarket.com/v1/ws/prices?api_key=ngm_live_YOUR_KEY&symbols=${symbols.join(',')}`
  );

  ws.onmessage = (event) => {
    const msg = JSON.parse(event.data);
    console.log(msg.type, msg.prices);
  };
  ```

  ```javascript Node.js theme={null}
  import WebSocket from 'ws';

  const symbols = ['DANGCEM', 'GTCO', 'MTNN'];
  const ws = new WebSocket(
    `wss://api.ngnmarket.com/v1/ws/prices?api_key=ngm_live_YOUR_KEY&symbols=${symbols.join(',')}`
  );

  ws.on('message', (data) => {
    const msg = JSON.parse(data.toString());
    console.log(msg.type, msg.prices);
  });
  ```

  ```python Python theme={null}
  import asyncio
  import json
  import websockets

  SYMBOLS = ["DANGCEM", "GTCO", "MTNN"]

  async def main():
      url = f"wss://api.ngnmarket.com/v1/ws/prices?api_key=ngm_live_YOUR_KEY&symbols={','.join(SYMBOLS)}"
      async with websockets.connect(url) as ws:
          async for raw in ws:
              msg = json.loads(raw)
              print(msg["type"], msg["prices"])

  asyncio.run(main())
  ```
</CodeGroup>

## Message format

`snapshot` arrives once, right after you connect, with the current price for every requested symbol that has trading data. `update` arrives whenever any of those prices change, and always carries the full requested set, not just the symbol that moved.

```json theme={null}
{
  "type": "snapshot",
  "prices": [
    {
      "id": 33,
      "symbol": "DANGCEM",
      "name": "Dangote Cement Plc",
      "logo_url": "https://cdn.jsdelivr.net/gh/ngnmarket/ngx-logos/dist/png/DANGCEM.png",
      "sector": "Industrial Goods",
      "sub_sector": "Building Materials",
      "market_classification": "Premium Board",
      "shares_outstanding": 16873559251,
      "website": "www.dangotecement.com",
      "price": 302.50,
      "prev_close": 298.50,
      "open": 299.00,
      "day_high": 305.00,
      "day_low": 297.50,
      "volume": 535714,
      "value_traded": 547886121.80,
      "market_cap": 5150820750000,
      "price_change": 4.00,
      "price_change_percent": 1.34,
      "change_7d_percent": 2.10,
      "change_52w_percent": 133.57,
      "change_1m_percent": 5.42,
      "change_ytd_percent": 89.66,
      "high_52wk": 1189.00,
      "low_52wk": 494.50,
      "last_updated": "2026-08-21T11:40:00.000Z"
    }
  ]
}
```

<Note>
  `day_high` and `day_low` come back `null` before the first trade of the session. They fill in once trading activity for the day exists.
</Note>

## Errors specific to this channel

| Error code             | Status | Cause                                                                     |
| :--------------------- | :----: | :------------------------------------------------------------------------ |
| `SYMBOLS_REQUIRED`     |   400  | Connected without a `symbols` parameter                                   |
| `SYMBOL_NOT_FOUND`     |   404  | One or more requested symbols don't exist. The response lists which ones. |
| `SYMBOL_LIMIT_REACHED` |   429  | Requested more symbols than your plan allows on one connection            |

See [Errors](/websocket/errors) for the full reference, including the auth codes every channel shares.
