A multi-user server for Jupyter notebooks that provides authentication, spawning, and proxying for multiple users simultaneously
—
JupyterHub provides a comprehensive REST API for external integration and programmatic management of users, servers, groups, services, and system configuration. The API supports JSON requests/responses and uses token-based authentication.
Foundation class for all REST API endpoints in JupyterHub.
class APIHandler(BaseHandler):
"""
Base class for REST API handlers.
Provides JSON request/response handling, authentication,
and common API functionality.
"""
def get_current_user(self):
"""
Get the current authenticated user.
Returns:
User object if authenticated, None otherwise
"""
def get_current_user_token(self):
"""
Get the current API token.
Returns:
APIToken object if token auth, None otherwise
"""
def check_xsrf_cookie(self):
"""Check XSRF cookie for API requests"""
def get_json_body(self):
"""
Parse JSON request body.
Returns:
Parsed JSON data as Python object
Raises:
HTTPError: If JSON is invalid
"""
def write_error(self, status_code, **kwargs):
"""
Write JSON error response.
Args:
status_code: HTTP status code
**kwargs: Additional error context
"""REST endpoints for managing JupyterHub users and their servers.
# GET /api/users
# List all users
def get_users() -> List[Dict[str, Any]]:
"""
Get list of all users.
Returns:
List of user objects with basic information
"""
# GET /api/users/{name}
def get_user(name: str) -> Dict[str, Any]:
"""
Get information about a specific user.
Args:
name: Username
Returns:
User object with servers, groups, and roles
"""
# POST /api/users/{name}
def create_user(name: str, admin: bool = False) -> Dict[str, Any]:
"""
Create a new user.
Args:
name: Username
admin: Whether user should be admin
Returns:
Created user object
"""
# PATCH /api/users/{name}
def modify_user(name: str, admin: bool = None) -> Dict[str, Any]:
"""
Modify user properties.
Args:
name: Username
admin: Admin status to set
Returns:
Updated user object
"""
# DELETE /api/users/{name}
def delete_user(name: str) -> None:
"""
Delete a user and all their servers.
Args:
name: Username to delete
"""
# POST /api/users/{name}/server
def start_user_server(name: str, server_name: str = '') -> Dict[str, Any]:
"""
Start a user's server.
Args:
name: Username
server_name: Named server (empty for default)
Returns:
Server start result
"""
# DELETE /api/users/{name}/server
def stop_user_server(name: str, server_name: str = '') -> None:
"""
Stop a user's server.
Args:
name: Username
server_name: Named server (empty for default)
"""
# GET /api/users/{name}/tokens
def get_user_tokens(name: str) -> List[Dict[str, Any]]:
"""
Get user's API tokens.
Args:
name: Username
Returns:
List of token information (without actual token values)
"""
# POST /api/users/{name}/tokens
def create_user_token(name: str, expires_in: int = None, note: str = None) -> Dict[str, Any]:
"""
Create API token for user.
Args:
name: Username
expires_in: Token lifetime in seconds
note: Token description
Returns:
Token information with actual token value
"""REST endpoints for managing user groups and group membership.
# GET /api/groups
def get_groups() -> List[Dict[str, Any]]:
"""
Get list of all groups.
Returns:
List of group objects with member information
"""
# GET /api/groups/{name}
def get_group(name: str) -> Dict[str, Any]:
"""
Get information about a specific group.
Args:
name: Group name
Returns:
Group object with users and roles
"""
# POST /api/groups/{name}
def create_group(name: str, users: List[str] = None) -> Dict[str, Any]:
"""
Create a new group.
Args:
name: Group name
users: Initial list of usernames
Returns:
Created group object
"""
# POST /api/groups/{name}/users
def add_group_users(name: str, users: List[str]) -> Dict[str, Any]:
"""
Add users to a group.
Args:
name: Group name
users: List of usernames to add
Returns:
Updated group object
"""
# DELETE /api/groups/{name}/users
def remove_group_users(name: str, users: List[str]) -> Dict[str, Any]:
"""
Remove users from a group.
Args:
name: Group name
users: List of usernames to remove
Returns:
Updated group object
"""
# DELETE /api/groups/{name}
def delete_group(name: str) -> None:
"""
Delete a group.
Args:
name: Group name to delete
"""REST endpoints for managing external services integrated with JupyterHub.
# GET /api/services
def get_services() -> List[Dict[str, Any]]:
"""
Get list of all services.
Returns:
List of service objects
"""
# GET /api/services/{name}
def get_service(name: str) -> Dict[str, Any]:
"""
Get information about a specific service.
Args:
name: Service name
Returns:
Service object with configuration and status
"""
# POST /api/services/{name}
def create_service(name: str, url: str, admin: bool = False) -> Dict[str, Any]:
"""
Register a new service.
Args:
name: Service name
url: Service URL
admin: Whether service has admin privileges
Returns:
Created service object
"""
# DELETE /api/services/{name}
def delete_service(name: str) -> None:
"""
Unregister a service.
Args:
name: Service name to delete
"""REST endpoints for the server sharing functionality.
# GET /api/shares/{code}
def get_share(code: str) -> Dict[str, Any]:
"""
Get information about a server share.
Args:
code: Share access code
Returns:
Share information and server details
"""
# POST /api/shares
def create_share(server_name: str = '', expires_in: int = None) -> Dict[str, Any]:
"""
Create a server share.
Args:
server_name: Server to share (empty for default)
expires_in: Share lifetime in seconds
Returns:
Share code and access information
"""
# POST /api/shares/{code}/accept
def accept_share(code: str) -> Dict[str, Any]:
"""
Accept a server share invitation.
Args:
code: Share access code
Returns:
Server access information
"""REST endpoints for managing the JupyterHub system itself.
# GET /api/hub/info
def get_hub_info() -> Dict[str, Any]:
"""
Get JupyterHub version and configuration info.
Returns:
Hub version, Python version, and basic config
"""
# POST /api/hub/shutdown
def shutdown_hub() -> Dict[str, Any]:
"""
Shutdown the JupyterHub system.
Returns:
Shutdown confirmation
"""
# GET /api/info
def get_info() -> Dict[str, Any]:
"""
Get general system information.
Returns:
JupyterHub version and basic system info
"""REST endpoints for managing the HTTP proxy component.
# GET /api/proxy
def get_proxy_routes() -> Dict[str, Dict[str, Any]]:
"""
Get current proxy routing table.
Returns:
Dictionary of routes with target information
"""
# POST /api/proxy
def add_proxy_route(path: str, target: str) -> None:
"""
Add a route to the proxy.
Args:
path: URL path to route
target: Target URL to proxy to
"""
# DELETE /api/proxy/{path}
def delete_proxy_route(path: str) -> None:
"""
Remove a route from the proxy.
Args:
path: URL path to remove
"""import requests
# Token-based authentication
headers = {
'Authorization': f'Bearer {api_token}',
'Content-Type': 'application/json'
}
# Make authenticated request
response = requests.get(
'http://jupyterhub.example.com/api/users',
headers=headers
)
users = response.json()# Create a new user
response = requests.post(
'http://jupyterhub.example.com/api/users/alice',
headers=headers,
json={'admin': False}
)
user = response.json()
# Start user's server
response = requests.post(
'http://jupyterhub.example.com/api/users/alice/server',
headers=headers
)
# Get user information
response = requests.get(
'http://jupyterhub.example.com/api/users/alice',
headers=headers
)
user_info = response.json()# Create group with initial users
response = requests.post(
'http://jupyterhub.example.com/api/groups/students',
headers=headers,
json={'users': ['alice', 'bob', 'charlie']}
)
# Add users to existing group
response = requests.post(
'http://jupyterhub.example.com/api/groups/students/users',
headers=headers,
json={'users': ['david', 'eve']}
)# Register external service
response = requests.post(
'http://jupyterhub.example.com/api/services/my-service',
headers=headers,
json={
'url': 'http://localhost:8001',
'admin': False
}
)
# Get service status
response = requests.get(
'http://jupyterhub.example.com/api/services/my-service',
headers=headers
)
service_info = response.json()from jupyterhub.services.auth import HubAuth
# Initialize Hub authentication
auth = HubAuth(
api_token='your-api-token',
api_url='http://jupyterhub.example.com/hub/api'
)
# Make authenticated requests
async def get_users():
"""Get all users via API"""
response = await auth.api_request('GET', '/users')
return response.json()
async def start_server(username):
"""Start user's server"""
response = await auth.api_request(
'POST',
f'/users/{username}/server'
)
return response.json(){
"name": "alice",
"admin": false,
"groups": ["students"],
"server": "/user/alice/",
"pending": null,
"created": "2024-01-01T00:00:00.000000Z",
"last_activity": "2024-01-02T12:30:00.000000Z",
"servers": {
"": {
"name": "",
"last_activity": "2024-01-02T12:30:00.000000Z",
"started": "2024-01-02T12:00:00.000000Z",
"pending": null,
"ready": true,
"state": {},
"url": "/user/alice/",
"user_options": {},
"progress_url": "/hub/api/users/alice/server/progress"
}
}
}{
"name": "students",
"users": ["alice", "bob", "charlie"],
"roles": ["student-role"]
}{
"name": "announcement-service",
"admin": false,
"url": "http://localhost:8001",
"prefix": "/services/announcement-service",
"pid": 12345,
"managed": true,
"info": {
"status": "running"
}
}# API error response format
{
"status": 400,
"message": "Bad Request: Invalid user data"
}
# Common HTTP status codes
# 200 - Success
# 201 - Created
# 204 - No Content (successful deletion)
# 400 - Bad Request
# 401 - Unauthorized
# 403 - Forbidden
# 404 - Not Found
# 409 - Conflict (resource already exists)
# 500 - Internal Server ErrorInstall with Tessl CLI
npx tessl i tessl/pypi-jupyterhub