Trace a Serverless API with AWS X-Ray

Instrument a serverless API with X-Ray to visualize request flow.

Introduction

30 Second Summary

When an app feels slow, figuring out which part is causing the delay can feel like searching for a needle in a haystack. Distributed tracing solves this by recording the exact path each request takes through your system.

In this project, you will build a serverless "Book Inventory" API on AWS, enable AWS X-Ray tracing across every service in the chain, and use the X-Ray console to visualize exactly how requests flow from start to finish.

What You'll Build

A REST API backed by API Gateway, Lambda, and DynamoDB, fully instrumented with the AWS X-Ray SDK for end-to-end distributed tracing.

By the end of this project, you'll have:

  • A DynamoDB table storing book records and a Python Lambda function instrumented with the X-Ray SDK to trace downstream calls.
  • An API Gateway REST API connected to your Lambda with active tracing enabled on both services.
  • X-Ray traces visible in the service map showing latency across all three services as requests flow through your system.
  • Secret Mission: Add custom annotations to your traces and create a sampling rule to control which requests get traced.

Are there any prerequisites?

You need an AWS account (free tier eligible) and basic familiarity with navigating the AWS console. No prior experience with X-Ray or distributed tracing is required.

Before We Start

Before We Start...

Set Up Your Project and Deployment Package

Before you can trace requests through your serverless API, you need to prepare the code that powers it. Your Lambda function needs the AWS X-Ray SDK bundled alongside it, because Lambda does not install third-party packages for you.

In this step, you will write the Lambda function locally, install its dependencies, and package everything into a zip file that is ready to upload to AWS.

In this step, get ready to:
  • Verify Python is installed on your machine.
  • Create the Lambda function code with X-Ray instrumentation.
  • Package the code and its dependencies into a deployment zip.
Verify Python is installed

You need Python installed locally so you can use pip to download the X-Ray SDK. You will not run the Lambda function locally. Python is only needed for packaging the dependencies.

  • Press the Windows key to open your search bar.
  • Type PowerShell and press Enter to open it.
  • Check your Python version by running this command:
python --version

What should I see?

You should see output like Python 3.13.x (or any 3.x version). The exact minor version does not matter as long as it is Python 3.

Command not found?

If PowerShell does not recognize python, Python is not installed or not in your system PATH.

  • Download the installer from https://www.python.org/downloads/.
  • During installation, check the box that says Add python.exe to PATH.
  • After installing, close and reopen PowerShell, then run python --version again.

Still stuck? Help me install Python on Windows so I can run pip.

Create the project folder and Lambda function code

Your Lambda function uses the X-Ray SDK to automatically trace every call it makes to DynamoDB. Two lines of setup code are all it takes to instrument the function.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir xray-book-api
cd xray-book-api
  • Create the Lambda function file by running:
New-Item lambda_function.py -ItemType File
  • Open the file in Notepad by running:
notepad lambda_function.py
  • Paste the following code into Notepad:
import json
import boto3
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all

# Instrument all AWS SDK clients so X-Ray traces downstream calls
patch_all()

# Connect to the DynamoDB table
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('BookInventory')


def lambda_handler(event, context):
    # Determine which HTTP method the client used
    http_method = event['httpMethod']

    if http_method == 'GET':
        # Scan the table and return all books
        response = table.scan()
        return {
            'statusCode': 200,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps(response['Items'])
        }
    elif http_method == 'POST':
        # Parse the request body and add a new book
        body = json.loads(event['body'])
        table.put_item(Item={
            'BookId': body['BookId'],
            'Title': body['Title'],
            'Author': body['Author']
        })
        return {
            'statusCode': 201,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps({'message': 'Book added successfully'})
        }
    else:
        return {
            'statusCode': 400,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps({'message': 'Unsupported method'})
        }

What does this code do?

  • patch_all() instruments every AWS SDK client (like boto3) so X-Ray automatically records how long each downstream call takes.
  • The lambda_handler function reads the HTTP method from the incoming request. GET returns all books from DynamoDB. POST adds a new book.
  • The response format (statusCode, headers, body) is required by API Gateway Lambda proxy integration.
  • Save the file in Notepad by pressing Ctrl+S.
  • Close Notepad.
  • Confirm the file exists by running:
ls

You should see lambda_function.py listed in the output.

Don't see the file?

  • Make sure you pressed Ctrl+S in Notepad before closing it.
  • Confirm you are inside the xray-book-api folder by running pwd in PowerShell.

Help me create my lambda_function.py file in the right folder.

✔️ Awesome, I've got everything!

Great. Make sure the file is saved before moving on.

ⓧ I'd like to double check the full code

Here is the complete lambda_function.py for reference:

import json
import boto3
from aws_xray_sdk.core import xray_recorder
from aws_xray_sdk.core import patch_all

patch_all()

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('BookInventory')


def lambda_handler(event, context):
    http_method = event['httpMethod']

    if http_method == 'GET':
        response = table.scan()
        return {
            'statusCode': 200,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps(response['Items'])
        }
    elif http_method == 'POST':
        body = json.loads(event['body'])
        table.put_item(Item={
            'BookId': body['BookId'],
            'Title': body['Title'],
            'Author': body['Author']
        })
        return {
            'statusCode': 201,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps({'message': 'Book added successfully'})
        }
    else:
        return {
            'statusCode': 400,
            'headers': {'Content-Type': 'application/json'},
            'body': json.dumps({'message': 'Unsupported method'})
        }
Build the deployment package

Lambda needs all third-party dependencies bundled into a single zip file alongside your function code. You will install the aws-xray-sdk into a package subfolder, copy your function in, and zip everything.

  • Install the X-Ray SDK into a package subfolder by running:
pip install --target ./package aws-xray-sdk

Why install into a subfolder?

The --target flag tells pip to place the library files into a specific directory instead of your global Python installation. This lets you bundle them alongside your code in a zip that Lambda can unpack and run directly.

  • Copy your Lambda function into the package folder by running:
Copy-Item lambda_function.py ./package/
  • Zip the contents of the package folder into a deployment archive by running:
Compress-Archive -Path ./package/* -DestinationPath deployment-package.zip

What does this command do?

Compress-Archive is a built-in PowerShell cmdlet that creates zip files. Using ./package/* (with the wildcard) zips the contents of the folder without including the package folder itself. Lambda expects your handler file and dependencies at the top level of the zip.

  • Confirm the zip was created by running:
ls

You should see deployment-package.zip listed alongside lambda_function.py and the package folder.

Seeing an error from pip or Compress-Archive?

  • If pip is not recognized, try python -m pip install --target ./package aws-xray-sdk instead.
  • If Compress-Archive says the path does not exist, confirm the package folder was created by pip (run ls ./package to check).
  • If the zip already exists from a previous attempt, delete it first with Remove-Item deployment-package.zip and try again.

Help me fix my deployment package build in PowerShell.

Your deployment package is ready to go. Next up, you will create the DynamoDB table and Lambda function in AWS, then upload this zip to bring your API to life.

Create the DynamoDB Table and Lambda Function

Your deployment package is ready to go. Now you need somewhere for it to run and somewhere for it to store data.

In this step, you will create the two core AWS resources your API needs: a DynamoDB table to store book records and a Lambda function to handle API requests. You will also give the function the permissions it needs to access DynamoDB and send traces to X-Ray.

In this step, get ready to:
  • Create a DynamoDB table to store book inventory records.
  • Create a Lambda function with the correct permissions.
  • Upload and test your deployment package.
Create the DynamoDB table

Your Lambda function needs a database to read and write book records. DynamoDB is a fully managed NoSQL database that requires no server provisioning.

  • In the AWS Management Console, search for DynamoDB in the top search bar and select it.
  • In the left sidebar, click Tables.
  • Click Create table.
  • For Table name, enter BookInventory.
  • For Partition key, enter BookId and leave the type set to String.
  • Leave all other settings at their defaults. The table capacity mode defaults to On-demand, which means DynamoDB scales automatically and you only pay for what you use.
  • Click Create table.

Wait for the table status to change to Active. This usually takes a few seconds.

What is a partition key?

A partition key is the primary way DynamoDB identifies each item in your table. Every item must have a unique partition key value. In your case, each book gets a unique BookId that distinguishes it from other books.

Table stuck in Creating status?

If your table stays in Creating status for more than a minute, try refreshing the page. DynamoDB tables typically become active within 10-20 seconds.

help me troubleshoot my DynamoDB table creation

Create the Lambda function and configure permissions

Now you need a Lambda function to run your Python code. When you create a Lambda function, AWS automatically creates an IAM execution role for it. You will then add policies to that role so the function can access DynamoDB and send trace data to X-Ray.

  • In the AWS Management Console, search for Lambda in the top search bar and select it.
  • Click Create function.
  • Select Author from scratch.
  • For Function name, enter BookInventoryFunction.
  • For Runtime, select Python 3.13.
  • Leave Architecture set to x86_64.
  • Expand Change default execution role and confirm Create a new role with basic Lambda permissions is selected.
  • Click Create function.

You should see a success banner confirming your function was created. Now you need to add two more permissions to the execution role.

  • On your function page, click the Configuration tab.
  • In the left sidebar, click Permissions.
  • Under Execution role, click the role name link. This opens the IAM console in a new tab.
  • In the IAM console, click Add permissions, then select Attach policies.
  • Search for AmazonDynamoDBFullAccess and select its checkbox.
  • Search for AWSXRayDaemonWriteAccess and select its checkbox.
  • Click Add permissions.

Why two extra policies?

The AmazonDynamoDBFullAccess policy lets your function read and write items in the BookInventory table. The AWSXRayDaemonWriteAccess policy grants permissions to send trace segments to X-Ray.

Without these policies, your function would get "Access Denied" errors when trying to reach DynamoDB or report trace data.

Don't see the role name link?

Make sure you are on the Configuration tab (not the Code tab) and that Permissions is selected in the left sidebar. The role name appears as a clickable blue link under Execution role.

help me find my Lambda execution role in the console

Upload your deployment package and test

With permissions configured, it is time to upload your deployment-package.zip and verify the function works.

  • Return to the Lambda console tab with your BookInventoryFunction page.
  • Click the Code tab.
  • In the Code source pane, click Upload from.
  • Select .zip file.
  • Click Upload, navigate to your xray-book-api folder, select deployment-package.zip, and click Open.
  • Click Save.

Now verify the handler is set correctly.

  • Scroll down to the Runtime settings section below the code editor.
  • Confirm the Handler value is lambda_function.lambda_handler. If it shows a different value, click Edit and change it to lambda_function.lambda_handler, then click Save.

What is the handler?

The handler tells Lambda which function to call when your code is invoked. The format is filename.function_name. So lambda_function.lambda_handler means Lambda looks in lambda_function.py and calls the lambda_handler function.

Time to test the function with a simulated GET request.

  • Click the Test tab.
  • For Event name, enter TestGetBooks.
  • Replace the default event JSON with the following:
{
  "httpMethod": "GET"
}
  • Click Save, then click Test.

You should see an Execution result: succeeded banner. The response body should show a 200 status code with an empty list, confirming your function can connect to DynamoDB.

What should I see?

The response should look like this:

{
  "statusCode": 200,
  "headers": {
    "Content-Type": "application/json"
  },
  "body": "[]"
}

The empty list [] is correct because you have not added any books to the table yet.

Seeing an AccessDeniedException?

This means the AmazonDynamoDBFullAccess policy was not attached correctly. Go back to the Configuration tab, click Permissions, open the role in IAM, and confirm both policies appear in the list.

help me fix an AccessDeniedException in my Lambda function

Your serverless backend is live. You have a DynamoDB table ready for data and a Lambda function that can read from it. Next, you will connect an API Gateway in front of this function so it can receive real HTTP requests from the internet.

Create the API Gateway REST API

Your Lambda function is ready to handle book requests, but there is no way for the outside world to reach it yet. You need a public HTTP endpoint that accepts requests and routes them to your function.

API Gateway is that public endpoint. In this step, you will create a REST API that exposes your Lambda function at a /books URL, deploy it to a live stage, and test it by adding and retrieving books.

In this step, get ready to:
  • Create a REST API with a /books resource and GET/POST methods.
  • Deploy the API to a live stage and copy the invoke URL.
  • Test adding and retrieving books through the API.
Create the REST API and configure methods

A REST API in API Gateway is made up of resources (URL paths) and methods (HTTP verbs like GET and POST). You will create one resource called books and attach two methods to it.

  • In the AWS console search bar at the top, type API Gateway and select it from the results.
  • Click Create API.
  • Find the REST API card (not the Private one) and click Build.
  • For API name, enter BookInventoryAPI.
  • Keep API endpoint type set to Regional.
  • Click Create API.

You should see the Resources pane with a root resource (/) already created.

  • Select the root resource /, then click Create resource.
  • For Resource name, enter books.
  • Click Create resource.

You should see /books appear in the Resources tree. Now you will add a GET method to it.

  • Select the /books resource, then click Create method.
  • For Method type, select GET.
  • For Integration type, select Lambda function.
  • Turn on Lambda proxy integration.
  • For Lambda function, type BookInventoryFunction and select it from the dropdown.
  • Click Create method.

What is Lambda proxy integration?

With Lambda proxy integration, API Gateway passes the entire incoming HTTP request directly to your Lambda function. Your function receives the full event (including headers, query parameters, and body) and returns a response in a specific format with statusCode, headers, and body.

This is exactly the format your lambda_function.py already uses, so everything connects without additional mapping.

Now add the POST method using the same approach.

  • With /books still selected, click Create method again.
  • For Method type, select POST.
  • For Integration type, select Lambda function.
  • Turn on Lambda proxy integration.
  • For Lambda function, type BookInventoryFunction and select it from the dropdown.
  • Click Create method.

You should now see both GET and POST listed under the /books resource in the Resources tree.

Don't see your Lambda function in the dropdown?

Make sure you are in the same AWS region where you created BookInventoryFunction. Check the region selector in the top-right corner of the console.

help me find my Lambda function when creating an API Gateway method

Deploy the API to a stage

Your API exists but is not yet accessible over the internet. Deploying it creates a live URL you can send requests to.

  • In the Resources pane, click Deploy API.
  • For Stage, select New stage.
  • For Stage name, enter prod.
  • Click Deploy.

After deployment, you will land on the Stage details page. You should see an Invoke URL displayed at the top.

  • Click the copy icon next to the Invoke URL to copy it.
  • Paste this URL somewhere safe (a text file or sticky note). You will need it in future steps.

What does the invoke URL look like?

Your invoke URL will look something like https://abc123.execute-api.us-east-1.amazonaws.com/prod. To reach your /books resource, you append /books to the end of this URL.

Test the API with requests

Time to confirm everything works end-to-end. You will send a POST request to add a book, then a GET request to retrieve it.

  • In the left navigation pane, click Resources.
  • Select the POST method under /books.
  • Click the Test tab.
  • In the Request body field, paste the following JSON:
{
  "BookId": "1",
  "Title": "The Great Gatsby",
  "Author": "F. Scott Fitzgerald"
}
  • Click Test.

You should see a Status: 201 response with the body {"message": "Book added successfully"}.

  • Now select the GET method under /books.
  • Click the Test tab.
  • Leave the Request body empty.
  • Click Test.

You should see a Status: 200 response. The body should contain a list that includes the book you just added, with BookId, Title, and Author fields.

Seeing a 500 Internal Server Error?

This usually means your Lambda function encountered an error. Check these common causes:

  • The BookInventory DynamoDB table might not exist in the same region. Verify the table exists in the same region as your Lambda function.
  • The execution role might be missing the AmazonDynamoDBFullAccess policy. Return to the Lambda console and check the role's attached policies.
  • The handler might be set incorrectly. Confirm it reads lambda_function.lambda_handler in the Lambda function's runtime settings.

help me debug a 500 error from my API Gateway Lambda integration

Your API is live and working. Next up, you will enable X-Ray tracing on both Lambda and API Gateway to see exactly how requests flow through your serverless architecture.

Enable X-Ray Tracing and Generate Traffic

Your API is live and working, but right now you have zero visibility into what happens between when a request arrives and when it returns a response.

In this step, you will enable AWS X-Ray tracing on both your Lambda function and API Gateway stage. Then you will send a burst of requests to generate trace data that X-Ray can visualize.

In this step, get ready to:
  • Enable X-Ray active tracing on your Lambda function.
  • Enable X-Ray tracing on your API Gateway stage.
  • Generate traffic to produce trace data.
Enable active tracing on Lambda

Active tracing tells Lambda to run the X-Ray daemon alongside your function. The daemon collects trace segments from your code (including the patch_all() call you already have) and sends them to the X-Ray service.

  • Return to the Lambda console and select your BookInventoryFunction function.
  • Click the Configuration tab.
  • In the left navigation, click Monitoring and operations tools.
  • Click Edit.
  • Under AWS X-Ray, toggle Active tracing to enabled.
  • Click Save.

What does active tracing do?

When active tracing is enabled, Lambda automatically runs an X-Ray daemon process alongside your function. This daemon receives trace segments from the X-Ray SDK in your code and forwards them to the X-Ray service.

Your function code already imports xray_recorder and calls patch_all(), which instruments all boto3 calls (like DynamoDB). Active tracing is the switch that makes Lambda actually collect and ship those traces.

You should see a green success banner confirming that the configuration was saved.

Don't see the Active tracing option?

Make sure you are in the Configuration tab (not the Code tab), and that you clicked Monitoring and operations tools in the left sidebar. The option only appears after clicking Edit.

help me find the X-Ray active tracing option in my Lambda function configuration

Enable X-Ray tracing on API Gateway

Enabling tracing on the API Gateway stage ensures the full request path is captured. Without this, you would only see the Lambda portion of each trace.

  • Open the API Gateway console.
  • Select your BookInventoryAPI.
  • In the left sidebar, click Stages.
  • Select the prod stage.
  • Click the Logs and tracing tab (or section).
  • Click Edit.
  • Check the box to enable X-Ray tracing.
  • Click Save changes.

Why enable tracing on both services?

API Gateway creates the first trace segment for each incoming request. Lambda then adds its own segments showing function initialization and execution. By enabling tracing on both, you get an end-to-end view of the entire request path.

API Gateway also adds the X-Amzn-Trace-Id header to incoming requests that don't already have one. This header ties all the segments from different services together into a single trace.

You should see a confirmation that the stage settings were saved successfully.

Don't see the X-Ray tracing option?

X-Ray tracing is only available for REST APIs, not HTTP APIs. Confirm your API type is REST in the API Gateway console. Also make sure you selected the prod stage before looking for the Logs and tracing section.

help me enable X-Ray tracing on my API Gateway REST API stage

Generate traffic to create trace data

X-Ray needs actual requests to create traces. You will send a mix of POST and GET requests to give X-Ray varied data to display.

  • Open PowerShell on your machine.
  • Store your API invoke URL in a variable by running this command:
$api = "[[API_INVOKE_URL="your invoke URL from API Gateway e.g. https://abc123.execute-api.us-east-1.amazonaws.com/prod"]]/books"

Where do I find my invoke URL?

Go to API Gateway, select BookInventoryAPI, click Stages, then select prod. The invoke URL is displayed at the top of the stage editor. Append /books to it.

  • Add a few books by running these POST requests:
# Add first book
$body1 = '{"BookId": "101", "Title": "The Great Gatsby", "Author": "F. Scott Fitzgerald"}'
Invoke-RestMethod -Uri $api -Method Post -Body $body1 -ContentType "application/json"

# Add second book
$body2 = '{"BookId": "102", "Title": "To Kill a Mockingbird", "Author": "Harper Lee"}'
Invoke-RestMethod -Uri $api -Method Post -Body $body2 -ContentType "application/json"

# Add third book
$body3 = '{"BookId": "103", "Title": "1984", "Author": "George Orwell"}'
Invoke-RestMethod -Uri $api -Method Post -Body $body3 -ContentType "application/json"

What does this code do?

Each command sends an HTTP POST request to your API's /books endpoint with a JSON body containing a BookId, Title, and Author. Your Lambda function writes each book to the BookInventory DynamoDB table.

You should see message : Book added successfully printed after each command.

  • Retrieve all books by running several GET requests:
# Send multiple GET requests to generate more traces
Invoke-RestMethod -Uri $api -Method Get
Invoke-RestMethod -Uri $api -Method Get
Invoke-RestMethod -Uri $api -Method Get
Invoke-RestMethod -Uri $api -Method Get
Invoke-RestMethod -Uri $api -Method Get

What does this code do?

Each GET request triggers a Scan operation on DynamoDB and returns all books in the table. Running five GET requests gives X-Ray enough traces to display meaningful latency data in the service map.

You should see a list of your books returned after each GET request.

  • Wait 30 seconds for the trace data to propagate to X-Ray before moving on.

More requests give richer data

X-Ray uses sampling to control trace volume. By default, it records the first request each second and five percent of additional requests. Sending a burst of requests ensures several traces are captured for you to explore.

Getting an error from your requests?

If you see a 500 Internal Server Error, double-check that your $api variable ends with /books. The full URL should look like https://abc123.execute-api.us-east-1.amazonaws.com/prod/books.

If you see a 403 Forbidden error, your stage may not be deployed after adding the tracing change. Return to API Gateway and redeploy the API to the prod stage.

help me debug errors when calling my API Gateway endpoint from PowerShell

Your API is now fully instrumented with X-Ray tracing. Next up, you will open the X-Ray console and explore the service map and individual traces to see exactly how your requests flow through each service.

Explore Traces in the X-Ray Console

You have a fully instrumented serverless API generating trace data across three AWS services. Now it is time to see what all that data looks like.

In this step, you will open the X-Ray console inside CloudWatch and read the service map, individual trace timelines, and segment details to understand exactly where time is spent in each request.

In this step, get ready to:
  • View the X-Ray service map showing API Gateway, Lambda, and DynamoDB connected together.
  • Drill into an individual trace and read the Segments Timeline.
  • Compare a GET trace against a POST trace to identify latency differences.
Open the X-Ray trace map in CloudWatch

The X-Ray trace map is a visual representation of every service involved in handling your API requests. Each service appears as a node, and the connections between them show the request flow.

  • In the AWS Management Console, search for CloudWatch and open the CloudWatch console.
  • In the left navigation pane, expand the X-Ray traces section.
  • Click Trace Map.

You should see a map with nodes representing your services connected by arrows showing the request path. Look for three main nodes: your API Gateway stage, your Lambda function, and the DynamoDB table.

What do the nodes and edges represent?

Each circle (node) is a service that processed part of your request. The lines (edges) between them show the direction of the call.

Clicking a node shows a latency histogram for that service. Clicking an edge shows latency including network time between the two services. The average response time displayed on each node tells you how long that service takes to do its work.

Why does Lambda show two nodes?

Lambda functions appear as two nodes on the trace map. One represents the Lambda container (the execution environment), and the other represents your function code. This separation helps you identify cold start delays versus actual function execution time.

Don't see any nodes on the map?

Make sure you are in the same AWS region where you created your Lambda function and API. Check the region selector in the top-right corner of the console.

If you just sent your requests, wait another 30 seconds. X-Ray trace data can take up to a minute to appear after the requests complete.

help me troubleshoot why my X-Ray trace map is empty even though I enabled tracing on both Lambda and API Gateway

Examine an individual trace

The service map shows you the big picture. Now you will drill into a single request to see exactly how time is distributed across each service.

  • In the left navigation pane under X-Ray traces, click Traces.
  • Click Run query to display a list of recent traces in the Traces section at the bottom of the page.
  • Click any trace ID in the list to open the Trace details page.

The Trace details page has three main sections: a Trace map (showing the path of this specific request), a Trace summary (response code, total duration, and age), and the Segments Timeline.

  • Scroll down to the Segments Timeline.
  • Observe the hierarchy of segments and subsegments listed with horizontal bars showing their duration.
  • Note the Duration column showing how long each segment ran.

How do I read the Segments Timeline?

The timeline shows a hierarchy. Segments are at the top level (one per service), and subsegments are indented beneath them. The horizontal bars show relative timing so you can see what ran in parallel and what ran sequentially.

For your Book Inventory API, you should see segments for API Gateway and the Lambda function. Under the Lambda segment, look for a DynamoDB subsegment showing the downstream call your function made. This subsegment is how X-Ray creates the DynamoDB node on the service map.

Not seeing DynamoDB in the timeline?

Make sure your Lambda function code includes patch_all() at the top. This is what instruments your boto3 calls so they appear as subsegments in the trace.

If you uploaded your deployment package without the aws-xray-sdk dependency, your function will fail or skip instrumentation. Check the Lambda logs in CloudWatch for import errors.

help me figure out why DynamoDB does not appear as a subsegment in my X-Ray trace

Compare a GET trace vs a POST trace

Different API operations interact with DynamoDB differently. Comparing traces helps you identify which operations are more expensive.

  • Click Traces in the left navigation pane to return to the traces list.
  • Click Run query again to refresh the list.
  • Select a trace that corresponds to a GET request (look at the URL or method column if visible, or open a trace and check the Trace summary).
  • Scroll to the Segments Timeline and note the DynamoDB subsegment. For GET requests, this shows a Scan operation.
  • Go back to the traces list and select a trace that corresponds to a POST request.
  • In this trace, the DynamoDB subsegment shows a PutItem operation.

Notice how the Scan operation (GET) typically takes longer than the PutItem operation (POST) because it reads all items in the table.

Why does Scan take longer than PutItem?

A Scan reads every item in the table and returns them all. As your table grows, this operation gets slower. A PutItem writes a single record, so its duration stays constant regardless of table size.

This is exactly the kind of insight distributed tracing gives you. In a production system, seeing that Scan dominates your response time would tell you to add pagination or switch to a Query operation with a secondary index.

What is X-Ray's default sampling?

X-Ray does not trace every single request by default. It records the first request each second, and five percent of any additional requests. This means some of your requests might not appear in the traces list, which is normal.

Secret mission

Add Custom Annotations and a Sampling Rule

Want more control over your traces? In this mission, you'll add custom annotations that make your traces searchable by operation type. Then you'll create a sampling rule to control how many requests get traced.

Clean Up Your Resources

Clean Up Your Resources

Decide whether to keep your resources running, pause them to come back later, or delete them entirely. All resources in this project fall within the AWS free tier for light usage, so there are no ongoing costs as long as you stay within the free tier limits.

Resources you used:

  • DynamoDB table BookInventory (free tier: 25 GB storage).
  • Lambda function BookInventoryFunction (free tier: 1M requests/month).
  • API Gateway REST API BookInventoryAPI (free tier: 1M API calls/month for first 12 months).
  • IAM execution role with AmazonDynamoDBFullAccess and AWSXRayDaemonWriteAccess policies attached.
  • X-Ray sampling rule BookAPIRule (if you completed the Secret Mission).
  • Local project folder xray-book-api on your Desktop.

Keep everything running

No action needed. Choose this if you are still actively building or experimenting with your API and X-Ray traces.

  • All resources stay within the AWS free tier for light usage.
  • X-Ray traces automatically expire after 30 days.
  • Your local xray-book-api folder remains on your Desktop.

Pause - I'll come back to this later

No action needed for serverless resources. These resources only cost money when invoked, so they are effectively paused when you stop sending requests.

  • Lambda, API Gateway, and DynamoDB incur no charges when idle within free tier limits.
  • X-Ray traces automatically expire after 30 days.
  • Your project folder and deployment package remain on your local machine for future use.

Delete - I don't want to use this again

Remove all project resources to start fresh. Follow these steps in order.

Delete the API Gateway REST API:

  • Open the API Gateway console.
  • On the APIs page, select BookInventoryAPI.
  • Choose Actions, then Delete.
  • Confirm the deletion when prompted.

Delete the Lambda function:

  • Open the Lambda console and go to the Functions page.
  • Select BookInventoryFunction.
  • Choose Actions, then Delete.
  • Type confirm in the confirmation field and choose Delete.

Delete the DynamoDB table:

  • Open the DynamoDB console.
  • In the left navigation pane, choose Tables.
  • Select the checkbox next to BookInventory.
  • Choose Delete.
  • Enter delete in the confirmation dialog and choose Delete table.

Delete the IAM execution role:

  • Open the IAM console.
  • In the navigation pane, choose Roles.
  • Select the checkbox next to the execution role created for BookInventoryFunction.
  • Choose Delete at the top of the page.
  • Type the role name in the confirmation field and choose Delete.

Delete the X-Ray sampling rule (if you completed the Secret Mission):

  • Open the CloudWatch console.
  • In the left navigation, expand X-Ray traces and choose Sampling rules.
  • Select BookAPIRule and choose Delete.
  • Confirm the deletion.

Delete the local project folder:

  • Open PowerShell and run the following command to remove the project folder from your Desktop:
Remove-Item -Recurse -Force ~/Desktop/xray-book-api

What about X-Ray traces?

X-Ray traces cannot be manually deleted. They automatically expire after 30 days. No action is required on your part.

That's a Wrap!

That's a Wrap!

Nice work! You just built a fully traced serverless API and used AWS X-Ray to see exactly how requests flow through your distributed system.

You've learned how to:

  • Built a serverless REST API using API Gateway, Lambda, and DynamoDB that stores and retrieves book records.
  • Instrumented a Python Lambda function with the AWS X-Ray SDK to automatically trace downstream DynamoDB calls.
  • Read the X-Ray service map and drilled into individual trace segments to identify latency at each stage of a request.
  • Secret Mission: Added custom annotations to traces for searchable metadata and created a sampling rule to control trace volume.

Ready to quiz yourself?