{"templateId":"markdown","sharedDataIds":{},"props":{"metadata":{"markdoc":{"tagList":["admonition"]},"type":"markdown"},"seo":{"title":"Connecting","description":"Write your own software to control Collective2","keywords":"documentation,api,automated trading,Collective2 API Documentation","lang":"en-US","siteUrl":"https://c2-api.redocly.app/","meta":[{"name":"charset","content":"utf-8"}],"llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"connecting","__idx":0},"children":["Connecting"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-1-verify-your-api-key-with-curl","__idx":1},"children":["Step 1: Verify Your API Key with cURL"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Before writing any code, confirm your API key is valid. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["-i"]}," flag shows the HTTP"," ","response headers — a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["101 Switching Protocols"]}," status means authentication succeeded."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -i \\\n  -H \"Authorization: Bearer YOUR_API_KEY\" \\\n  -H \"Connection: Upgrade\" \\\n  -H \"Upgrade: websocket\" \\\n  -H \"Sec-WebSocket-Version: 13\" \\\n  -H \"Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\" \\\n  wss://api4-general.collective2.com/ws\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A successful response starts with:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"header":{"controls":{"copy":{}}},"source":"HTTP/1.1 101 Switching Protocols\nUpgrade: websocket\nConnection: Upgrade\n"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["An invalid API key returns HTTP 401 with a JSON error body."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-2-open-a-live-connection-with-websocat","__idx":2},"children":["Step 2: Open a Live Connection with websocat"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Use ",{"$$mdtype":"Tag","name":"a","attributes":{"href":"https://github.com/vi/websocat"},"children":["websocat"]}," to open a live connection and see the"," ","server's first message:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"websocat -H \"Authorization: Bearer YOUR_API_KEY\" wss://api4-general.collective2.com/ws\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The server immediately sends a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["connection.connected"]}," message:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"msg_type\": \"connection.connected\",\n  \"id\": \"msg_a1b2c3d4e5f6a1b2c3d4e5f6a7b8\",\n  \"timestamp\": \"2024-01-01T12:00:00Z\",\n  \"v\": 4\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your connection is ready once you receive this message."]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["connection.disconnected"]}," is a reserved message type for future use. The server does not"," ","currently emit it."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"step-3-connect-with-python","__idx":3},"children":["Step 3: Connect with Python"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Install the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["websockets"]}," library if you haven't already:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"pip install websockets\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import asyncio\nimport json\nimport websockets\n\nAPI_KEY = \"YOUR_API_KEY\"\nWS_URL = \"wss://api4-general.collective2.com/ws\"\n\nasync def connect():\n    headers = {\"Authorization\": f\"Bearer {API_KEY}\"}\n    async with websockets.connect(WS_URL, additional_headers=headers) as ws:\n        message = await ws.recv()\n        data = json.loads(message)\n        print(f\"Connected: {data['msg_type']}\")\n        # Your logic here\n\nasyncio.run(connect())\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"heartbeat","__idx":4},"children":["Heartbeat"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The server sends a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["heartbeat"]}," message every 30 seconds to verify the connection is alive:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"msg_type\": \"heartbeat\",\n  \"id\": \"msg_a1b2c3d4e5f6a1b2c3d4e5f6a7b8\",\n  \"timestamp\": \"2024-01-01T12:00:00Z\",\n  \"v\": 4\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your client ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["must"]}," reply with a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["heartbeat.ack"]}," before the next heartbeat arrives"," ","(within 30 seconds). The server allows a 10-second grace period — 40 seconds total from"," ","the previous acknowledgement. Connections that miss a heartbeat are closed."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"msg_type\": \"heartbeat.ack\",\n  \"timestamp\": \"2024-01-01T12:00:01Z\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Always handle heartbeats in your client. A connection that ignores heartbeats will be"," ","closed within 40 seconds of the last acknowledgement."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"python-keep-alive-loop","__idx":5},"children":["Python Keep-alive Loop"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import asyncio\nimport json\nimport websockets\nfrom datetime import datetime, timezone\n\nAPI_KEY = \"YOUR_API_KEY\"\nWS_URL = \"wss://api4-general.collective2.com/ws\"\n\nasync def run():\n    headers = {\"Authorization\": f\"Bearer {API_KEY}\"}\n    async with websockets.connect(WS_URL, additional_headers=headers) as ws:\n        # Wait for connection.connected\n        msg = json.loads(await ws.recv())\n        print(f\"Connected: {msg['msg_type']}\")\n\n        async for raw in ws:\n            msg = json.loads(raw)\n            if msg[\"msg_type\"] == \"heartbeat\":\n                ack = {\n                    \"msg_type\": \"heartbeat.ack\",\n                    \"timestamp\": datetime.now(timezone.utc).isoformat()\n                }\n                await ws.send(json.dumps(ack))\n            else:\n                # Handle other messages\n                print(msg)\n\nasyncio.run(run())\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"disconnecting","__idx":6},"children":["Disconnecting"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Close the connection cleanly when you are done. Most WebSocket libraries handle this"," ","automatically when exiting a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["with"]}," block."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"await ws.close()\n","lang":"python"},"children":[]}]},"headings":[{"value":"Connecting","id":"connecting","depth":1},{"value":"Step 1: Verify Your API Key with cURL","id":"step-1-verify-your-api-key-with-curl","depth":2},{"value":"Step 2: Open a Live Connection with websocat","id":"step-2-open-a-live-connection-with-websocat","depth":2},{"value":"Step 3: Connect with Python","id":"step-3-connect-with-python","depth":2},{"value":"Heartbeat","id":"heartbeat","depth":2},{"value":"Python Keep-alive Loop","id":"python-keep-alive-loop","depth":3},{"value":"Disconnecting","id":"disconnecting","depth":2}],"frontmatter":{"title":"Connecting","description":"How to establish a WebSocket connection to the Collective2 API","seo":{"title":"Connecting"}},"lastModified":"2026-03-24T22:15:54.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/guides/websocket-connect","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}