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

# Overview

> Two sockets, one subscribe model, theses and pump.fun callouts as they arrive.

![Frames on wss://api.fomoscan.sh/v2/ws over time: FomoScan sends ready, you send subscribe, FomoScan confirms with subscription, then sends theses; a ping from FomoScan is answered with a pong. Each socket costs 20,000 CU per minute open](https://mintcdn.com/socialscan/pX0brSzGgXBryxeh/images/art/websockets.webp?fit=max\&auto=format\&n=pX0brSzGgXBryxeh\&q=85\&s=8d2fb735b37c5a31f4e63428eef5c6b8)

| Socket | Carries |
| - | - |
| `wss://api.fomoscan.sh/v2/ws` | Trader theses |
| `wss://api.fomoscan.sh/v2/ws/pump` | new pump.fun callouts |

> [!NOTE]
>
> Sockets are included from the **Growth** plan. Keys on legacy plans keep the
> access they have. Other keys are refused on the handshake with
> `403 WEBSOCKET_PLAN_REQUIRED`.

## Connect and subscribe

Authenticate with the same `Authorization` header as REST. You attach
unsubscribed and receive nothing until you send a `subscribe` frame naming every
token or up to 50 token addresses:

```json theme={null}
{ "type": "subscribe", "subscription": { "type": "all" } }
{ "type": "subscribe", "subscription": { "type": "tokens", "tokens": ["So11111111111111111111111111111111111111112"] } }
```

A connection holds one subscription. A new `subscribe` replaces the current one,
so you can change it without reconnecting. Send `{ "type": "unsubscribe" }` to
stop receiving. A `tokens` list cannot be empty.

```js theme={null}
import WebSocket from "ws";

const ws = new WebSocket("wss://api.fomoscan.sh/v2/ws", {
  headers: { Authorization: `Bearer ${process.env.FOMOSCAN_KEY}` },
});

ws.on("open", () => {
  ws.send(JSON.stringify({ type: "subscribe", subscription: { type: "all" } }));
});

ws.on("message", (raw) => {
  const frame = JSON.parse(raw.toString());
  if (frame.type === "thesis") console.log(frame.data.id, frame.data.thesisHash);
  if (frame.type === "error") console.error(frame.error);
});

ws.on("error", (err) => console.error(err.message));
```

## Feeds

> **[Thesis feed](https://docs.fomoscan.sh/websockets/theses.md)**
>
> `/v2/ws`: trader theses, every token or up to 50.

> **[pump.fun feed](https://docs.fomoscan.sh/websockets/pump.md)**
>
> `/v2/ws/pump`: new pump.fun callouts.

You also receive `ready` on attach, `subscription` after every accepted frame,
`replaced` when a `subscribe` tore down an earlier subscription, and `error` for
a rejected frame. A rejected frame does not close the socket or change your
subscription. The full frame list is in the
[API reference](https://docs.fomoscan.sh/api-reference/introduction.md) under **WebSocket**.

## Before you build on it

* **Hold the socket on your backend.** The browser `WebSocket` constructor
  cannot set headers. Relay to your front end.
* **There is no replay.** Take a snapshot from the REST wall on connect and
  reconcile on `thesisHash` (`id` for pump callouts). After a drop, fetch the
  newest page to close the gap, then reattach and resubscribe.
* **Answer pings.** We ping on a fixed interval and close with `1011` a socket
  we have not heard from for three ping intervals. A pong or any frame you send
  counts. Browser and Node `ws` clients reply on their own; a hand-rolled
  client must reply itself.
* **Read as fast as frames arrive.** A socket that falls too far behind is
  closed with `1013`.

## Close codes

| Code | Reason | What to do |
| - | - | - |
| `1001` | We restarted | Reconnect and resubscribe |
| `1008` | Out of credits | Top up, then reconnect |
| `1009` | A frame over 64 KiB, or more than 32 frames or 64 KiB sent before `ready` | Send smaller frames, reconnect |
| `1011` | No pong for three intervals | Fix the ping handler, reconnect |
| `1013` | Frames queued faster than you read them | Read faster, reconnect |

A refusal before the upgrade is an HTTP status, not a close code: `401` for a
bad key, `402 QUOTA_EXCEEDED` when the key cannot pay for the first minute, and
`403` for a plan or scope that does not include sockets.

Each socket is billed per minute held open, on its own, from the same credits as
REST calls. The first minute is charged when the socket opens. One token or
every token costs the same.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.