WebSockets
Subscribe to a model's real-time OnlyFans event feed through a persistent WebSocket connection. The gateway reads events off its own internal event stream and broadcasts them to every subscriber — it never opens a new OnlyFans session on your behalf.
Broadcast architecture
Endpoint
wss://api.onlyfans-api.ai/ws/bridge/{model_id}| Parameter | Type | Description |
|---|---|---|
model_id | path | UUID of the connected model to subscribe to |
X-API-Key | header | Your API key. Takes priority if both the header and the api_key query param are present. |
api_key | query | Fallback for clients that can't set custom headers (e.g. a browser WebSocket). |
Prefer the header
X-API-Key header from a server-side client (Node.js, Python, etc.) whenever possible. The api_key query param exists only for clients that can't set custom headers, such as a browser WebSocket — a query param can end up in server access logs, browser history, or proxy logs, so prefer the header when you have the choice.Connecting
Open the WebSocket connection. The gateway validates your API key, verifies the model is connected, then sends a confirmation message.
import WebSocket from "ws"
const ws = new WebSocket(
"wss://api.onlyfans-api.ai/ws/bridge/MODEL_UUID",
{ headers: { "X-API-Key": process.env.OF_API_KEY } }
)
ws.on("open", () => console.log("WS open"))
ws.on("message", (raw) => {
const msg = JSON.parse(raw.toString())
if (msg.connected) {
console.log("Subscribed to model:", msg.modelId)
return
}
if (msg.error) {
console.error("Error:", msg.error, msg.code)
return
}
// Real-time event: message, typing, block_user, chat_message_like, chat_message_delete
console.log("Event:", msg)
})
ws.on("close", (code) => console.log("Closed:", code))
ws.on("error", (err) => console.error("WS error:", err))Confirmation message
// Sent by the gateway immediately after a successful subscription
{
"connected": true,
"modelId": "16f3d13b-9415-4e6b-babe-e8db6b31bd97"
}Events
After subscribing, the gateway pushes one JSON message per event. This is a read-only feed — there is no way to send actions back through this connection.
| event_type | Description |
|---|---|
message | A chat message was sent or received — both fan-inbound and model-outgoing, distinguished by direction |
typing | A fan started typing in a conversation |
block_user | A fan was blocked |
chat_message_like | A chat message was liked |
chat_message_delete | A chat message was deleted |
message event
// event_type: "message" — fired for both fan-inbound and model-outgoing messages
{
"entry_id": "1723190400000-0",
"event_type": "message",
"direction": "fan-inbound",
"model_id": "16f3d13b-9415-4e6b-babe-e8db6b31bd97",
"conversation_id": "8f2c1e...",
"message_id": "9a7b3d...",
"fan_platform_id": "12345678",
"timestamp": "2026-08-09T13:45:02.123Z",
"organizations": "[{\"organizationId\":\"...\",\"atlasOrganizationId\":null}]",
"content": "{...raw OnlyFans message frame...}"
}typing event
// event_type: "typing"
{
"entry_id": "1723190401500-0",
"event_type": "typing",
"model_id": "16f3d13b-9415-4e6b-babe-e8db6b31bd97",
"payload": "{...raw OnlyFans typing frame...}",
"conversation_id": "8f2c1e...",
"timestamp": "2026-08-09T13:45:03.601Z"
}Connection Errors
If the connection cannot be established, the gateway sends a JSON error message and closes the socket immediately.
| Code | Meaning | Fix |
|---|---|---|
NO_CREDENTIAL | No X-API-Key header provided | Add the X-API-Key header |
INVALID_API_KEY | API key is invalid or revoked | Generate a new key from the dashboard |
NO_SUBSCRIPTION | Organization has no active subscription | Subscribe to a plan in the Billing section |
MODEL_NOT_FOUND | Model UUID not found or not accessible | Verify the model UUID and org access |
MODEL_NOT_CONNECTED | Model session is not active | Reconnect the model from the Models page |
Example error message
// Sent before the connection is closed
{
"error": "Model is not connected",
"code": "MODEL_NOT_CONNECTED"
}Reconnection
The gateway does not automatically reconnect on your behalf. If the socket closes unexpectedly, implement exponential backoff in your client before reopening the connection.
function connectWithRetry(modelId, apiKey, attempt = 0) {
const delay = Math.min(1000 * 2 ** attempt, 30000)
setTimeout(() => {
const ws = new WebSocket(
`wss://api.onlyfans-api.ai/ws/bridge/${modelId}`,
{ headers: { "X-API-Key": apiKey } }
)
ws.addEventListener("close", (e) => {
if (e.code !== 1000) { // not a clean close
connectWithRetry(modelId, apiKey, attempt + 1)
}
})
}, delay)
}