Skip to content
This repository was archived by the owner on Apr 26, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,39 @@ Example SSID format:

If you are unable to find it, try running the automatic SSID scraper under the `tools` folder.

## Comon errors
## Common errors

### Traceback:
### Authentication timeout or connection immediately closes

If you see errors like:
```
WARNING | pocketoptionapi_async.websocket_client:receive_messages:395 - WebSocket connection closed
WARNING | pocketoptionapi_async.client:_start_regular_connection:245 - Failed to connect to region DEMO: Authentication timeout
```

**Solution**: Your SSID is likely in the wrong format or is expired. Make sure you are using the **complete SSID format**, not just the session ID:

✅ **Correct format:**
```python
SSID = '42["auth",{"session":"n1p5ah5u8t9438rbunpgrq0hlq","isDemo":1,"uid":84402008,"platform":1}]'
```

❌ **Wrong format (just the session):**
```python
SSID = 'dxxxxxxxxxxxxxxxxxxxxxxxxxxxx' # This won't work!
```

To get the correct SSID:
1. Open PocketOption in your browser
2. Open Developer Tools (F12)
3. Go to Network tab
4. Filter by "WS" (WebSocket)
5. Look for a message that starts with `42["auth",`
6. Copy the **entire message** including the `42["auth",{...}]` part

### Websockets version error

## Traceback:
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incorrect section header structure. The header "## Traceback:" should be "### Traceback:" to maintain proper heading hierarchy, as it appears under the "### Websockets version error" section.

Suggested change
## Traceback:
### Traceback:

Copilot uses AI. Check for mistakes.
```
2025-07-13 15:25:16.531 | INFO | pocketoptionapi_async.client:__init__:130 - Initialized PocketOption client (demo=True, uid=105754921, persistent=False) with enhanced monitoring
2025-07-13 15:25:16.532 | INFO | pocketoptionapi_async.client:connect:162 - Connecting to PocketOption...
Expand Down
107 changes: 107 additions & 0 deletions examples/correct_ssid_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
"""
Example demonstrating the correct way to use SSID with PocketOption API

This example shows how to:
1. Get the correct SSID format from browser
2. Initialize the client properly
3. Handle authentication errors
"""

import asyncio
from pocketoptionapi_async import AsyncPocketOptionClient
from pocketoptionapi_async.exceptions import InvalidParameterError, AuthenticationError


async def main():
print("=" * 70)
print("PocketOption API - Correct SSID Usage Example")
print("=" * 70)

print("\n📋 INSTRUCTIONS:")
print("1. Open PocketOption in your browser (https://pocketoption.com)")
print("2. Press F12 to open Developer Tools")
print("3. Go to the Network tab")
print("4. Filter by 'WS' (WebSocket)")
print("5. Look for a message starting with: 42[\"auth\",")
print("6. Copy the ENTIRE message (including 42[\"auth\",{...}])")
print("\n")

# Example of CORRECT SSID format
print("✅ CORRECT SSID format:")
print(' 42["auth",{"session":"your_session_here","isDemo":1,"uid":12345,"platform":1}]')
print("\n")

# Example of WRONG format
print("❌ WRONG SSID format (just the session ID):")
print(' dxxxxxxxxxxxxxxxxxxxxxxxxxxxx')
print("\n")

# Get SSID from user
ssid_input = input("Enter your SSID (or press Enter to see demo): ").strip()

if not ssid_input:
print("\n📝 Using demo SSID to show validation...")
ssid_input = '42["auth",{"session":"demo_session_id","isDemo":1,"uid":12345,"platform":1}]'

print("\n" + "=" * 70)

try:
print("🔧 Initializing PocketOption client...")

# Create client with SSID
client = AsyncPocketOptionClient(
ssid=ssid_input,
is_demo=True, # Set to False for live trading
enable_logging=True # Set to False to reduce console output
)

print("✅ Client initialized successfully!")
print(f" Session ID: {client.session_id[:20]}...")
print(f" User ID: {client.uid}")
print(f" Demo mode: {client.is_demo}")
print("\n")

# Try to connect
print("🔌 Connecting to PocketOption...")
connected = await client.connect()

if connected:
print("✅ Connected successfully!")

# Get balance
try:
balance = await client.get_balance()
print(f"💰 Balance: {balance.balance} {balance.currency}")
except Exception as e:
print(f"⚠️ Could not get balance: {e}")

# Disconnect
await client.disconnect()
print("✅ Disconnected successfully")

else:
print("❌ Connection failed")
print("\n💡 Troubleshooting:")
print(" • Make sure your SSID is in the correct format")
print(" • Your SSID might be expired - get a fresh one from browser")
print(" • Make sure you copied the ENTIRE message including 42[\"auth\",{...}]")

except InvalidParameterError as e:
print(f"\n❌ SSID Format Error:")
print(f" {e}")
print("\n💡 Make sure you're using the complete SSID format from browser DevTools!")

except AuthenticationError as e:
print(f"\n❌ Authentication Error:")
print(f" {e}")
print("\n💡 Your SSID might be expired. Get a fresh one from your browser!")

except Exception as e:
print(f"\n❌ Unexpected Error:")
print(f" {type(e).__name__}: {e}")

print("\n" + "=" * 70)


if __name__ == "__main__":
asyncio.run(main())
96 changes: 82 additions & 14 deletions pocketoptionapi_async/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,14 +78,10 @@ def __init__(
if not enable_logging:
logger.remove()
logger.add(lambda msg: None, level="CRITICAL") # Disable most logging
# Parse SSID if it's a complete auth message

# Validate and parse SSID
self._original_demo = None # Store original demo value from SSID
if ssid.startswith('42["auth",'):
self._parse_complete_ssid(ssid)
else:
# Treat as raw session ID
self.session_id = ssid
self._complete_ssid = None
self._validate_and_parse_ssid(ssid)

# Core components
self._websocket = AsyncWebSocketClient()
Expand Down Expand Up @@ -656,6 +652,39 @@ def get_connection_stats(self) -> Dict[str, Any]:

return stats # Private methods

def _validate_and_parse_ssid(self, ssid: str) -> None:
"""Validate and parse SSID format"""
if not ssid or not isinstance(ssid, str):
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition if not ssid or not isinstance(ssid, str) will always evaluate isinstance(ssid, str) as False when ssid is None, since the first condition short-circuits. Consider checking the type first before checking for emptiness to avoid potential issues, or explicitly check for None: if ssid is None or not isinstance(ssid, str) or not ssid:.

Suggested change
if not ssid or not isinstance(ssid, str):
if ssid is None or not isinstance(ssid, str) or not ssid:

Copilot uses AI. Check for mistakes.
raise InvalidParameterError(
"SSID must be a non-empty string. "
"Expected format: 42[\"auth\",{\"session\":\"...\",\"isDemo\":1,\"uid\":0,\"platform\":1}]"
)

ssid = ssid.strip()

# Check if it's a complete SSID format
if ssid.startswith('42["auth",'):
self._parse_complete_ssid(ssid)
# Validate that we got a session ID
if not self.session_id or len(self.session_id) < 10:
raise InvalidParameterError(
f"Invalid SSID format - session ID is too short or missing. "
f"Please ensure your SSID is in the correct format: "
f"42[\"auth\",{{\"session\":\"your_session_id\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]. "
f"You can get this from browser DevTools (F12) -> Network tab -> WS filter -> "
f"look for authentication message starting with 42[\"auth\","
)
else:
# Treat as raw session ID
if len(ssid) < 10:
logger.warning(
f"Raw session ID appears to be too short ({len(ssid)} chars). "
f"If you're having connection issues, please use the complete SSID format: "
f"42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]"
)
self.session_id = ssid
self._complete_ssid = None

def _format_session_message(self) -> str:
"""Format session authentication message"""
# Always create auth message from components using constructor parameters
Expand Down Expand Up @@ -683,41 +712,80 @@ def _parse_complete_ssid(self, ssid: str) -> None:
data = json.loads(json_part)

self.session_id = data.get("session", "")
if not self.session_id:
raise InvalidParameterError(
"SSID is missing the 'session' field. "
"Expected format: 42[\"auth\",{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}]"
)

# Store original demo value from SSID, but don't override the constructor parameter
self._original_demo = bool(data.get("isDemo", 1))
# Keep the is_demo value from constructor - don't override it
self.uid = data.get("uid", 0)
self.platform = data.get("platform", 1)
# Don't store complete SSID - we'll reconstruct it with correct demo value
self._complete_ssid = None
else:
raise InvalidParameterError(
"Could not parse SSID - JSON object not found. "
"Expected format: 42[\"auth\",{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}]"
)
except json.JSONDecodeError as e:
raise InvalidParameterError(
f"Invalid SSID format - JSON parsing failed: {e}. "
f"Expected format: 42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]"
)
except InvalidParameterError:
raise # Re-raise our custom errors
except Exception as e:
logger.warning(f"Failed to parse SSID: {e}")
self.session_id = ssid
self._complete_ssid = None
raise InvalidParameterError(
f"Failed to parse SSID: {e}. "
f"Expected format: 42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]"
)
Comment on lines +738 to +744
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling logic is potentially confusing. After catching InvalidParameterError and re-raising it at line 738-739, the generic Exception handler at line 740-744 will never be reached if an InvalidParameterError is raised from within the try block (since it's caught earlier). Consider either removing the re-raise clause or restructuring the exception handling to avoid this dead code pattern.

Copilot uses AI. Check for mistakes.

async def _wait_for_authentication(self, timeout: float = 10.0) -> None:
"""Wait for authentication to complete (like old API)"""
auth_received = False
auth_error = None

def on_auth(data):
nonlocal auth_received
auth_received = True

def on_auth_error(data):
nonlocal auth_error
auth_error = data.get("message", "Unknown authentication error")

# Add temporary handler
# Add temporary handlers
self._websocket.add_event_handler("authenticated", on_auth)
self._websocket.add_event_handler("auth_error", on_auth_error)

try:
# Wait for authentication
start_time = time.time()
while not auth_received and (time.time() - start_time) < timeout:
while not auth_received and not auth_error and (time.time() - start_time) < timeout:
await asyncio.sleep(0.1)

if auth_error:
raise AuthenticationError(
f"Authentication failed: {auth_error}. "
f"Please verify your SSID is correct. "
f"SSID should be in format: 42[\"auth\",{{\"session\":\"your_session\",\"isDemo\":1,\"uid\":12345,\"platform\":1}}]. "
f"Get it from browser DevTools (F12) -> Network tab -> WS filter -> look for message starting with 42[\"auth\","
)

if not auth_received:
raise AuthenticationError("Authentication timeout")
raise AuthenticationError(
"Authentication timeout - server did not respond to authentication request. "
"This usually means your SSID is invalid or expired. "
"Please get a fresh SSID from browser DevTools (F12) -> Network tab -> WS filter -> "
"look for authentication message starting with 42[\"auth\",{\"session\":\"...\",..."
)

finally:
# Remove temporary handler
# Remove temporary handlers
self._websocket.remove_event_handler("authenticated", on_auth)
self._websocket.remove_event_handler("auth_error", on_auth_error)

async def _initialize_data(self) -> None:
"""Initialize client data after connection"""
Expand Down
14 changes: 10 additions & 4 deletions pocketoptionapi_async/websocket_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -582,8 +582,11 @@ async def _process_message(self, message) -> None:
await self._handle_json_message(data)

elif message.startswith("42") and "NotAuthorized" in message:
logger.error("Authentication failed: Invalid SSID")
await self._emit_event("auth_error", {"message": "Invalid SSID"})
logger.error(
"Authentication failed: Server rejected SSID. "
"Please verify your SSID is correct and not expired."
)
await self._emit_event("auth_error", {"message": "Invalid or expired SSID - Server returned NotAuthorized"})

except Exception as e:
logger.error(f"Error processing message: {e}")
Expand Down Expand Up @@ -611,8 +614,11 @@ async def _handle_json_message_wrapper(self, message: str) -> None:
async def _handle_auth_message(self, message: str) -> None:
"""Handle authentication message"""
if "NotAuthorized" in message:
logger.error("Authentication failed: Invalid SSID")
await self._emit_event("auth_error", {"message": "Invalid SSID"})
logger.error(
"Authentication failed: Server rejected SSID. "
"Please verify your SSID is correct and not expired."
)
await self._emit_event("auth_error", {"message": "Invalid or expired SSID - Server returned NotAuthorized"})

async def _process_message_optimized(self, message) -> None:
"""
Expand Down
Loading