Admin API: Complete Enterprise Management Guide for Organizations / Members / Workspaces / API Keys
Chapter 64: Admin API: Organization Management, User Permissions, and API Key Lifecycle
64.1 The Admin API's Role and Use Cases
Anthropic's Admin API is a set of REST endpoints for enterprise administrators, enabling programmatic management of Claude usage resources: organization settings, user accounts, Workspace allocation, API key lifecycle, and usage queries.
Compared to manual management through the Anthropic Console, the Admin API is indispensable in the following scenarios:
- Large-scale user management: Automate onboarding/offboarding workflows, batch-create or revoke API keys
- Multi-tenant SaaS architecture: Dynamically provision independent API keys and usage quotas for each customer
- Compliance and audit: Programmatically retrieve API usage records and generate compliance reports
- Cost control: Set API usage limits for different teams or projects and alert automatically when exceeded
- Security response: Immediately revoke API keys when anomalies are detected
The Admin API uses a dedicated Admin Key (sk-ant-admin-...), which must be stored and managed separately from regular API keys.
64.2 Authentication and Basic Configuration
64.2.1 Obtaining an Admin Key
Admin Keys can only be created by users with the Owner role in the Anthropic Console:
- Log in to https://console.anthropic.com
- Go to Settings → Admin Keys
- Click Create Admin Key, set a name and expiry
- Save the generated
sk-ant-admin-...key (shown only once)
64.2.2 Basic Request Structure
All Admin API requests use the same authentication headers:
import httpx
import os
from typing import Optional
ADMIN_BASE_URL = "https://api.anthropic.com/v1"
class AnthropicAdminClient:
"""Anthropic Admin API client"""
def __init__(self, admin_key: Optional[str] = None):
self.admin_key = admin_key or os.environ["ANTHROPIC_ADMIN_KEY"]
self.base_url = ADMIN_BASE_URL
self.headers = {
"x-api-key": self.admin_key,
"anthropic-version": "2023-06-01",
"content-type": "application/json"
}
self.client = httpx.Client(timeout=30)
def get(self, path: str, params: dict = None) -> dict:
response = self.client.get(
f"{self.base_url}{path}",
headers=self.headers,
params=params
)
response.raise_for_status()
return response.json()
def post(self, path: str, body: dict) -> dict:
response = self.client.post(
f"{self.base_url}{path}",
headers=self.headers,
json=body
)
response.raise_for_status()
return response.json()
def delete(self, path: str) -> dict:
response = self.client.delete(
f"{self.base_url}{path}",
headers=self.headers
)
response.raise_for_status()
return response.json()
64.3 Organization Management (/v1/organizations)
64.3.1 Get Organization Information
admin = AnthropicAdminClient()
# GET /v1/organizations
org_info = admin.get("/organizations")
Request:
GET https://api.anthropic.com/v1/organizations
x-api-key: sk-ant-admin-...
anthropic-version: 2023-06-01
Response example:
{
"id": "org_01XXXXXXXXXXXXXXXXXX",
"name": "Acme Corporation",
"created_at": "2024-01-15T08:00:00Z",
"billing": {
"plan": "enterprise",
"usage_limit_usd": 10000.00,
"current_month_usage_usd": 2345.67
},
"settings": {
"default_model": "claude-opus-4-5",
"require_mfa": true,
"allowed_ip_ranges": ["203.0.113.0/24", "198.51.100.0/24"]
}
}
64.3.2 Update Organization Settings
# PATCH /v1/organizations
updated = admin.post("/organizations", {
"settings": {
"require_mfa": True,
"allowed_ip_ranges": ["203.0.113.0/24"]
}
})
64.4 User Management (/v1/users)
64.4.1 List Organization Members
# GET /v1/users - paginated list of all users
def list_all_users(admin: AnthropicAdminClient) -> list[dict]:
"""Get all users in the organization (handles pagination)"""
all_users = []
after_cursor = None
while True:
params = {"limit": 100}
if after_cursor:
params["after_id"] = after_cursor
response = admin.get("/users", params=params)
users = response.get("data", [])
all_users.extend(users)
if not response.get("has_more", False):
break
after_cursor = users[-1]["id"] if users else None
return all_users
GET /v1/users response example:
{
"data": [
{
"id": "user_01XXXXXXXXXXXXXXXXXX",
"email": "[email protected]",
"name": "Alice Johnson",
"role": "developer",
"created_at": "2024-03-01T10:00:00Z",
"last_active_at": "2025-04-27T15:30:00Z",
"status": "active"
},
{
"id": "user_02XXXXXXXXXXXXXXXXXX",
"email": "[email protected]",
"name": "Bob Smith",
"role": "admin",
"created_at": "2024-01-20T09:00:00Z",
"last_active_at": "2025-04-28T08:00:00Z",
"status": "active"
}
],
"has_more": true,
"first_id": "user_01XXXXXXXXXXXXXXXXXX",
"last_id": "user_02XXXXXXXXXXXXXXXXXX"
}
64.4.2 Invite a New User
# POST /v1/users/invite
def invite_user(admin: AnthropicAdminClient, email: str, role: str = "developer") -> dict:
"""
Invite a user to join the organization.
role: owner | admin | developer | billing
"""
return admin.post("/users/invite", {"email": email, "role": role})
Request body:
{
"email": "[email protected]",
"role": "developer"
}
Response example:
{
"id": "invite_01XXXXXXXXXXXXXXXXXX",
"email": "[email protected]",
"role": "developer",
"status": "pending",
"invited_at": "2025-04-28T10:00:00Z",
"expires_at": "2025-05-05T10:00:00Z"
}
64.4.3 Update User Role
# PATCH /v1/users/{user_id}
def update_user_role(admin: AnthropicAdminClient, user_id: str, new_role: str) -> dict:
response = admin.client.patch(
f"{admin.base_url}/users/{user_id}",
headers=admin.headers,
json={"role": new_role}
)
response.raise_for_status()
return response.json()
PATCH /v1/users/{user_id} response:
{
"id": "user_01XXXXXXXXXXXXXXXXXX",
"email": "[email protected]",
"name": "Alice Johnson",
"role": "admin",
"updated_at": "2025-04-28T10:05:00Z"
}
64.4.4 Remove a User
# DELETE /v1/users/{user_id}
def remove_user(admin: AnthropicAdminClient, user_id: str) -> dict:
"""Remove a user from the organization (also revokes all their API keys)"""
return admin.delete(f"/users/{user_id}")
DELETE response:
{
"id": "user_01XXXXXXXXXXXXXXXXXX",
"deleted": true
}
64.5 API Key Lifecycle Management (/v1/api_keys)
API keys are the core credentials for Claude usage. Their lifecycle management is one of the most important Admin API functions.
64.5.1 List API Keys
# GET /v1/api_keys
def list_api_keys(admin: AnthropicAdminClient, workspace_id: Optional[str] = None) -> list[dict]:
"""List all API keys (optionally filtered by workspace)"""
params = {}
if workspace_id:
params["workspace_id"] = workspace_id
return admin.get("/api_keys", params=params).get("data", [])
GET /v1/api_keys response example:
{
"data": [
{
"id": "apikey_01XXXXXXXXXXXXXXXXXX",
"name": "Production Backend",
"created_at": "2024-06-01T00:00:00Z",
"last_used_at": "2025-04-28T09:45:00Z",
"status": "active",
"workspace_id": "workspace_01XXXXXXXXXXXXXXXX",
"created_by": {
"id": "user_01XXXXXXXXXXXXXXXXXX",
"email": "[email protected]"
},
"partial_key": "sk-ant-api03-...ABCD"
}
],
"has_more": false
}
64.5.2 Create a New API Key
# POST /v1/api_keys
def create_api_key(
admin: AnthropicAdminClient,
name: str,
workspace_id: str,
expires_at: Optional[str] = None
) -> dict:
"""
Create a new API key.
IMPORTANT: The full key is returned only once — save it immediately.
"""
body = {"name": name, "workspace_id": workspace_id}
if expires_at:
body["expires_at"] = expires_at # ISO 8601 format
return admin.post("/api_keys", body)
POST /v1/api_keys request body:
{
"name": "Customer Portal Service",
"workspace_id": "workspace_01XXXXXXXXXXXXXXXX",
"expires_at": "2026-04-28T00:00:00Z"
}
Response (full key shown only this once):
{
"id": "apikey_03XXXXXXXXXXXXXXXXXX",
"name": "Customer Portal Service",
"key": "sk-ant-api03-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
"workspace_id": "workspace_01XXXXXXXXXXXXXXXX",
"created_at": "2025-04-28T10:10:00Z",
"expires_at": "2026-04-28T00:00:00Z",
"status": "active"
}
64.5.3 Revoke an API Key
# DELETE /v1/api_keys/{api_key_id}
def revoke_api_key(admin: AnthropicAdminClient, api_key_id: str) -> dict:
"""Immediately revoke an API key (all requests using this key will fail instantly)"""
return admin.delete(f"/api_keys/{api_key_id}")
DELETE response:
{
"id": "apikey_01XXXXXXXXXXXXXXXXXX",
"deleted": true
}
64.6 Workspace Management (/v1/workspaces)
Workspaces are resource isolation units within an organization, allowing different teams to use independent API key pools and usage quotas.
64.6.1 List Workspaces
def list_workspaces(admin: AnthropicAdminClient) -> list[dict]:
return admin.get("/workspaces").get("data", [])
Response example:
{
"data": [
{
"id": "workspace_01XXXXXXXXXXXXXXXX",
"name": "Production",
"created_at": "2024-01-15T08:00:00Z",
"usage_limits": {
"monthly_spend_usd": 5000.00,
"requests_per_minute": 1000
}
},
{
"id": "workspace_02XXXXXXXXXXXXXXXX",
"name": "Development",
"created_at": "2024-01-15T08:00:00Z",
"usage_limits": {
"monthly_spend_usd": 500.00,
"requests_per_minute": 100
}
}
]
}
64.6.2 Create a Workspace
# POST /v1/workspaces
def create_workspace(
admin: AnthropicAdminClient,
name: str,
monthly_spend_limit: float,
rpm_limit: int = 100
) -> dict:
"""Create a new workspace with usage limits"""
return admin.post("/workspaces", {
"name": name,
"usage_limits": {
"monthly_spend_usd": monthly_spend_limit,
"requests_per_minute": rpm_limit
}
})
64.7 Usage Queries and Cost Monitoring
64.7.1 Get Usage Data
# GET /v1/usage
def get_usage(
admin: AnthropicAdminClient,
start_date: str, # YYYY-MM-DD
end_date: str,
workspace_id: Optional[str] = None,
granularity: str = "day" # hour | day | month
) -> dict:
params = {"start_date": start_date, "end_date": end_date, "granularity": granularity}
if workspace_id:
params["workspace_id"] = workspace_id
return admin.get("/usage", params=params)
GET /v1/usage response example:
{
"data": [
{
"date": "2025-04-27",
"input_tokens": 12345678,
"output_tokens": 3456789,
"cache_read_tokens": 987654,
"total_cost_usd": 456.78,
"requests": 23456,
"model_breakdown": {
"claude-opus-4-5": {
"input_tokens": 5000000,
"output_tokens": 2000000,
"cost_usd": 380.00
},
"claude-haiku-4-5": {
"input_tokens": 7345678,
"output_tokens": 1456789,
"cost_usd": 76.78
}
}
}
],
"total": {
"input_tokens": 12345678,
"output_tokens": 3456789,
"total_cost_usd": 456.78
}
}
64.7.2 Automated Cost Alerting
from datetime import datetime
class CostAlertSystem:
"""Automated cost alerting system based on the Admin API"""
def __init__(self, admin: AnthropicAdminClient, alert_threshold_usd: float):
self.admin = admin
self.threshold = alert_threshold_usd
def check_and_alert(self) -> bool:
"""Check current month usage; alert if threshold exceeded"""
today = datetime.now()
month_start = today.replace(day=1).strftime("%Y-%m-%d")
today_str = today.strftime("%Y-%m-%d")
usage = self.admin.get("/usage", params={
"start_date": month_start,
"end_date": today_str,
"granularity": "month"
})
total_cost = usage.get("total", {}).get("total_cost_usd", 0)
if total_cost > self.threshold:
self._send_alert(total_cost)
return True
return False
def _send_alert(self, current_cost: float):
alert_message = f"""
[COST ALERT] Anthropic API Usage Exceeds Budget
Current month usage: ${current_cost:.2f} USD
Alert threshold: ${self.threshold:.2f} USD
Overage: {((current_cost / self.threshold - 1) * 100):.1f}%
Log in to Anthropic Console for details. Consider:
1. Check for anomalous API calls
2. Adjust Workspace usage limits
3. Optimize model selection (use Haiku instead of Opus for high-frequency calls)
"""
print(alert_message)
# In production: send via email, Slack, PagerDuty, etc.
64.8 Automated Onboarding and Offboarding
Combining multiple Admin API endpoints into a complete user lifecycle automation:
class UserLifecycleManager:
"""Automated user onboarding/offboarding management"""
def __init__(self, admin: AnthropicAdminClient):
self.admin = admin
def onboard_developer(
self, email: str, name: str, team: str, workspace_id: str
) -> dict:
"""
Developer onboarding:
1. Invite user to organization
2. Create a dedicated API key for the user
3. Return credentials for secure delivery
"""
invite = self.admin.post("/users/invite", {
"email": email, "role": "developer"
})
from datetime import datetime, timedelta
expires = (datetime.now() + timedelta(days=365)).isoformat() + "Z"
api_key = create_api_key(
self.admin,
name=f"{name} - {team}",
workspace_id=workspace_id,
expires_at=expires
)
return {
"invite_id": invite["id"],
"invite_email": email,
"api_key_id": api_key["id"],
"api_key": api_key["key"], # One-time display — save and deliver securely
"workspace_id": workspace_id,
"expires_at": expires
}
def offboard_developer(self, user_id: str) -> dict:
"""
Developer offboarding:
1. Revoke all API keys belonging to this user
2. Remove user from organization
"""
revoked_keys = []
all_keys = list_api_keys(self.admin)
user_keys = [k for k in all_keys if k.get("created_by", {}).get("id") == user_id]
for key in user_keys:
revoke_api_key(self.admin, key["id"])
revoked_keys.append(key["id"])
removal = self.admin.delete(f"/users/{user_id}")
return {
"user_id": user_id,
"deleted": removal.get("deleted", False),
"revoked_keys": revoked_keys,
"revoked_count": len(revoked_keys)
}
64.9 Security Best Practices
64.9.1 Protecting Admin Keys
# Retrieve Admin Key from AWS Secrets Manager
import boto3
import json
def get_admin_key_from_secrets_manager(secret_name: str, region: str = "us-east-1") -> str:
client = boto3.client("secretsmanager", region_name=region)
response = client.get_secret_value(SecretId=secret_name)
secret = json.loads(response["SecretString"])
return secret["anthropic_admin_key"]
# Environment variable injection (recommended for CI/CD)
# export ANTHROPIC_ADMIN_KEY=$(aws secretsmanager get-secret-value ...)
64.9.2 Operation Audit Logging
import logging
import functools
import time
logger = logging.getLogger("anthropic_admin_audit")
def audit_log(action: str):
"""Decorator for auditing Admin API operations"""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
start = time.time()
error = None
try:
result = func(*args, **kwargs)
return result
except Exception as e:
error = str(e)
raise
finally:
logger.info({
"action": action,
"timestamp": time.time(),
"elapsed_ms": int((time.time() - start) * 1000),
"args": str(args[1:])[:200],
"success": error is None,
"error": error
})
return wrapper
return decorator
@audit_log("invite_user")
def invite_user_audited(admin: AnthropicAdminClient, email: str, role: str) -> dict:
return admin.post("/users/invite", {"email": email, "role": role})
@audit_log("revoke_api_key")
def revoke_api_key_audited(admin: AnthropicAdminClient, api_key_id: str) -> dict:
return admin.delete(f"/api_keys/{api_key_id}")
Summary
The Admin API gives enterprises complete programmatic control over Anthropic Claude usage resources. Core endpoints include: /v1/organizations (organization configuration), /v1/users (invite/remove/role changes), /v1/api_keys (key creation/listing/revocation), /v1/workspaces (resource isolation), and /v1/usage (usage and cost monitoring). Best practices are: store Admin Keys in Secrets Manager rather than code, record all operations in audit logs, set independent quotas for different teams via Workspaces, and build automated cost alert systems on top of /v1/usage. User lifecycle management—creating keys on onboarding, batch-revoking on offboarding—is the most typical automation scenario, significantly reducing manual management overhead and eliminating the security risk of lingering keys after departure.