---
title: "Send heartbeats and drop dead connections · Agent Wranglers"
url: https://agentwranglers.se/playbook/api-design/live-connections/heartbeats
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Send heartbeats and drop dead connections

For AI agents: the [site map](https://agentwranglers.se/llms.txt) lists everything here, and this page sits under [Live connections](https://agentwranglers.se/playbook/api-design/live-connections.md).

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

- ID: API-71
- Level: Recommended
- Chapter: [Live connections](https://agentwranglers.se/playbook/api-design/live-connections)
- Rule: Send a ping on every quiet WebSocket every 30 seconds, and close a connection that misses two pongs in a row.
- How: A phone that loses signal leaves no close frame behind. Its connection holds memory and a slot on the server until something notices. Ping and pong frames are built into the protocol, so most libraries send them with one setting. Event streams use a comment line instead, as [stream in a standard format](https://agentwranglers.se/playbook/api-design/requests-and-responses/streamed-responses) says. Keep the ping gap shorter than the idle timeout of every proxy and load balancer in front of the server.
- Check: Connect a test client, then block its network without closing the socket. The server closes the connection within 90 seconds and the open connection count drops by one.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
const PING_INTERVAL_MS = 30_000;

setInterval(() => {
  for (const socket of server.clients) {
    if (socket.missedPongs >= 2) { socket.terminate(); continue; }
    socket.missedPongs = (socket.missedPongs ?? 0) + 1;
    socket.ping();
  }
}, PING_INTERVAL_MS);

server.on('connection', socket => socket.on('pong', () => { socket.missedPongs = 0; }));
```

### Python example

```python
import websockets

PING_INTERVAL_SECONDS = 30

async def main():
    async with websockets.serve(
        handle_socket, "0.0.0.0", 8080,
        ping_interval=PING_INTERVAL_SECONDS,
        ping_timeout=2 * PING_INTERVAL_SECONDS,
    ) as server:
        await server.serve_forever()
```

## Sources

- [RFC 6455 section 5.5.2 Ping](https://www.rfc-editor.org/rfc/rfc6455#section-5.5.2)
- [OWASP WebSocket Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/WebSocket_Security_Cheat_Sheet.html)

## Related rules

- [Stream in a standard format and describe each item](https://agentwranglers.se/playbook/api-design/requests-and-responses/streamed-responses) in API design

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/api-design/live-connections/heartbeats">Agent Wranglers API design playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Check access when the connection opens and while it lasts](https://agentwranglers.se/playbook/api-design/live-connections/connection-access)
- Next: [Reconnect with backoff and resume from the last event](https://agentwranglers.se/playbook/api-design/live-connections/reconnect-and-resume)
- [Live connections](https://agentwranglers.se/playbook/api-design/live-connections)
