Module 5: AWS Services for AI (S3, Lambda, SageMaker Basics)
5. SageMaker Basics
Overview
In this capsule you'll understand what Amazon SageMaker is, when to use it instead of Lambda for AI inference, and how a basic endpoint deploy works. This is deliberately a "basics" capsule — SageMaker is a huge ecosystem (notebooks, training, pipelines, feature store, MLOps). We don't go deep into all of that here. What you need to know as an AI Engineer who deploys systems is: when is SageMaker the right answer? And if it is, what does a basic endpoint look like?
Context: You've built inference with Lambda invoking external APIs (OpenAI, Anthropic). That works when you use third-party models via API. But there are scenarios where Lambda isn't enough: custom models you trained yourself, open-source models you want to host (Llama, Mistral), or inference that needs a GPU. SageMaker covers those cases. This capsule gives you the criteria to decide and the basic mechanics to get started.
What SageMaker Is
SageMaker in one sentence
Amazon SageMaker is a machine learning platform that covers the whole cycle: from training models to serving them as inference endpoints. It's AWS's answer to "I need to host my own model."
The SageMaker ecosystem
Amazon SageMaker (complete ecosystem):
├── SageMaker Studio ← Notebooks and IDE (like Jupyter on AWS)
├── SageMaker Training ← Train models with on-demand GPU
├── SageMaker Processing ← Data preprocessing
├── SageMaker Endpoints ← Serve models as an API (THIS IS YOUR FOCUS)
├── SageMaker Pipelines ← ML workflow orchestration
├── SageMaker Feature Store ← Feature storage
├── SageMaker Model Registry ← Model versioning
├── SageMaker Ground Truth ← Data labeling
└── SageMaker Canvas ← No-code ML
For this guide, what matters is SageMaker Endpoints: the ability to take a model (yours or pre-trained) and expose it as an HTTP API. The rest of the ecosystem is MLOps — relevant if you specialize in ML, but out of scope for this deployment guide.
Mental model: SageMaker vs Lambda
What kind of inference do you need?
Case 1: You invoke a third-party API (OpenAI, Anthropic, Cohere)
├── Your code makes an HTTP request to the provider's API
├── You don't host any model
├── You need: lightweight compute + internet
└── Solution: Lambda ✅ (capsules 02-04 of this module)
Case 2: You host your own model (Llama, Mistral, custom model)
├── The model runs on YOUR infrastructure
├── You need a GPU or dedicated compute
├── The model is in S3 or a registry
└── Solution: SageMaker Endpoint ✅ (this capsule)
Case 3: Inference with a lightweight model (scikit-learn, small model)
├── The model fits in Lambda memory (< 10GB container)
├── Doesn't need a GPU
├── Inference time < 15min
└── Solution: Lambda with container ✅ (can work)
When SageMaker vs Lambda
Decision framework
| Criterion | Lambda | SageMaker Endpoint |
|---|---|---|
| Model type | External APIs (OpenAI, Anthropic) | Your own or open-source models |
| GPU needed | ❌ Lambda has no GPU | ✅ GPU instances available |
| Model size | N/A (the model is at the provider) | 100MB - 100GB+ |
| Cold start latency | 1-15s | 5-10 min (instance startup) |
| Inference latency | Depends on the API (1-30s) | Depends on the model (50ms-5s) |
| Cost when idle | $0 (it shuts off) | $$$ (instance always on) |
| Cost when in use | Per invocation + duration | Per instance hour |
| Scaling | Automatic (0 to 1000+) | Configurable (min/max instances) |
| Max execution | 15 minutes | No limit |
| Setup complexity | Low (zip/container + SAM) | Medium-high (model artifacts, container, config) |
Concrete scenarios
Scenario 1: Chatbot with GPT-4o
├── Model: OpenAI API (you host nothing)
├── Traffic: Variable (peaks and valleys)
├── Decision: Lambda ✅
└── Reason: You only need compute to make HTTP requests
Scenario 2: Custom sentiment classifier (fine-tuned BERT)
├── Model: 400MB, trained on your data
├── Traffic: Constant, 24/7
├── Decision: SageMaker ✅
└── Reason: Your own model that needs dedicated hosting
Scenario 3: RAG with OpenAI embeddings
├── Model: OpenAI Embeddings API
├── Traffic: Moderate, business hours
├── Decision: Lambda ✅
└── Reason: Embeddings via API, you don't host a model
Scenario 4: Image generation with Stable Diffusion
├── Model: 5GB+, needs a GPU
├── Traffic: Low but needs a fast response
├── Decision: SageMaker ✅
└── Reason: GPU required, heavy model
Scenario 5: Lightweight scikit-learn classifier
├── Model: 50MB, pure Python
├── Traffic: Event-driven (incoming emails)
├── Decision: Lambda (container) ✅
└── Reason: Small model, no GPU needed, event-driven
The golden rule
Use Lambda when you invoke third-party APIs or when your model is small enough to fit in a Lambda container (< 10GB) without needing a GPU.
Use SageMaker when you host your own models, large open-source models, or any model that needs a GPU.
Anatomy of a SageMaker Endpoint
The three components
A SageMaker Endpoint has three pieces:
1. Model Artifacts (in S3)
├── Your trained model (model.tar.gz)
├── Can include: weights, config, tokenizer
└── SageMaker downloads it when the instance starts
2. Container Image (in ECR)
├── The runtime that loads and serves the model
├── AWS provides pre-built containers for common frameworks
│ (PyTorch, TensorFlow, Hugging Face, scikit-learn)
└── Or you can create a custom container
3. Endpoint Configuration
├── Instance type (ml.m5.large, ml.g4dn.xlarge for GPU)
├── Instance count (1+ for high availability)
└── Auto-scaling rules
┌─────────────────────────────┐
│ S3: model-artifacts/ │
│ └── model.tar.gz │
└──────────┬──────────────────┘
│ Download when endpoint is created
↓
┌─────────────────────────────┐
│ SageMaker Endpoint │
│ ├── Container (ECR image) │
│ ├── Instance (ml.m5.large) │
│ ├── Model loaded in memory │
│ └── HTTPS API available │
└──────────┬──────────────────┘
│
invoke_endpoint()
│
┌──────────┴──────────────────┐
│ Your code (Lambda, app, etc) │
│ sagemaker_runtime.invoke_ │
│ endpoint(EndpointName=...) │
└─────────────────────────────┘
Deploying a Pre-Trained Model
Example: Classification model with scikit-learn
# deploy_sagemaker_endpoint.py
import boto3
import json
import os
import tarfile
import pickle
import numpy as np
from datetime import datetime
sagemaker = boto3.client("sagemaker")
s3 = boto3.client("s3")
BUCKET = os.environ.get("SAGEMAKER_BUCKET", "sagemaker-models-dev")
ROLE_ARN = os.environ.get("SAGEMAKER_ROLE_ARN", "arn:aws:iam::123456789012:role/SageMakerExecutionRole")
def create_dummy_model():
"""Creates a dummy scikit-learn model for demonstration."""
from sklearn.linear_model import LogisticRegression
X = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])
y = np.array([0, 0, 1, 1])
model = LogisticRegression()
model.fit(X, y)
return model
def package_model(model, model_name: str) -> str:
"""Packages a model as model.tar.gz and uploads it to S3."""
local_path = f"/tmp/{model_name}"
os.makedirs(local_path, exist_ok=True)
model_file = f"{local_path}/model.pkl"
with open(model_file, "wb") as f:
pickle.dump(model, f)
tar_path = f"/tmp/{model_name}.tar.gz"
with tarfile.open(tar_path, "w:gz") as tar:
tar.add(model_file, arcname="model.pkl")
s3_key = f"models/{model_name}/model.tar.gz"
s3.upload_file(tar_path, BUCKET, s3_key)
model_uri = f"s3://{BUCKET}/{s3_key}"
print(f"Model uploaded to: {model_uri}")
return model_uri
def create_sagemaker_model(model_name: str, model_uri: str) -> str:
"""Creates a SageMaker Model pointing to the artifact in S3."""
response = sagemaker.create_model(
ModelName=model_name,
PrimaryContainer={
"Image": "683313688378.dkr.ecr.us-east-1.amazonaws.com/sagemaker-scikit-learn:1.2-1-cpu-py3",
"ModelDataUrl": model_uri,
},
ExecutionRoleArn=ROLE_ARN,
)
print(f"SageMaker Model created: {model_name}")
return response["ModelArn"]
def create_endpoint_config(config_name: str, model_name: str) -> str:
"""Creates the endpoint configuration."""
response = sagemaker.create_endpoint_config(
EndpointConfigName=config_name,
ProductionVariants=[
{
"VariantName": "default",
"ModelName": model_name,
"InstanceType": "ml.m5.large",
"InitialInstanceCount": 1,
}
],
)
print(f"Endpoint config created: {config_name}")
return response["EndpointConfigArn"]
def create_endpoint(endpoint_name: str, config_name: str) -> str:
"""Creates the endpoint (this takes 5-10 minutes)."""
response = sagemaker.create_endpoint(
EndpointName=endpoint_name,
EndpointConfigName=config_name,
)
print(f"Creating endpoint: {endpoint_name}")
print("This takes 5-10 minutes...")
return response["EndpointArn"]
def wait_for_endpoint(endpoint_name: str):
"""Waits for the endpoint to be InService."""
waiter = sagemaker.get_waiter("endpoint_in_service")
waiter.wait(
EndpointName=endpoint_name,
WaiterConfig={"Delay": 30, "MaxAttempts": 30},
)
print(f"Endpoint {endpoint_name} is InService")
Invoke the endpoint
import boto3
import json
sagemaker_runtime = boto3.client("sagemaker-runtime")
def invoke_sagemaker_endpoint(endpoint_name: str, data: list) -> dict:
"""Invokes a SageMaker endpoint for inference."""
response = sagemaker_runtime.invoke_endpoint(
EndpointName=endpoint_name,
ContentType="application/json",
Body=json.dumps({"instances": data}),
)
result = json.loads(response["Body"].read().decode("utf-8"))
return result
predictions = invoke_sagemaker_endpoint(
endpoint_name="sentiment-classifier-v1",
data=[[1.5, 2.5], [8.0, 9.0]],
)
print(f"Predictions: {predictions}")
Invoke SageMaker from Lambda
# lambda_sagemaker_handler.py
import boto3
import json
import os
sagemaker_runtime = boto3.client("sagemaker-runtime")
ENDPOINT_NAME = os.environ.get("SAGEMAKER_ENDPOINT", "sentiment-classifier-v1")
def handler(event, context):
"""Lambda that invokes a SageMaker endpoint."""
try:
body = json.loads(event.get("body", "{}"))
except (json.JSONDecodeError, TypeError):
return _response(400, {"error": "Invalid JSON"})
data = body.get("data")
if not data:
return _response(400, {"error": "data field is required"})
try:
response = sagemaker_runtime.invoke_endpoint(
EndpointName=ENDPOINT_NAME,
ContentType="application/json",
Body=json.dumps({"instances": data}),
)
result = json.loads(response["Body"].read().decode("utf-8"))
except Exception as e:
return _response(502, {"error": f"SageMaker error: {str(e)}"})
return _response(200, {
"predictions": result,
"endpoint": ENDPOINT_NAME,
})
def _response(status_code, body):
return {
"statusCode": status_code,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(body),
}
Cleanup: Don't Leave Endpoints Running
SageMaker endpoints cost money while they're active. An ml.m5.large costs $0.115/hour ($84/month). Always delete endpoints when you finish experimenting:
def cleanup_endpoint(endpoint_name: str):
"""Deletes the endpoint, config, and model from SageMaker."""
try:
sagemaker.delete_endpoint(EndpointName=endpoint_name)
print(f"Endpoint deleted: {endpoint_name}")
except sagemaker.exceptions.ClientError:
print(f"Endpoint {endpoint_name} not found")
config_name = endpoint_name
try:
sagemaker.delete_endpoint_config(EndpointConfigName=config_name)
print(f"Config deleted: {config_name}")
except sagemaker.exceptions.ClientError:
print(f"Config {config_name} not found")
model_name = endpoint_name
try:
sagemaker.delete_model(ModelName=model_name)
print(f"Model deleted: {model_name}")
except sagemaker.exceptions.ClientError:
print(f"Model {model_name} not found")
cleanup_endpoint("sentiment-classifier-v1")
SageMaker Costs: Why They Matter
Basic pricing
SageMaker Endpoint Pricing (us-east-1):
├── ml.t3.medium (CPU, basic): $0.05/hr → ~$36/month
├── ml.m5.large (CPU, production): $0.115/hr → ~$84/month
├── ml.m5.xlarge (CPU, heavy): $0.23/hr → ~$168/month
├── ml.g4dn.xlarge (GPU, NVIDIA T4): $0.736/hr → ~$537/month
└── ml.g5.xlarge (GPU, NVIDIA A10G): $1.408/hr → ~$1,028/month
Comparison with Lambda:
├── Lambda (1K inv/day, 5s, 512MB): ~$2/month
├── SageMaker ml.m5.large 24/7: ~$84/month
└── SageMaker ml.g4dn.xlarge 24/7: ~$537/month
SageMaker has no free tier for endpoints. From the first minute your endpoint is "InService," you're paying. That's why cleanup is critical.
When the cost of SageMaker makes sense
Lambda + OpenAI API (10K inv/day):
├── Lambda: ~$3/month
├── OpenAI: ~$200/month (depends on tokens)
└── Total: ~$203/month
SageMaker + open-source model (10K inv/day):
├── SageMaker ml.g4dn.xlarge: ~$537/month
├── You don't pay OpenAI's API: $0
└── Total: ~$537/month
When is SageMaker cheaper?
├── At very high volume (>50K inv/day), the API cost skyrockets
├── When the open-source model is "good enough" for your case
├── When you need full control of the model (fine-tuning, sensitive data)
└── When you can't send data to external APIs (compliance)
Serverless Inference: The Middle Ground
SageMaker also offers Serverless Inference — endpoints that scale to zero:
def create_serverless_endpoint_config(config_name: str, model_name: str) -> str:
"""Creates a serverless endpoint config (scales to zero)."""
response = sagemaker.create_endpoint_config(
EndpointConfigName=config_name,
ProductionVariants=[
{
"VariantName": "default",
"ModelName": model_name,
"ServerlessConfig": {
"MemorySizeInMB": 2048,
"MaxConcurrency": 5,
},
}
],
)
return response["EndpointConfigArn"]
SageMaker Serverless Inference:
├── Scales to zero (you don't pay when there's no traffic)
├── You pay for: inference duration + memory
├── Cold start: 30s-2min (significantly slower than Lambda)
├── Ideal for: custom models with sporadic traffic
└── Doesn't support GPU (CPU only)
Troubleshooting
Problem 1: "Could not find model data" when creating the endpoint
The ModelDataUrl points to an S3 path that doesn't exist, or the role has no permissions.
# Verify that model.tar.gz exists in S3
aws s3 ls s3://sagemaker-models-dev/models/sentiment-classifier-v1/
# Verify that the IAM role has S3 permissions
aws iam get-role-policy --role-name SageMakerExecutionRole --policy-name S3Access
Problem 2: Endpoint takes >15 minutes to create
This is normal for large or GPU instances. The startup includes: provisioning the instance, downloading the container, downloading the model, loading it into memory.
# Monitor the status
response = sagemaker.describe_endpoint(EndpointName="my-endpoint")
print(response["EndpointStatus"])
# Creating → InService (success) or Failed (error)
Problem 3: "ModelError" when invoking the endpoint
The model can't process the input you sent it. Check the format.
# Check what format the container expects
# For scikit-learn: {"instances": [[1, 2], [3, 4]]}
# For PyTorch: depends on the inference script
# For Hugging Face: {"inputs": "text to classify"}
Problem 4: Unexpected SageMaker bill
You left an endpoint running. List and delete active endpoints.
response = sagemaker.list_endpoints(StatusEquals="InService")
for ep in response["Endpoints"]:
print(f"Active endpoint: {ep['EndpointName']} since {ep['CreationTime']}")
# cleanup_endpoint(ep["EndpointName"])
Practical Exercises
Exercise 1: Applied decision framework
Given these 5 scenarios, decide whether you'd use Lambda or SageMaker for each. Justify your answer with at least 2 reasons.
See solution
Scenario A: Customer support app that uses Claude to answer tickets
├── Decision: Lambda ✅
├── Reason 1: You invoke Anthropic's API, you don't host a model
└── Reason 2: Variable traffic → Lambda scales to zero
Scenario B: Fraud detection model trained on your own data (XGBoost, 200MB)
├── Decision: SageMaker ✅ (or Lambda with container if volume is low)
├── Reason 1: Your own model that you need to host
└── Reason 2: Real-time 24/7 inference for transactions
Scenario C: Batch processing of 10K documents with GPT-4o-mini
├── Decision: Lambda ✅
├── Reason 1: You invoke OpenAI's API, you don't host a model
└── Reason 2: Event-driven (S3 trigger), bursty traffic
Scenario D: Embedding generation with an open-source sentence-transformers model
├── Decision: SageMaker ✅
├── Reason 1: A ~500MB model that needs a GPU for speed
└── Reason 2: Can be cheaper than OpenAI at high volume
Scenario E: Sentiment classifier with a fine-tuned 50MB model (scikit-learn)
├── Decision: Lambda (container) ✅
├── Reason 1: Small model that fits in Lambda
└── Reason 2: No GPU needed, event-driven, scales to zero
Exercise 2: Calculate monthly cost SageMaker vs Lambda+API
Calculate the monthly cost of serving a classification model for 50K invocations/day in two scenarios: (A) SageMaker with ml.m5.large and (B) Lambda + OpenAI API.
See solution
def compare_costs():
"""Compares SageMaker vs Lambda+API costs for 50K inv/day."""
daily_invocations = 50_000
monthly_invocations = daily_invocations * 30
# Scenario A: SageMaker ml.m5.large
sm_hourly = 0.115
sm_monthly = sm_hourly * 24 * 30 # $82.80
sm_total = sm_monthly
# Scenario B: Lambda + OpenAI
lambda_invocations_cost = max(0, monthly_invocations - 1_000_000) * 0.0000002
lambda_duration_gb_s = monthly_invocations * 5 * (512 / 1024)
lambda_compute_cost = max(0, lambda_duration_gb_s - 400_000) * 0.0000166667
lambda_total = lambda_invocations_cost + lambda_compute_cost
avg_input_tokens = 100
avg_output_tokens = 50
openai_input_cost = (monthly_invocations * avg_input_tokens / 1_000_000) * 0.15
openai_output_cost = (monthly_invocations * avg_output_tokens / 1_000_000) * 0.60
openai_total = openai_input_cost + openai_output_cost
lambda_api_total = lambda_total + openai_total
print("=" * 50)
print(f"Invocations: {daily_invocations:,}/day ({monthly_invocations:,}/month)")
print()
print(f"Scenario A: SageMaker ml.m5.large")
print(f" Endpoint 24/7: ${sm_total:>8.2f}/month")
print(f" OpenAI API: ${0:>8.2f}/month")
print(f" TOTAL: ${sm_total:>8.2f}/month")
print()
print(f"Scenario B: Lambda + OpenAI API")
print(f" Lambda compute: ${lambda_total:>8.2f}/month")
print(f" OpenAI API: ${openai_total:>8.2f}/month")
print(f" TOTAL: ${lambda_api_total:>8.2f}/month")
print()
if sm_total < lambda_api_total:
print(f"SageMaker is ${lambda_api_total - sm_total:.2f}/month cheaper")
else:
print(f"Lambda+API is ${sm_total - lambda_api_total:.2f}/month cheaper")
print("=" * 50)
compare_costs()
Exercise 3: Endpoint cleanup script
Create a script that lists all active SageMaker endpoints, shows how long they've been running, estimates the accumulated cost, and asks whether you want to delete them.
See solution
import boto3
from datetime import datetime, timezone
sagemaker = boto3.client("sagemaker")
INSTANCE_PRICES = {
"ml.t3.medium": 0.05,
"ml.m5.large": 0.115,
"ml.m5.xlarge": 0.23,
"ml.g4dn.xlarge": 0.736,
"ml.g5.xlarge": 1.408,
}
def audit_endpoints():
"""Lists active endpoints with estimated cost."""
response = sagemaker.list_endpoints(StatusEquals="InService")
endpoints = response["Endpoints"]
if not endpoints:
print("No active endpoints found.")
return []
print(f"Found {len(endpoints)} active endpoint(s):\n")
total_estimated_cost = 0
results = []
for ep in endpoints:
name = ep["EndpointName"]
created = ep["CreationTime"]
now = datetime.now(timezone.utc)
hours_running = (now - created).total_seconds() / 3600
config = sagemaker.describe_endpoint_config(EndpointConfigName=name)
variants = config.get("ProductionVariants", [])
for variant in variants:
instance_type = variant.get("InstanceType", "unknown")
instance_count = variant.get("InitialInstanceCount", 1)
hourly_rate = INSTANCE_PRICES.get(instance_type, 0)
estimated_cost = hourly_rate * hours_running * instance_count
total_estimated_cost += estimated_cost
results.append({
"name": name,
"instance_type": instance_type,
"count": instance_count,
"hours": hours_running,
"estimated_cost": estimated_cost,
})
print(f" {name}")
print(f" Instance: {instance_type} x{instance_count}")
print(f" Running: {hours_running:.1f} hours")
print(f" Cost: ${estimated_cost:.2f} estimated")
print()
print(f"Total estimated cost: ${total_estimated_cost:.2f}")
return results
def cleanup_all(endpoints: list, confirm: bool = False):
"""Deletes all the listed endpoints."""
for ep in endpoints:
if confirm:
sagemaker.delete_endpoint(EndpointName=ep["name"])
print(f"Deleted: {ep['name']}")
else:
print(f"Would delete: {ep['name']} (${ep['estimated_cost']:.2f})")
results = audit_endpoints()
# cleanup_all(results, confirm=False) # Change to True to delete
Exercise 4: Lambda that chooses between an external API and SageMaker
Implement a Lambda handler that dynamically decides whether to invoke the OpenAI API or a SageMaker endpoint based on a request parameter.
See solution
import boto3
import json
import os
from openai import OpenAI
openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", ""))
sagemaker_runtime = boto3.client("sagemaker-runtime")
SAGEMAKER_ENDPOINT = os.environ.get("SAGEMAKER_ENDPOINT", "classifier-v1")
def invoke_openai(prompt: str, max_tokens: int) -> dict:
response = openai_client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
)
return {
"result": response.choices[0].message.content,
"backend": "openai",
"model": response.model,
"tokens_used": response.usage.total_tokens,
}
def invoke_sagemaker(data: list) -> dict:
response = sagemaker_runtime.invoke_endpoint(
EndpointName=SAGEMAKER_ENDPOINT,
ContentType="application/json",
Body=json.dumps({"instances": data}),
)
result = json.loads(response["Body"].read().decode("utf-8"))
return {
"result": result,
"backend": "sagemaker",
"endpoint": SAGEMAKER_ENDPOINT,
}
def handler(event, context):
try:
body = json.loads(event.get("body", "{}"))
except (json.JSONDecodeError, TypeError):
return _response(400, {"error": "Invalid JSON"})
backend = body.get("backend", "openai")
try:
if backend == "openai":
prompt = body.get("prompt", "").strip()
if not prompt:
return _response(400, {"error": "prompt required for openai"})
result = invoke_openai(prompt, body.get("max_tokens", 500))
elif backend == "sagemaker":
data = body.get("data")
if not data:
return _response(400, {"error": "data required for sagemaker"})
result = invoke_sagemaker(data)
else:
return _response(400, {"error": f"Unknown backend: {backend}"})
except Exception as e:
return _response(502, {"error": str(e), "backend": backend})
return _response(200, result)
def _response(status_code, body):
return {
"statusCode": status_code,
"headers": {"Content-Type": "application/json"},
"body": json.dumps(body),
}
Summary
- SageMaker is for hosting your own or open-source models. If you invoke third-party APIs (OpenAI, Anthropic), Lambda is enough.
- A SageMaker endpoint has three pieces: model artifacts in S3, a container image in ECR, and endpoint configuration (instance type, count).
- SageMaker costs are per instance hour — not per invocation. An ml.m5.large costs ~$84/month 24/7. Delete endpoints you don't use.
- Serverless Inference is the middle ground: it scales to zero like Lambda, but serves custom models. Significant cold start (30s-2min).
- The Lambda vs SageMaker decision depends on: model type (API vs your own), GPU need, traffic pattern, and budget.
- SageMaker basics is enough for this guide. If you need training, pipelines, MLOps → that's a full guide of its own.
Additional Resources
- SageMaker Developer Guide — Complete official documentation
- SageMaker Python SDK — High-level SDK for SageMaker
- SageMaker Pricing — Pricing by instance type
- SageMaker Serverless Inference — Serverless endpoints
- SageMaker Built-in Algorithms — Pre-built algorithms
- Hugging Face on SageMaker — Deploying Hugging Face models on SageMaker
- SageMaker vs Lambda — Official blog with comparisons
- SageMaker Cleanup — Cleanup guide to avoid costs