Introduction
This guide shows you how to query available GPU capacity on Crusoe Cloud using the REST API. Checking capacity programmatically allows you to automate resource planning, monitor availability across regions, and integrate capacity checks into your deployment workflows. By following this guide, you'll be able to retrieve real-time information about available GPU instances across different locations and VM types.
Prerequisites
Before starting, ensure you have:
- API Credentials: Valid Crusoe Cloud API access key and secret key
-
Python Environment: Python 3.6+ with the following packages:
requests-
hmac(built-in) -
hashlib(built-in) -
base64(built-in) -
datetime(built-in)
-
Network Access: Ability to make HTTPS requests to
api.crusoecloud.com - Account Permissions: Your API credentials must have permission to query capacity data
Step-by-Step Instructions
1. Set Up Authentication
-
Replace the placeholder API credentials in your code with your actual credentials:
api_access_key = "your_actual_access_key_here" api_secret_key = "your_actual_secret_key_here"
- Ensure your credentials are in plain text
- Security Note: Never hardcode credentials in production code. Use environment variables or secure credential management systems
2. Generate the HMAC-SHA256 Signature
- The Crusoe API requires HMAC-SHA256 authentication for all requests
-
The signature is generated using the request path, query parameters, HTTP verb, and timestamp:
def generate_signature(path, query, verb, timestamp): payload = f"{path}\n{query}\n{verb}\n{timestamp}\n" decoded = base64.urlsafe_b64decode(api_secret_key + '=' * (-len(api_secret_key) % 4)) sig = hmac.new(decoded, msg=payload.encode('ascii'), digestmod=hashlib.sha256).digest() signature = base64.urlsafe_b64encode(sig).decode('ascii').rstrip('=') return signature - Important: The timestamp must be in UTC ISO format with no microseconds
3. Build the API Request
-
Construct the request URL using the base API endpoint:
base_url = "https://api.crusoecloud.com" api_version = "/v1alpha5" endpoint = "/capacities"
- Add query parameters for filtering (optional):
-
product_name: Filter by specific GPU types (e.g., 'a100.1x', 'h100.1x') -
location: Filter by specific regions (e.g., 'us-east-1a', 'eu-iceland1-a')
-
- Tip: Omit filters to query all available capacity across all regions and GPU types
4. Set Request Headers
-
Include the required headers for authentication:
headers = { "X-Crusoe-Timestamp": timestamp, "Authorization": f"Bearer 1.0:{api_access_key}:{signature}", "Content-Type": "application/json", } - The Authorization header follows the format:
Bearer 1.0:access_key:signature
5. Send the Request and Handle the Response
-
Make a GET request to the API endpoint:
response = requests.get(url, headers=headers, timeout=30)
- Process the response based on the HTTP status code:
- 200 OK: Successful query, parse the JSON response
- 400 Bad Request: Invalid parameters or malformed request
- 401 Unauthorized: Authentication failed
- 403 Forbidden: Insufficient permissions
- 500 Internal Server Error: Server-side issue
6. Parse and Display Capacity Data
-
Extract capacity information from the JSON response:
data = response.json() for item in data['items']: location = item.get('location') vm_type = item.get('type') quantity = item.get('quantity') - The response includes available capacity organized by location and VM type
- Tip: Aggregate data to create summary tables by region or GPU type
Example
Querying All Available Capacity
import hmac
import hashlib
import base64
import datetime
import requests
import json
from urllib.parse import urlencode
# Replace with your actual API credentials
api_access_key = "<Your-Access-Key>"
api_secret_key = "<Your-Secret-Key>"
def generate_signature(path, query, verb, timestamp):
"""
Generate the HMAC-SHA256 signature for the API request.
"""
payload = f"{path}\n{query}\n{verb}\n{timestamp}\n"
print(f"DEBUG - Signature payload: '{payload}'")
print(f"DEBUG - Path: '{path}'")
print(f"DEBUG - Query: '{query}'")
print(f"DEBUG - Verb: '{verb}'")
print(f"DEBUG - Timestamp: '{timestamp}'")
try:
decoded = base64.urlsafe_b64decode(api_secret_key + '=' * (-len(api_secret_key) % 4))
print(f"DEBUG - Secret key decoded successfully, length: {len(decoded)}")
except Exception as e:
print(f"DEBUG - Error decoding secret key: {e}")
return None
sig = hmac.new(decoded, msg=payload.encode('ascii'), digestmod=hashlib.sha256).digest()
signature = base64.urlsafe_b64encode(sig).decode('ascii').rstrip('=')
print(f"DEBUG - Generated signature: '{signature}'")
return signature
def handle_response(response):
"""
Enhanced error handling based on Crusoe Cloud API response schemas.
"""
if response.status_code == 200:
print("HTTP 200 OK - Capacity query succeeded")
try:
data = response.json()
if 'items' in data and data['items']:
print(f"Found {len(data['items'])} capacity items:")
print(json.dumps(data, indent=2))
# Summary of capacity
print("\nCapacity Summary:")
locations = set()
types = set()
total_quantity = 0
# Create capacity table data
capacity_data = {}
for item in data['items']:
location = item.get('location', 'Unknown')
vm_type = item.get('type', 'Unknown')
quantity = item.get('quantity', 0)
locations.add(location)
types.add(vm_type)
total_quantity += quantity
# Build capacity table
if location not in capacity_data:
capacity_data[location] = {}
capacity_data[location][vm_type] = quantity
print(f" Locations: {len(locations)} ({', '.join(sorted(locations))})")
print(f" GPU Types: {len(types)} ({', '.join(sorted(types))})")
print(f" Total Available Units: {total_quantity}")
# Create and display capacity table
print("\n" + "="*100)
print("CAPACITY TABLE BY REGION AND VM TYPE")
print("="*100)
# Get all unique VM types for table headers
all_vm_types = sorted(types)
all_locations = sorted(locations)
# Print table header
print(f"{'Region':<20} | {'VM Type':<25} | {'Available Units':<15}")
print("-" * 65)
# Print capacity data organized by region
for location in all_locations:
first_row = True
if location in capacity_data:
# Sort VM types by name for consistent ordering
location_vms = sorted(capacity_data[location].items())
for vm_type, quantity in location_vms:
if quantity > 0: # Only show VM types with available capacity
if first_row:
print(f"{location:<20} | {vm_type:<25} | {quantity:<15}")
first_row = False
else:
print(f"{'':<20} | {vm_type:<25} | {quantity:<15}")
if not first_row: # Add separator between regions if we printed anything
print("-" * 65)
# Summary table by VM type across all regions
print("\n" + "="*100)
print("SUMMARY TABLE BY VM TYPE (Total Across All Regions)")
print("="*100)
vm_totals = {}
for location_data in capacity_data.values():
for vm_type, quantity in location_data.items():
if vm_type not in vm_totals:
vm_totals[vm_type] = 0
vm_totals[vm_type] += quantity
print(f"{'VM Type':<30} | {'Total Available Units':<20}")
print("-" * 55)
for vm_type in sorted(vm_totals.keys()):
if vm_totals[vm_type] > 0:
print(f"{vm_type:<30} | {vm_totals[vm_type]:<20}")
print("="*100)
else:
print("No capacity items found in response")
print(json.dumps(data, indent=2))
except (ValueError, KeyError) as e:
print(f"Error parsing response JSON: {e}")
print("Raw response:", response.text)
elif response.status_code == 400:
print("HTTP 400 Bad Request")
try:
error_data = response.json()
code = error_data.get('code', 'unknown_error')
message = error_data.get('message', 'No error message provided')
print(f" Error Code: {code}")
print(f" Message: {message}")
# Provide helpful suggestions for common 400 errors
if 'location' in message.lower():
print("Suggestion: Check if the location names are correct")
print(" Valid locations: us-east-1a, us-southcentral-1a, us-northcentral-1a, eu-iceland1-a")
elif 'product' in message.lower():
print("Suggestion: Check if the product names are correct")
print(" Example product types: a100.1x, a100.2x, h100.1x, etc.")
except (ValueError, KeyError):
print(" Raw response:", response.text)
elif response.status_code == 401:
print("HTTP 401 Unauthorized - Authentication Failed")
try:
error_data = response.json()
code = error_data.get('code', 'unknown_error')
message = error_data.get('message', 'No error message provided')
print(f" Error Code: {code}")
print(f" Message: {message}")
print("Troubleshooting suggestions:")
print(" • Verify your API access key and secret key are correct")
print(" • Check if your API credentials have expired")
print(" • Ensure the timestamp generation is working correctly")
print(" • Verify the signature generation matches API requirements")
except (ValueError, KeyError):
print(" Raw response:", response.text)
elif response.status_code == 403:
print("HTTP 403 Forbidden - Permission Denied")
try:
error_data = response.json()
code = error_data.get('code', 'unknown_error')
message = error_data.get('message', 'No error message provided')
print(f" Error Code: {code}")
print(f" Message: {message}")
print("Troubleshooting suggestions:")
print(" • Your credentials may not have permission to access capacity data")
print(" • Contact Crusoe Cloud support to verify your account permissions")
print(" • Check if your account is properly activated")
except (ValueError, KeyError):
print(" Raw response:", response.text)
elif response.status_code == 500:
print("HTTP 500 Internal Server Error")
try:
error_data = response.json()
code = error_data.get('code', 'unknown_error')
message = error_data.get('message', 'No error message provided')
print(f" Error Code: {code}")
print(f" Message: {message}")
print("This is a server-side issue. Try again in a few minutes.")
print(" If the problem persists, contact Crusoe Cloud support.")
except (ValueError, KeyError):
print(" Raw response:", response.text)
else:
print(f"HTTP {response.status_code} - Unexpected Response")
print("Raw response:", response.text)
print("This is an unexpected status code. Contact Crusoe Cloud support if this persists.")
def query_capacity(product_names=None, locations=None):
"""
Query Crusoe Cloud capacity with optional filters.
Args:
product_names (list): List of product names to filter by (e.g., ['a100.1x', 'a100.2x'])
locations (list): List of locations to filter by (e.g., ['us-east-1a', 'us-southcentral-1a'])
"""
base_url = "https://api.crusoecloud.com"
api_version = "/v1alpha5"
verb = "GET"
endpoint = "/capacities"
# Build query parameters
query_params = {}
if product_names:
query_params['product_name'] = product_names
if locations:
query_params['location'] = locations
# Convert query parameters to URL-encoded string
query = urlencode(query_params, doseq=True) if query_params else ""
# Generate timestamp for the request
ts = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0).isoformat()
print(f"DEBUG - Generated timestamp: {ts}")
# Generate the signature
signature = generate_signature(api_version + endpoint, query, verb, ts)
if signature is None:
print("ERROR - Failed to generate signature, aborting request")
return None
# Prepare headers
auth_header = f"Bearer 1.0:{api_access_key}:{signature}"
headers = {
"X-Crusoe-Timestamp": ts,
"Authorization": auth_header,
"Content-Type": "application/json",
}
print(f"DEBUG - Authorization header: '{auth_header}'")
print(f"DEBUG - All headers: {headers}")
# Build the full URL
url = f"{base_url}{api_version}{endpoint}"
if query:
url += f"?{query}"
print(f"Querying capacity at: {url}")
# Send the GET request
try:
response = requests.get(
url,
headers=headers,
timeout=30
)
print(f"DEBUG - Response status: {response.status_code}")
print(f"DEBUG - Response headers: {dict(response.headers)}")
# Handle the response
handle_response(response)
return response
except requests.exceptions.Timeout:
print("Request timed out. The API might be slow or unavailable.")
print("Try again in a few minutes.")
except requests.exceptions.ConnectionError:
print("Connection error. Check your internet connection.")
print("Verify the API endpoint URL is correct.")
except requests.exceptions.RequestException as e:
print(f"Request failed with error: {e}")
print("Check your network connection and try again.")
except Exception as e:
print(f"Unexpected error occurred: {e}")
print("This might be a code issue. Check your implementation.")
def query_all_capacity():
"""
Query all available Crusoe Cloud capacity (all GPU types, all locations).
"""
print("Querying all available capacity (all GPU types, all locations)...")
query_capacity()
def main():
"""
Main function to query all available capacity.
"""
print("=== Crusoe Cloud Capacity Query ===\n")
# Query all capacity
print("Querying all available capacity (all GPU types, all locations)...")
query_all_capacity()
print("\nQuery completed!")
if __name__ == "__main__":
main()Sample Response Output
.
.
Capacity Summary:
Locations: 4 (eu-iceland1-a, us-east1-a, us-southcentral1-a, us-west1-a)
GPU Types: 30 (a100-80gb-sxm-ib.8x, a100-80gb.1x, a100-80gb.2x, a100-80gb.4x, a100-80gb.8x, b200-180gb-sxm-ib.8x, c1a.128x, c1a.16x, c1a.176x, c1a.2x, c1a.32x, c1a.4x, c1a.64x, c1a.8x, gb200-186gb-nvl-ib.4x, gb200-186gb-nvl.4x, h100-80gb-sxm-ib.8x, h200-141gb-sxm-ib.8x, l40s-48gb.10x, l40s-48gb.1x, l40s-48gb.2x, l40s-48gb.4x, l40s-48gb.8x, mi300x-192gb-ib.8x, s1a.120x, s1a.160x, s1a.20x, s1a.40x, s1a.60x, s1a.80x)
Total Available Units: 45133
====================================================================================================
CAPACITY TABLE BY REGION AND VM TYPE
====================================================================================================
Region | VM Type | Available Units
-----------------------------------------------------------------
eu-iceland1-a | b200-180gb-sxm-ib.8x | 2
| c1a.128x | 43
| c1a.16x | 453
| c1a.176x | 33
| c1a.2x | 3654
| c1a.32x | 223
| c1a.4x | 1826
| c1a.64x | 104
| c1a.8x | 911
| gb200-186gb-nvl-ib.4x | 36
| gb200-186gb-nvl.4x | 2
| h100-80gb-sxm-ib.8x | 4
| h200-141gb-sxm-ib.8x | 62
| s1a.120x | 10
| s1a.160x | 8
| s1a.20x | 81
| s1a.40x | 39
| s1a.60x | 20
| s1a.80x | 18
-----------------------------------------------------------------
us-east1-a | a100-80gb-sxm-ib.8x | 22
| a100-80gb.1x | 128
| a100-80gb.2x | 62
| a100-80gb.4x | 28
| a100-80gb.8x | 12
| c1a.128x | 10
| c1a.16x | 109
| c1a.176x | 7
| c1a.2x | 901
| c1a.32x | 54
| c1a.4x | 450
| c1a.64x | 25
| c1a.8x | 223
| h100-80gb-sxm-ib.8x | 30
| l40s-48gb.10x | 58
| l40s-48gb.1x | 707
| l40s-48gb.2x | 346
| l40s-48gb.4x | 141
| l40s-48gb.8x | 66
| mi300x-192gb-ib.8x | 4
| s1a.120x | 2
| s1a.20x | 27
| s1a.40x | 11
| s1a.60x | 7
| s1a.80x | 4
-----------------------------------------------------------------
us-southcentral1-a | c1a.128x | 5
| c1a.16x | 68
| c1a.176x | 5
| c1a.2x | 561
| c1a.32x | 33
| c1a.4x | 279
| c1a.64x | 14
| c1a.8x | 139
| h100-80gb-sxm-ib.8x | 17
| l40s-48gb.10x | 4
| l40s-48gb.1x | 90
| l40s-48gb.2x | 42
| l40s-48gb.4x | 15
| l40s-48gb.8x | 6
-----------------------------------------------------------------
====================================================================================================
SUMMARY TABLE BY VM TYPE (Total Across All Regions)
====================================================================================================
VM Type | Total Available Units
-------------------------------------------------------
a100-80gb-sxm-ib.8x | 22
a100-80gb.1x | 128
a100-80gb.2x | 62
a100-80gb.4x | 28
a100-80gb.8x | 12
b200-180gb-sxm-ib.8x | 2
c1a.128x | 58
c1a.16x | 630
c1a.176x | 45
c1a.2x | 5116
c1a.32x | 310
c1a.4x | 2555
c1a.64x | 143
c1a.8x | 1273
gb200-186gb-nvl-ib.4x | 36
gb200-186gb-nvl.4x | 2
h100-80gb-sxm-ib.8x | 51
h200-141gb-sxm-ib.8x | 62
l40s-48gb.10x | 62
l40s-48gb.1x | 797
l40s-48gb.2x | 388
l40s-48gb.4x | 156
l40s-48gb.8x | 72
mi300x-192gb-ib.8x | 4
s1a.120x | 12
s1a.160x | 8
s1a.20x | 108
s1a.40x | 50
s1a.60x | 27
s1a.80x | 22
====================================================================================================Troubleshooting
401 Unauthorized Error
- Problem: Authentication failed
-
Solution:
- Verify your API access key and secret key are correct
- Check if your API credentials have expired
- Ensure the timestamp is generated correctly in UTC format
- Verify the signature generation matches API requirements
400 Bad Request Error
- Problem: Invalid request parameters
-
Solution:
- Check that location names are valid (e.g., 'us-east-1a', 'us-southcentral-1a', 'us-northcentral-1a', 'eu-iceland1-a')
- Verify product names are correct (e.g., 'a100.1x', 'a100.2x', 'h100.1x')
- Ensure query parameters are properly URL-encoded
403 Forbidden Error
- Problem: Insufficient permissions
-
Solution:
- Your credentials may not have permission to access capacity data
- Contact Crusoe Cloud support to verify your account permissions
- Check if your account is properly activated
Empty Response or No Capacity Items
- Problem: No capacity data returned
-
Solution:
- Try querying without filters to see all available capacity
- Check if the regions you're querying actually have GPU capacity
- Verify the API version is correct (/v1alpha5)
Signature Generation Issues
- Problem: Authentication fails due to incorrect signature
-
Solution:
- Ensure the secret key is properly base64-decoded before signing
- Verify the payload format matches exactly:
path\nquery\nverb\ntimestamp\n - Check that the timestamp format is correct (ISO format, UTC, no microseconds)