Last Updated: 2025-10-28
This document covers all authentication methods for the Bambu Lab Cloud API, including the new login flow with two-factor authentication.
# Interactive login (recommended)
python cli_tools/login.py
# Non-interactive login
python cli_tools/login.py --username user@email.com --password yourpass
# China region
python cli_tools/login.py --region china --username user@email.com
# Verify existing token
python cli_tools/login.py --verify-only
# Test token by fetching profile
python cli_tools/login.py --testfrom bambulab import BambuAuthenticator, BambuClient
# Authenticate and get token
auth = BambuAuthenticator(region="global")
token = auth.login(
username="your-email@example.com",
password="your-password"
)
# Token is automatically saved to ~/.bambu_token
# Use with API client
client = BambuClient(token=token)
devices = client.get_devices()Bambu Lab enforces two-factor authentication via email verification for all users.
- Initial Login: Submit email and password
- Verification Code: Bambu Lab sends 6-digit code to your email
- Code Verification: Enter code to complete authentication
- Token Received: Get access token for API calls
from bambulab import BambuAuthenticator, BambuAuthError
auth = BambuAuthenticator()
try:
# Method 1: Interactive (prompts for code)
token = auth.login("user@email.com", "password")
# Method 2: Custom callback
def get_code():
return input("Enter code from email: ")
token = auth.login("user@email.com", "password", get_code)
# Method 3: Use saved token if valid, login if needed
token = auth.get_or_create_token(
username="user@email.com",
password="password"
)
print(f"Logged in! Token: {token[:20]}...")
except BambuAuthError as e:
print(f"Authentication failed: {e}")Tokens are automatically saved to ~/.bambu_token with secure permissions (0600):
{
"region": "global",
"token": "eyJhbGc..."
}Custom token file location:
auth = BambuAuthenticator(token_file="/path/to/token.json")Endpoint: POST /v1/user-service/user/login
Request:
{
"account": "user@email.com",
"password": "your_password",
"apiError": ""
}Response (Success):
{
"success": true,
"accessToken": "eyJhbGc..."
}Response (2FA Required):
{
"success": false,
"loginType": "verifyCode"
}Response (MFA Required):
{
"success": false,
"loginType": "tfa",
"tfaKey": "..."
}Endpoint: POST /v1/user-service/user/sendemail/code
Request:
{
"email": "user@email.com",
"type": "codeLogin"
}Response:
{
"success": true,
"message": "Verification code sent"
}Endpoint: POST /v1/user-service/user/login
Request:
{
"account": "user@email.com",
"code": "123456"
}Response:
{
"success": true,
"accessToken": "eyJhbGc..."
}Endpoint: POST /api/sign-in/tfa
Request:
{
"tfaKey": "...",
"tfaCode": "123456"
}class BambuAuthenticator:
def __init__(
self,
region: str = "global",
token_file: Optional[str] = None
)Parameters:
region: API region - "global" (default) or "china"token_file: Path to save tokens (default:~/.bambu_token)
Perform login with 2FA support.
def login(
self,
username: str,
password: str,
code_callback: Optional[Callable[[], str]] = None
) -> strParameters:
username: Bambu Lab account emailpassword: Account passwordcode_callback: Optional function to get verification code
Returns: Access token string
Raises: BambuAuthError on failure
Get existing token or login if needed.
def get_or_create_token(
self,
username: Optional[str] = None,
password: Optional[str] = None,
code_callback: Optional[Callable[[], str]] = None,
force_new: bool = False
) -> strParameters:
username: Account email (required if no saved token)password: Account password (required if no saved token)code_callback: Optional code input functionforce_new: Force new login even if token exists
Returns: Valid access token
Save token to file.
def save_token(self, token: str) -> NoneLoad saved token from file.
def load_token(self) -> Optional[str]Returns: Token string or None
Check if token is valid.
def verify_token(self, token: str) -> boolReturns: True if valid, False otherwise
def fetch_code_from_email():
"""Fetch code from email automatically"""
# Example: Use IMAP to read email
import imaplib
import re
# Connect to email
mail = imaplib.IMAP4_SSL('imap.gmail.com')
mail.login('user@email.com', 'app_password')
mail.select('INBOX')
# Search for Bambu Lab email
_, messages = mail.search(None, 'FROM "noreply@bambulab.com"')
latest = messages[0].split()[-1]
# Fetch and parse
_, msg = mail.fetch(latest, '(RFC822)')
email_body = msg[0][1].decode()
# Extract 6-digit code
match = re.search(r'\b\d{6}\b', email_body)
return match.group(0) if match else None
# Use with authenticator
auth = BambuAuthenticator()
token = auth.login("user@email.com", "password", fetch_code_from_email)import tkinter as tk
from tkinter import messagebox
def get_code_via_gui():
"""Show GUI dialog for code input"""
root = tk.Tk()
root.withdraw()
code = tk.simpledialog.askstring(
"Verification Code",
"Enter the 6-digit code from your email:"
)
return code
auth = BambuAuthenticator()
token = auth.login("user@email.com", "password", get_code_via_gui)from bambulab import BambuAuthenticator, BambuAuthError
import time
def login_with_retry(username, password, max_retries=3):
"""Login with retry logic"""
auth = BambuAuthenticator()
for attempt in range(max_retries):
try:
token = auth.login(username, password)
return token
except BambuAuthError as e:
if attempt < max_retries - 1:
print(f"Attempt {attempt + 1} failed: {e}")
print("Retrying in 5 seconds...")
time.sleep(5)
else:
raise
return None
token = login_with_retry("user@email.com", "password")Global (International):
Main API: https://api.bambulab.com
Portal: https://e.bambulab.com
China Region:
Main API: https://api.bambulab.cn
Portal: https://e.bambulab.cn
Dev API: https://api-dev.bambulab.net
QA API: https://api-qa.bambulab.net
Pre-prod: https://api-pre.bambulab.net
All authenticated API requests require these headers:
Authorization: Bearer <your_jwt_token>
Content-Type: application/json
x-bbl-app-certification-id: <cert_id>
x-bbl-device-security-sign: <signed_timestamp>| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer token obtained from login |
Content-Type |
Yes | Always application/json for API requests |
x-bbl-app-certification-id |
Optional | Device certificate ID for enhanced security |
x-bbl-device-security-sign |
Optional | RSA signature of current timestamp |
The Bambu Connect app uses RSA certificate-based signing for enhanced security:
- App has a device certificate with RSA key pair
- Signs current timestamp with private key
- Sends signature in
x-bbl-device-security-signheader - Sends certificate ID in
x-bbl-app-certification-idheader - Server verifies signature with public key from certificate
- Algorithm: RSA 2048-bit or higher
- Signature Algorithm: SHA-256
- Certificate Storage: Secure local storage in app
import time
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
def sign_timestamp(private_key_pem):
timestamp = str(int(time.time() * 1000))
# Load private key
private_key = serialization.load_pem_private_key(
private_key_pem.encode(),
password=None
)
# Sign timestamp
signature = private_key.sign(
timestamp.encode(),
padding.PKCS1v15(),
hashes.SHA256()
)
# Return base64 encoded signature
import base64
return base64.b64encode(signature).decode()
# Usage
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
"x-bbl-app-certification-id": cert_id,
"x-bbl-device-security-sign": sign_timestamp(private_key)
}Bambu Lab uses JWT (JSON Web Tokens) for authentication.
{
"header": {
"alg": "RS256",
"typ": "JWT"
},
"payload": {
"user_id": "012345678",
"email": "0123@01234567890",
"permissions": ["device:read", "device:write", "files:upload"],
"exp": 1761435401,
"iat": 1761349001
},
"signature": "..."
}- Algorithm: RS256 (RSA with SHA-256)
- Expiration: Typically 24 hours
- Refresh: Must obtain new token after expiration
- Scope: Contains user_id and permissions
Note: The login endpoint is not publicly documented. You must obtain a token through:
- Official Bambu Connect app
- Bambu Handy mobile app
- Web portal login flow
import requests
headers = {
"Authorization": f"Bearer {your_token}",
"Content-Type": "application/json"
}
response = requests.get(
"https://api.bambulab.com/v1/iot-service/api/user/bind",
headers=headers
)- Store tokens securely (encrypted storage, keychain, etc.)
- Never commit tokens to version control
- Use environment variables for development
- Implement token refresh before expiration
- Keep private keys secure and never expose them
- Rotate certificates periodically
- Use hardware security modules (HSM) for production
- Implement certificate revocation checking
- Always use HTTPS
- Validate SSL certificates
- Implement certificate pinning for production apps
- Use secure network connections (avoid public WiFi)
def make_authenticated_request(url, token):
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
try:
response = requests.get(url, headers=headers, timeout=30)
if response.status_code == 401:
print("Authentication failed - token expired or invalid")
# Implement token refresh logic here
return None
elif response.status_code == 403:
print("Access forbidden - insufficient permissions")
return None
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
print(f"Request failed: {e}")
return NoneCauses:
- Expired token
- Invalid token
- Missing Authorization header
- Malformed Bearer token
Solution:
- Obtain a new token
- Verify token format:
Bearer <token> - Check token expiration time
Causes:
- Insufficient permissions
- Token valid but user lacks access
- Device not bound to user account
Solution:
- Verify user permissions
- Ensure device is bound to your account
- Check if feature requires premium subscription
Causes:
- Missing required headers
- Invalid certificate signature
- Malformed request
Solution:
- Verify all required headers are present
- Check certificate signing implementation
- Validate request body format
Complete example showing authentication workflow:
import requests
import os
class BambuAuth:
def __init__(self, token, region="global"):
self.token = token
self.base_url = (
"https://api.bambulab.com" if region == "global"
else "https://api.bambulab.cn"
)
def get_headers(self):
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
def test_auth(self):
"""Test if authentication is working"""
url = f"{self.base_url}/v1/user-service/my/profile"
response = requests.get(url, headers=self.get_headers())
if response.status_code == 200:
print("Authentication successful")
return True
else:
print(f"Authentication failed: {response.status_code}")
return False
def get_devices(self):
"""Get list of bound devices"""
url = f"{self.base_url}/v1/iot-service/api/user/bind"
response = requests.get(url, headers=self.get_headers())
response.raise_for_status()
return response.json()
# Usage
token = os.getenv("BAMBU_TOKEN")
auth = BambuAuth(token)
if auth.test_auth():
devices = auth.get_devices()
print(f"Found {len(devices.get('devices', []))} devices")Authentication endpoints have specific rate limits:
- Login attempts: 5 per minute
- Token refresh: 10 per hour
- Profile access: 60 per minute
- Device queries: 120 per minute
Exceeding rate limits returns 429 Too Many Requests.
def debug_auth(token, base_url):
import jwt
import datetime
# Decode token (without verification for debugging)
try:
decoded = jwt.decode(token, options={"verify_signature": False})
print("Token Payload:")
print(f" User ID: {decoded.get('user_id')}")
print(f" Issued: {datetime.datetime.fromtimestamp(decoded.get('iat', 0))}")
print(f" Expires: {datetime.datetime.fromtimestamp(decoded.get('exp', 0))}")
# Check if expired
if decoded.get('exp', 0) < time.time():
print(" Status: EXPIRED")
else:
print(" Status: VALID")
except Exception as e:
print(f"Token decode error: {e}")
# Test connection
response = requests.get(
f"{base_url}/v1/user-service/my/profile",
headers={"Authorization": f"Bearer {token}"}
)
print(f"\nAPI Response: {response.status_code}")
if response.status_code != 200:
print(f"Error: {response.text}")-
Token won't work across regions
- Global and China tokens are separate
- Must use matching API endpoint
-
Certificate signature fails
- Check timestamp is current (within 5 minutes)
- Verify private key matches certificate
- Ensure correct signature algorithm (SHA-256)
-
Intermittent auth failures
- Check network connectivity
- Verify system time is accurate
- Implement retry logic with exponential backoff
- API_DEVICES.md - Device management endpoints
- API_USERS.md - User profile endpoints
- API_REFERENCE.md - Error codes and responses