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

CriterionLambdaSageMaker Endpoint
Model typeExternal APIs (OpenAI, Anthropic)Your own or open-source models
GPU needed❌ Lambda has no GPU✅ GPU instances available
Model sizeN/A (the model is at the provider)100MB - 100GB+
Cold start latency1-15s5-10 min (instance startup)
Inference latencyDepends on the API (1-30s)Depends on the model (50ms-5s)
Cost when idle$0 (it shuts off)$$$ (instance always on)
Cost when in usePer invocation + durationPer instance hour
ScalingAutomatic (0 to 1000+)Configurable (min/max instances)
Max execution15 minutesNo limit
Setup complexityLow (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

  1. SageMaker Developer Guide — Complete official documentation
  2. SageMaker Python SDK — High-level SDK for SageMaker
  3. SageMaker Pricing — Pricing by instance type
  4. SageMaker Serverless Inference — Serverless endpoints
  5. SageMaker Built-in Algorithms — Pre-built algorithms
  6. Hugging Face on SageMaker — Deploying Hugging Face models on SageMaker
  7. SageMaker vs Lambda — Official blog with comparisons
  8. SageMaker Cleanup — Cleanup guide to avoid costs