Build an AI-Powered Translation API

Deploy a Python translation microservice using AWS Bedrock and Amazon Translate.

Introduction

30 Second Summary

Every major app you use daily has translation built in. Behind the scenes, those translations rely on APIs that combine machine learning with careful engineering to produce natural-sounding results.

In this project, you will build an AI-powered translation API that combines two AWS services for high-quality translations. You will also deploy it with a fully automated CI/CD pipeline so every code change goes live without manual steps.

What You'll Build

A Python microservice that performs two-stage translation using Amazon Translate and AWS Bedrock, deployed to AWS Lambda with automated deployments via GitHub Actions.

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

  • A FastAPI application with a /translate endpoint that uses Amazon Translate for raw translation and AWS Bedrock (Amazon Nova Micro) for natural-sounding refinement.
  • An Infrastructure as Code template using AWS SAM that defines your entire serverless architecture in a single YAML file.
  • A complete CI/CD pipeline using GitHub Actions that automatically builds and deploys your API on every push to main.
  • Secret Mission: Add a batch translation endpoint that processes multiple texts concurrently with graceful error handling.

Are there any prerequisites?

You need an AWS account (free tier works), a GitHub account, Python 3.12, Git, and the AWS CLI installed and configured. You will install the AWS SAM CLI in Step 1.

Set Up Your Development Environment

Before you can build your translation API, you need the right tools in place. AWS SAM CLI is the tool that packages and deploys your serverless application to AWS.

In this step, you will install the SAM CLI, create your project structure, and connect it to a GitHub repository so your code is ready for the CI/CD pipeline later.

In this step, get ready to:
  • Install AWS SAM CLI and verify it works alongside your existing AWS CLI.
  • Create the project directory structure with a FastAPI application folder and requirements file.
  • Initialize a Git repository and push your code to GitHub.
Install AWS SAM CLI

The AWS SAM CLI lets you build, test, and deploy serverless applications defined in SAM templates. It works alongside the AWS CLI you already have configured.

  • Open your terminal.
  • Download the SAM CLI installer by running this command:
wget https://github.com/aws/aws-sam-cli/releases/latest/download/aws-sam-cli-linux-x86_64.zip
  • Unzip the installer into a temporary directory by running:
unzip aws-sam-cli-linux-x86_64.zip -d sam-installation
  • Run the installer by running:
sudo ./sam-installation/install
  • Verify the installation by running:
sam --version

You should see output like SAM CLI, version 1.x.x confirming the installation succeeded.

What is AWS SAM CLI?

SAM stands for Serverless Application Model. The SAM CLI lets you define your infrastructure (Lambda functions, API Gateways, IAM roles) in a YAML template and deploy everything with a single command.

It works on top of CloudFormation, so your infrastructure is versioned and reproducible.

  • Also confirm your AWS CLI is configured correctly by running:
aws sts get-caller-identity

You should see your AWS account ID, user ARN, and user ID printed in JSON format.

sam: command not found?

If the sam --version command is not recognized, the installer may not have added it to your PATH. Try opening a new terminal session or running which sam to check if the symlink was created at /usr/local/bin/sam.

help me troubleshoot why sam command is not found after installing AWS SAM CLI on Linux

Create the project structure

AWS SAM projects follow a specific layout. The SAM template sits at the project root, and your application code lives in a subdirectory that SAM packages for deployment.

  • Move to your Desktop by running this command:
cd ~/Desktop
  • Create your project folder and move into it by running these commands:
mkdir translation-api
cd translation-api
  • Create the application subdirectory where your FastAPI code will live:
mkdir translate_api
  • Confirm the directory structure by running:
ls

You should see translate_api listed in the output.

  • Create the requirements file inside the application directory by running:
cat > translate_api/requirements.txt << 'EOF'
fastapi==0.136.1
mangum==0.19.0
pydantic==2.11.1
boto3==1.38.0
EOF

What are these dependencies?

  • fastapi is the web framework for building your REST API with automatic validation and documentation.
  • mangum wraps your FastAPI app so it can run inside AWS Lambda.
  • pydantic handles request and response data validation using Python type hints.
  • boto3 is the AWS SDK for Python, used to call Amazon Translate and Bedrock.
  • Verify the file was created correctly by running:
cat translate_api/requirements.txt

You should see all four pinned dependencies listed with their version numbers.

✔️ Awesome, I've got everything!

Great. Make sure you are still inside the translation-api directory before continuing.

ⓧ I'd like to double check the full code

Here is the complete requirements.txt file:

fastapi==0.136.1
mangum==0.19.0
pydantic==2.11.1
boto3==1.38.0

Seeing a "No such file or directory" error?

Make sure you created the translate_api directory first. Run ls to confirm you are inside the translation-api folder and that translate_api appears in the listing.

help me fix a file path error when creating requirements.txt in my SAM project

Initialize Git and push to GitHub

Your project needs version control from the start. The GitHub Actions CI/CD pipeline you build later triggers on pushes to your repository.

  • Initialize a Git repository in your project folder by running:
git init
  • Stage and commit the requirements file by running these commands:
git add .
git commit -m "Initial project structure with requirements"
  • Open your browser and go to https://github.com/new to create a new repository.
  • Enter translation-api in the Repository name field.
  • Leave the repository set to Public.
  • Do NOT check any boxes for README, .gitignore, or license (you already have a local repo).
  • Click Create repository.

GitHub shows a page with setup commands. You will use the "push an existing repository" commands.

  • Connect your local repo to GitHub and push by running these commands:
git branch -M main
git remote add origin https://github.com/[[GITHUB_USERNAME="your-github-username"]]/translation-api.git
git push -u origin main

What do these commands do?

  • git branch -M main renames your default branch to main (matching GitHub's default).
  • git remote add origin links your local repository to the GitHub repository you just created.
  • git push -u origin main uploads your code and sets main as the default upstream branch for future pushes.
  • Refresh your GitHub repository page in the browser.

You should see your translate_api folder and requirements.txt file listed in the repository.

Authentication failed when pushing?

GitHub no longer accepts passwords for Git operations. You need a Personal Access Token (PAT) or SSH key configured. Go to GitHub Settings > Developer settings > Personal access tokens > Tokens (classic) and generate a token with repo scope. Use this token as your password when prompted.

help me authenticate with GitHub from the command line so I can push my code

Your development environment is ready. Next up, you will write the actual translation API using FastAPI and connect it to AWS AI services.

Build the Translation API with FastAPI

Your project structure is ready with the SAM directory layout and dependencies file in place. Now it's time to write the actual translation API.

Your API uses a two-stage translation approach. First, Amazon Translate performs fast neural machine translation. Then, AWS Bedrock refines that translation for natural tone and fluency using a large language model.

In this step, get ready to:
  • Write the FastAPI application with Pydantic request and response models.
  • Implement the Amazon Translate integration for raw neural translation.
  • Implement the AWS Bedrock integration using Amazon Nova Micro for translation refinement.
Set up the FastAPI application and data models

FastAPI is a modern Python web framework designed for building APIs. It uses Pydantic models to validate incoming requests and shape outgoing responses automatically.

  • Install the project dependencies by running this command from your project root directory:
pip install -r translate_api/requirements.txt

Seeing permission errors?

If you see a permissions error, try running pip install --user -r translate_api/requirements.txt instead. Make sure you are using Python 3.12's pip.

Why is pip install failing for me?

  • Create a new file called app.py inside the translate_api/ folder.
  • Add the following code to set up the application foundation:
import json
import boto3
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from mangum import Mangum

# Create the FastAPI app instance
app = FastAPI(title="AI Translation API")

# Initialize AWS service clients
translate_client = boto3.client("translate", region_name="us-east-1")
bedrock_client = boto3.client("bedrock-runtime", region_name="us-east-1")

# Model ID for Amazon Nova Micro on Bedrock
NOVA_MICRO_MODEL_ID = "amazon.nova-micro-v1:0"


# Request schema: what the client sends to our API
class TranslateRequest(BaseModel):
    text: str
    source_language: str
    target_language: str


# Response schema: what our API sends back
class TranslateResponse(BaseModel):
    original_text: str
    raw_translation: str
    refined_translation: str
    source_language: str
    target_language: str

What does this code do?

This sets up the building blocks of your API.

  • app = FastAPI() creates the core application object that handles all incoming HTTP requests.
  • translate_client and bedrock_client are boto3 clients that let your code talk to AWS services.
  • TranslateRequest defines the shape of incoming data. FastAPI automatically rejects requests that don't match this schema.
  • TranslateResponse defines the shape of outgoing data. It includes both the raw machine translation and the refined version so the client can compare them.

Why FastAPI instead of Flask?

FastAPI has built-in request validation through Pydantic, automatic API documentation, and async support. Flask is simpler but requires manual validation and extra libraries for what FastAPI provides out of the box.

  • Save translate_api/app.py.
  • Verify the file has no syntax errors by running:
python -c "import ast; ast.parse(open('translate_api/app.py').read()); print('Syntax OK')"

You should see Syntax OK printed in your terminal.

Seeing a SyntaxError?

Check that you copied all closing parentheses and colons. Python syntax errors usually point to the line after the actual problem.

Help me find the syntax error in my app.py

Implement the Amazon Translate function

Amazon Translate is a neural machine translation service. It handles the heavy lifting of converting text between languages. Your function wraps the AWS API call into a clean, reusable helper.

  • In translate_api/app.py, add the following function below the TranslateResponse class:
def translate_with_aws(text: str, source_lang: str, target_lang: str) -> str:
    """Perform initial translation using Amazon Translate."""
    response = translate_client.translate_text(
        Text=text,
        SourceLanguageCode=source_lang,
        TargetLanguageCode=target_lang,
    )
    return response["TranslatedText"]

What does this function do?

  • translate_text() sends your text to Amazon Translate with the source and target language codes (like en for English, es for Spanish).
  • The response dictionary contains a TranslatedText key with the machine-translated output.
  • Save app.py.

You won't see a change yet. The next function will use this translation as input for refinement.

Add the Bedrock refinement and API routes

Machine translation is fast but can sound robotic. Amazon Nova Micro is a lightweight LLM on AWS Bedrock that costs $0.000035 per 1,000 input tokens. It takes the raw translation and polishes it for natural tone and fluency.

  • In app.py, add the Bedrock refinement function below translate_with_aws:
def refine_with_bedrock(
    original_text: str,
    raw_translation: str,
    source_lang: str,
    target_lang: str,
) -> str:
    """Refine translation using AWS Bedrock Nova Micro for natural tone."""
    # Build a prompt that gives the LLM context about what to refine
    prompt = (
        f"You are a professional translator. Below is a machine translation "
        f"from {source_lang} to {target_lang}. Please refine it for natural "
        f"tone, fluency, and accuracy. Return ONLY the refined translation, "
        f"nothing else.\n\n"
        f"Original ({source_lang}): {original_text}\n"
        f"Machine translation ({target_lang}): {raw_translation}\n\n"
        f"Refined translation ({target_lang}):"
    )

    # Structure the request using Nova Micro's messages format
    request_body = {
        "messages": [
            {
                "role": "user",
                "content": [{"text": prompt}],
            }
        ],
        "inferenceConfig": {
            "maxTokens": 1024,
            "temperature": 0.3,
        },
    }

    # Send the request to Bedrock
    response = bedrock_client.invoke_model(
        modelId=NOVA_MICRO_MODEL_ID,
        body=json.dumps(request_body),
    )

    # Parse the response and extract the refined text
    response_body = json.loads(response["body"].read())
    content_list = response_body["output"]["message"]["content"]
    text_block = next((item for item in content_list if "text" in item), None)
    if text_block is None:
        return raw_translation
    return text_block["text"].strip()

What does this function do?

  • The prompt gives Nova Micro both the original text and the machine translation, asking it to refine only the translation output.
  • request_body follows Nova Micro's native format: a messages array with role and content, plus inference configuration for token limit and creativity level.
  • invoke_model() sends the request to Bedrock. The response body contains a nested content list where the refined text lives.
  • The fallback (if text_block is None) returns the raw translation if Bedrock returns an unexpected response format.
  • Save app.py.

You won't see a change yet. The next chunk wires both functions into the API route.

Now you need the actual API endpoints that clients will call. You also need Mangum to wrap your FastAPI app so it can run inside AWS Lambda.

  • Add the following routes and handler at the bottom of app.py, below the refine_with_bedrock function:
# Health check endpoint for monitoring
@app.get("/")
def health_check():
    return {"status": "healthy", "service": "AI Translation API"}


# Main translation endpoint
@app.post("/translate", response_model=TranslateResponse)
def translate(request: TranslateRequest):
    # Stage 1: Raw machine translation
    try:
        raw_translation = translate_with_aws(
            request.text, request.source_language, request.target_language
        )
    except Exception as e:
        raise HTTPException(
            status_code=500, detail=f"Translation failed: {str(e)}"
        )

    # Stage 2: LLM refinement (graceful fallback if Bedrock fails)
    try:
        refined_translation = refine_with_bedrock(
            request.text,
            raw_translation,
            request.source_language,
            request.target_language,
        )
    except Exception as e:
        refined_translation = raw_translation

    return TranslateResponse(
        original_text=request.text,
        raw_translation=raw_translation,
        refined_translation=refined_translation,
        source_language=request.source_language,
        target_language=request.target_language,
    )


# Mangum wraps FastAPI so Lambda can invoke it
handler = Mangum(app, lifespan="off")

What does this code do?

  • The GET / health check endpoint lets you quickly confirm the API is running.
  • The POST /translate endpoint orchestrates both stages. If Amazon Translate fails, the request returns a 500 error. If Bedrock fails, it gracefully falls back to the raw translation instead of crashing.
  • handler = Mangum(app, lifespan="off") creates the Lambda entry point. When Lambda receives a request, it calls this handler, which routes it through FastAPI.
  • Save app.py.
  • Verify the complete file loads correctly by running:
cd translate_api && python -c "from app import app; print(app.title)" && cd ..

You should see AI Translation API printed in your terminal. This confirms all imports resolve and the app object is created successfully.

Seeing an ImportError or ModuleNotFoundError?

  • If you see ModuleNotFoundError: No module named 'fastapi', re-run pip install -r translate_api/requirements.txt from your project root.
  • Make sure you are running the command from the project root directory, not from inside the translate_api folder.
  • Check that your Python version is 3.12 by running python --version.

Why is my app.py failing to import?

✔️ Awesome, I've got everything!

Great. Double check you have saved your file before moving on.

ⓧ I'd like to double check the full code

Here is the complete translate_api/app.py file. Compare it with yours and fix any differences.

import json
import boto3
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from mangum import Mangum

app = FastAPI(title="AI Translation API")

translate_client = boto3.client("translate", region_name="us-east-1")
bedrock_client = boto3.client("bedrock-runtime", region_name="us-east-1")

NOVA_MICRO_MODEL_ID = "amazon.nova-micro-v1:0"


class TranslateRequest(BaseModel):
    text: str
    source_language: str
    target_language: str


class TranslateResponse(BaseModel):
    original_text: str
    raw_translation: str
    refined_translation: str
    source_language: str
    target_language: str


def translate_with_aws(text: str, source_lang: str, target_lang: str) -> str:
    """Perform initial translation using Amazon Translate."""
    response = translate_client.translate_text(
        Text=text,
        SourceLanguageCode=source_lang,
        TargetLanguageCode=target_lang,
    )
    return response["TranslatedText"]


def refine_with_bedrock(
    original_text: str,
    raw_translation: str,
    source_lang: str,
    target_lang: str,
) -> str:
    """Refine translation using AWS Bedrock Nova Micro for natural tone."""
    prompt = (
        f"You are a professional translator. Below is a machine translation "
        f"from {source_lang} to {target_lang}. Please refine it for natural "
        f"tone, fluency, and accuracy. Return ONLY the refined translation, "
        f"nothing else.\n\n"
        f"Original ({source_lang}): {original_text}\n"
        f"Machine translation ({target_lang}): {raw_translation}\n\n"
        f"Refined translation ({target_lang}):"
    )

    request_body = {
        "messages": [
            {
                "role": "user",
                "content": [{"text": prompt}],
            }
        ],
        "inferenceConfig": {
            "maxTokens": 1024,
            "temperature": 0.3,
        },
    }

    response = bedrock_client.invoke_model(
        modelId=NOVA_MICRO_MODEL_ID,
        body=json.dumps(request_body),
    )

    response_body = json.loads(response["body"].read())
    content_list = response_body["output"]["message"]["content"]
    text_block = next((item for item in content_list if "text" in item), None)
    if text_block is None:
        return raw_translation
    return text_block["text"].strip()


@app.get("/")
def health_check():
    return {"status": "healthy", "service": "AI Translation API"}


@app.post("/translate", response_model=TranslateResponse)
def translate(request: TranslateRequest):
    try:
        raw_translation = translate_with_aws(
            request.text, request.source_language, request.target_language
        )
    except Exception as e:
        raise HTTPException(
            status_code=500, detail=f"Translation failed: {str(e)}"
        )

    try:
        refined_translation = refine_with_bedrock(
            request.text,
            raw_translation,
            request.source_language,
            request.target_language,
        )
    except Exception as e:
        refined_translation = raw_translation

    return TranslateResponse(
        original_text=request.text,
        raw_translation=raw_translation,
        refined_translation=refined_translation,
        source_language=request.source_language,
        target_language=request.target_language,
    )


handler = Mangum(app, lifespan="off")

Your translation API code is complete. Next up, you'll define the serverless infrastructure with AWS SAM and test the API locally before deploying to the cloud.

Configure the Serverless Infrastructure with AWS SAM

You have a working FastAPI application, but it only exists as Python files on your machine. To run it on AWS Lambda, you need a way to describe your cloud resources in code.

This is where Infrastructure as Code comes in. You will write a single YAML file that defines your Lambda function, API Gateway, and IAM permissions. AWS SAM reads this file and creates everything for you automatically.

In this step, get ready to:
  • Write a SAM template defining your Lambda function, API Gateway, and IAM policies.
  • Understand how the Mangum adapter bridges FastAPI and Lambda.
  • Test the API locally using SAM before deploying to AWS.
Write the SAM template

The SAM template is a YAML file called template.yaml that lives in your project root. It tells AWS exactly what resources to create and how to wire them together.

  • Create a file called template.yaml in the root of your translation-api folder (not inside translate_api/), then paste the following code:
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AI-Powered Translation API with Bedrock and Translate

Globals:
  Function:
    Timeout: 30
    MemorySize: 256
    Runtime: python3.12
    Architectures:
      - x86_64

Resources:
  TranslateFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: translate_api/
      Handler: app.handler
      Events:
        AnyApi:
          Type: HttpApi
          Properties:
            Path: /{proxy+}
            Method: ANY
        RootApi:
          Type: HttpApi
          Properties:
            Path: /
            Method: ANY
      Policies:
        - Statement:
            - Effect: Allow
              Action:
                - translate:TranslateText
              Resource: "*"
            - Effect: Allow
              Action:
                - bedrock:InvokeModel
              Resource: "*"

Outputs:
  ApiUrl:
    Description: API Gateway endpoint URL
    Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/"

What does this template do?

  • Globals sets defaults for all functions: a 30-second timeout, 256 MB of memory, and the python3.12 runtime.
  • TranslateFunction defines your Lambda function. CodeUri: translate_api/ points SAM at your application code. Handler: app.handler tells Lambda to call the handler variable in app.py (your Mangum wrapper).
  • The two HttpApi events create an API Gateway HTTP API. One catches the root path / and the other catches all other paths via /{proxy+}.
  • The Policies section grants your function permission to call Amazon Translate and AWS Bedrock.
  • Save template.yaml.
  • Confirm the file exists by running:
ls template.yaml

You should see template.yaml in the output.

Not seeing template.yaml?

Make sure you created the file in the translation-api root folder, not inside the translate_api/ subdirectory. Your terminal must also be in the translation-api directory when you run ls.

help me find where I saved my template.yaml file

Why SAM over other IaC tools?

AWS SAM is purpose-built for serverless applications. It extends CloudFormation with shorthand syntax that turns 50+ lines of raw CloudFormation into the concise template you just wrote.

Alternatives like the CDK or Terraform are more powerful for large infrastructure, but SAM gives you the fastest path from code to a working Lambda API with minimal boilerplate.

Understand the Mangum adapter

Lambda expects a specific function signature to handle incoming events. FastAPI uses a different protocol called ASGI. The Mangum adapter bridges these two worlds.

  • Open translate_api/app.py and find the last line:
handler = Mangum(app, lifespan="off")

What does this line do?

This line creates the handler variable that your SAM template references via Handler: app.handler. When Lambda receives an API Gateway event, it calls this handler function.

Mangum translates the Lambda event into a format FastAPI understands, runs your route logic, then converts FastAPI's response back into a format API Gateway expects. The lifespan="off" parameter disables ASGI lifespan events that are not supported in Lambda.

You do not need to change anything here. This line was already added when you built the application in the previous step.

Test the application locally with SAM

Before deploying to AWS, you can run your entire API locally using SAM's built-in emulator. This spins up a local API Gateway and Lambda container so you can test your endpoints without spending any money.

Docker is required for local testing

The sam local start-api command uses Docker to simulate the Lambda environment. You must have Docker installed and running on your machine.

If you do not have Docker installed, you can skip local testing and deploy directly to AWS in the next step. The deployment will still work without local testing.

  • Build the application by running:
sam build

What does sam build do?

This command reads your template.yaml, installs the Python dependencies from requirements.txt, and packages everything into a .aws-sam/build directory. This prepared package is what gets deployed to Lambda or tested locally.

  • You should see Build Succeeded in the output.
  • Start the local API server by running:
sam local start-api

You should see output showing your function mounted at http://127.0.0.1:3000 with a message saying the server is running.

  • Open a new terminal window and send a test request by running:
curl -X POST http://localhost:3000/translate \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, how are you?", "source_language": "en", "target_language": "fr"}'

You should see a JSON response containing original_text, raw_translation, and refined_translation fields.

What if the Bedrock call fails locally?

If you have not yet enabled Amazon Nova Micro in the Bedrock console, the refinement step will fail. Your API is designed to handle this gracefully. The refined_translation field will fall back to the same value as raw_translation. You will enable Bedrock model access in the next step.

  • Go back to the terminal running the local server and press Ctrl+C to stop it.

Seeing 'Running AWS SAM projects locally requires Docker'?

This error means Docker is not installed or not running. Start Docker Desktop (or the Docker daemon on Linux), wait for it to fully load, then try sam local start-api again.

If you choose not to install Docker, you can skip local testing entirely. Your code will still deploy correctly to AWS in the next step.

help me troubleshoot sam local start-api Docker errors

✔️ Awesome, I've got everything!

Great. Double check you have saved template.yaml in your project root.

ⓧ I'd like to double check the full code

Here is the complete template.yaml file:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: AI-Powered Translation API with Bedrock and Translate

Globals:
  Function:
    Timeout: 30
    MemorySize: 256
    Runtime: python3.12
    Architectures:
      - x86_64

Resources:
  TranslateFunction:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: translate_api/
      Handler: app.handler
      Events:
        AnyApi:
          Type: HttpApi
          Properties:
            Path: /{proxy+}
            Method: ANY
        RootApi:
          Type: HttpApi
          Properties:
            Path: /
            Method: ANY
      Policies:
        - Statement:
            - Effect: Allow
              Action:
                - translate:TranslateText
              Resource: "*"
            - Effect: Allow
              Action:
                - bedrock:InvokeModel
              Resource: "*"

Outputs:
  ApiUrl:
    Description: API Gateway endpoint URL
    Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/"

Your infrastructure is defined and tested locally. Next up, you will deploy this stack to AWS and test your live API endpoint.

Deploy to AWS and Test Your Live API

In the previous step, you defined your entire serverless infrastructure in a single SAM template and verified it works locally.

Now it's time to push your API live. You'll deploy your stack to AWS, enable the AI model your API depends on, and make your first real translation request to a public endpoint.

In this step, get ready to:
  • Build and deploy your SAM stack to AWS.
  • Enable Amazon Nova Micro model access in the Bedrock console.
  • Test your live translation API with curl.
Build and deploy your stack to AWS

SAM deployment is a two-stage process. First, sam build packages your Python code and dependencies into a deployment-ready artifact. Then, sam deploy --guided creates a CloudFormation stack containing your Lambda function, API Gateway, and IAM roles.

  • Make sure you are inside your translation-api project folder, then build the project by running:
sam build

You should see output ending with Build Succeeded and a message about your build artifacts being in .aws-sam/build.

Build failed?

If you see a build error related to Python, confirm your Python version matches python3.12 by running python3 --version. The runtime in your template.yaml must match the Python version installed on your machine.

Help me debug my sam build failure

  • Deploy the stack to AWS for the first time by running:
sam deploy --guided

What does --guided do?

The --guided flag walks you through deployment settings interactively. It asks for your stack name, region, and confirmation preferences. SAM saves your answers to a samconfig.toml file so future deploys skip the prompts.

SAM will ask you a series of questions. Enter the following values:

  • Stack Name: enter translation-api.
  • AWS Region: enter us-east-1.
  • Confirm changes before deploy: enter y.
  • Allow SAM CLI IAM role creation: enter Y.
  • Disable rollback: enter N.
  • TranslateFunction has no authentication. Is this okay?: enter y.
  • Save arguments to configuration file: enter Y.
  • Accept the defaults for the SAM configuration file and environment name by pressing Enter for both.

SAM will show a changeset summary and ask you to confirm. Enter y to deploy. The deployment takes 1-2 minutes.

When deployment completes, look for the Outputs section at the bottom of the terminal. Copy the ApiUrl value. It looks like https://<id>.execute-api.us-east-1.amazonaws.com/.

Deploy failed or timed out?

If you see a permissions error, confirm your AWS CLI is configured with credentials that have CloudFormation, Lambda, API Gateway, IAM, and S3 permissions. Run aws sts get-caller-identity to check which account and user you're deploying as.

Help me fix my SAM deployment error

Enable Amazon Nova Micro model access in Bedrock

Your API is deployed, but it cannot call AWS Bedrock yet. Amazon requires you to manually enable each model before your account can invoke it. This is a one-time step.

  • Open the AWS Console in your browser and navigate to the Amazon Bedrock service.
  • Confirm you are in the us-east-1 (N. Virginia) region using the region selector in the top-right corner.
  • In the left sidebar, click Model access.
  • Click Manage model access.
  • Find Amazon Nova Micro in the list and check the box next to it.
  • Click Save changes.

The status should change to Access granted within a few seconds. Amazon's own models are typically approved instantly.

Why do I need to enable model access separately?

Your SAM template grants your Lambda function permission to call the bedrock:InvokeModel API. That handles the IAM side. But Bedrock also requires account-level model enablement as a safety gate. Think of it as two locks: IAM is the key for your function, and model access is the key for your account.

Test your live API endpoint with curl

Your API is deployed and the model is enabled. Time to send a real translation request to your live endpoint.

  • First, verify the health check endpoint is responding by running:
curl [[API_URL="your API Gateway URL from the deploy output"]]

You should see {"status":"healthy","service":"AI Translation API"} in the response.

Seeing "Internal Server Error"?

If the health check returns a 500 error, your Lambda function might have a cold start issue or import error. Check the CloudWatch logs for your function by running:

aws logs describe-log-groups --log-group-name-prefix /aws/lambda/translation-api

Help me debug my Lambda function error

  • Send a translation request to the /translate endpoint by running:
curl -X POST [[API_URL="your API Gateway URL from the deploy output"]]translate \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello, how are you today?", "source_language": "en", "target_language": "es"}'

What should I see?

The response is a JSON object with five fields: original_text (your English input), raw_translation (the Amazon Translate output), refined_translation (the Bedrock-refined version), source_language, and target_language.

The raw and refined translations may look similar for short sentences. The refinement shines more on longer, complex text where literal translations sound unnatural.

Why is the first request slow?

Lambda functions experience cold starts when invoked for the first time or after being idle. Your function needs to load Python, FastAPI, and the boto3 SDK. This can take 2-3 seconds on the first call. Subsequent requests respond much faster.

Seeing an AccessDeniedException for Bedrock?

This means model access is not enabled yet. Go back to the Bedrock console, confirm you are in us-east-1, and check that Amazon Nova Micro shows Access granted. If it shows Available instead, you need to click Manage model access and enable it.

Help me fix the Bedrock AccessDeniedException

Your translation API is live and processing requests through two AI services. Next up, you'll automate deployments so every push to your repository updates the live API automatically.

Build the CI/CD Pipeline with GitHub Actions

Your translation API is live and working. Now you need a way to deploy updates automatically every time you push code.

Right now, deploying means running sam build and sam deploy manually from your terminal. A CI/CD pipeline automates this so that every push to your main branch triggers a build and deploy without any manual steps.

In this step, get ready to:
  • Create a GitHub Actions workflow file that builds and deploys on push.
  • Configure AWS credentials as GitHub Secrets for secure deployment.
  • Push your code to trigger the pipeline and verify it runs end-to-end.
Create the GitHub Actions workflow file

GitHub Actions workflows live in a special directory inside your repository. GitHub automatically detects any YAML files in .github/workflows/ and runs them when their trigger conditions are met.

  • Create the workflow directory structure by running these commands:
mkdir -p .github/workflows
  • Confirm the directories exist by running:
ls .github/workflows

You should see an empty output (no files yet), which confirms the directory was created successfully.

  • Create the workflow file by running:
touch .github/workflows/deploy.yml
  • Open .github/workflows/deploy.yml in your text editor and paste the following workflow definition:
name: Deploy Translation API

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - uses: aws-actions/setup-sam@v2

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - run: sam build

      - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset --stack-name translation-api --resolve-s3

What does this workflow do?

The on: push: branches: [main] trigger runs this workflow every time you push to the main branch.

The steps section checks out your code, installs Python 3.12, sets up the SAM CLI, configures AWS credentials from GitHub Secrets, then runs sam build and sam deploy. The --no-confirm-changeset flag skips the manual confirmation prompt so the deploy runs unattended.

  • Save .github/workflows/deploy.yml.

YAML indentation errors?

YAML files are sensitive to indentation. Make sure you use spaces (not tabs) and that each nested level is indented by exactly 2 spaces. The with: keys must be indented under their parent step.

help me fix YAML indentation in my GitHub Actions workflow

✔️ Awesome, I've got everything!

Great. Double check you have saved your file.

ⓧ I'd like to double check the full code

name: Deploy Translation API

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - uses: aws-actions/setup-sam@v2

      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1

      - run: sam build

      - run: sam deploy --no-confirm-changeset --no-fail-on-empty-changeset --stack-name translation-api --resolve-s3
Configure AWS credentials in GitHub Secrets

Your workflow references secrets.AWS_ACCESS_KEY_ID and secrets.AWS_SECRET_ACCESS_KEY. These are encrypted values stored in your GitHub repository settings that the workflow reads at runtime.

First, you need to create a dedicated IAM user in AWS with permissions to deploy your stack.

  • Open the AWS Management Console and navigate to the IAM service.
  • Click Users in the left sidebar.
  • Click Create user.
  • Enter github-actions-deploy as the user name.
  • Click Next.
  • Select Attach policies directly.
  • Search for AdministratorAccess and select the checkbox next to it.
  • Click Next, then click Create user.

Why AdministratorAccess for a learning project?

SAM deployments need permissions across CloudFormation, Lambda, API Gateway, S3, and IAM. For a learning project, AdministratorAccess keeps things simple. In a production environment, you would create a custom policy scoped to only the services your deployment needs.

Now create an access key for this user.

  • Click on the github-actions-deploy user you just created.
  • Click the Security credentials tab.
  • Scroll down to Access keys and click Create access key.
  • Select Third-party service as the use case.
  • Check the confirmation checkbox at the bottom, then click Next.
  • Click Create access key.
  • Copy both the Access key ID and Secret access key values. Save them somewhere safe. You will not be able to see the secret key again after leaving this page.

Save your keys now

The secret access key is only shown once at creation time. If you navigate away without copying it, you will need to delete the key and create a new one.

Now add these credentials to your GitHub repository as secrets.

  • Open your GitHub repository in a browser.
  • Click Settings in the top navigation bar of the repository.
  • In the left sidebar, click Secrets and variables, then click Actions.
  • Click New repository secret.
  • Enter AWS_ACCESS_KEY_ID as the name.
  • Paste your access key ID as the value.
  • Click Add secret.
  • Click New repository secret again.
  • Enter AWS_SECRET_ACCESS_KEY as the name.
  • Paste your secret access key as the value.
  • Click Add secret.

You should now see both AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY listed under your repository secrets.

Don't see the Settings tab?

The Settings tab only appears if you are the repository owner or have admin permissions. Make sure you are viewing your own repository, not a fork or someone else's.

help me find where to add secrets in my GitHub repository settings

Push your code to trigger the pipeline

Your workflow triggers on any push to main. Committing and pushing the new workflow file will activate it for the first time.

  • Stage, commit, and push your changes by running these commands:
git add .
git commit -m "Add CI/CD pipeline with GitHub Actions"
git push origin main

What happens next?

The push triggers GitHub Actions to run your deploy.yml workflow. GitHub spins up an Ubuntu runner, installs Python and SAM, authenticates with your AWS credentials, builds your Lambda package, and deploys it to your existing CloudFormation stack.

  • Open your GitHub repository in a browser.
  • Click the Actions tab at the top of the repository page.
  • Click on the Deploy Translation API workflow in the left sidebar.
  • Click on the most recent workflow run (it will be named after your commit message).
  • Click on the deploy job to see the step-by-step logs.

The workflow takes 1-2 minutes to complete. You should see a green checkmark next to each step when it finishes successfully.

Seeing a red X on the workflow run?

Click on the failed step to see the error message. Common causes:

  • Secret names don't match exactly. Verify AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are spelled correctly in your repository secrets.
  • The IAM user doesn't have sufficient permissions. Check that AdministratorAccess is attached to the github-actions-deploy user.
  • YAML syntax error in deploy.yml. Check that indentation uses spaces (not tabs) and all lines are properly nested.

my GitHub Actions workflow failed during deployment, help me debug it

Secret mission

Add Batch Translation

Your API handles one translation at a time. But real-world applications need to process multiple texts in a single request. Can you add a batch endpoint that translates multiple texts concurrently and handles partial failures gracefully?

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. This project uses serverless AWS resources that cost virtually nothing when idle, so there is no urgency to delete.

Resources you used:

  • AWS CloudFormation stack (translation-api) containing your Lambda function, API Gateway HTTP API, and IAM roles.
  • GitHub repository with your project code and CI/CD workflow.
  • IAM user credentials stored in GitHub Secrets for automated deployments.
  • Local project directory on your Desktop.

Keep everything running

No action needed. Choose this if you want to keep calling your translation API or plan to build on it further.

  • Your Lambda function costs nothing when it is not receiving requests.
  • API Gateway charges only per request (1M requests free per month for the first 12 months).
  • Amazon Translate is free for 2 million characters per month for the first 12 months.
  • Bedrock Nova Micro charges only when invoked ($0.000035 per 1,000 input tokens).
  • This stack is safe to leave running indefinitely within free tier limits.

Pause - I'll come back to this later

Serverless resources do not run continuously, so there is nothing to pause. Your Lambda function and API Gateway only activate when a request arrives.

  • If you want to prevent any accidental charges, you can delete the IAM user credentials from GitHub Secrets so the pipeline cannot deploy updates.
  • The stack will remain deployed but dormant. You can re-add credentials and push a commit whenever you want to resume.

Delete - I don't want to use this again

Remove all AWS resources and start fresh. This deletes the CloudFormation stack and everything it created.

  • Open your terminal and navigate to your project directory.
  • Delete the AWS stack by running:
sam delete --stack-name translation-api
  • Confirm the deletion when prompted. This removes your Lambda function, API Gateway, IAM roles, and the S3 bucket SAM created for deployment artifacts.
  • Optionally delete the IAM user you created for GitHub Actions in the AWS IAM console.
  • Optionally delete the GitHub repository if you no longer need the code.
  • Optionally delete the local project folder from your Desktop.

That's a Wrap!

That's a Wrap!

Nice work! You just built a production-style AI translation microservice and deployed it to AWS with a full CI/CD pipeline.

Here's what you accomplished:

  • Built a FastAPI microservice that integrates two AWS AI services (Amazon Translate and AWS Bedrock) for context-aware translation.
  • Defined your entire serverless infrastructure as code using an AWS SAM template that provisions Lambda, API Gateway, and IAM roles in a single YAML file.
  • Created an automated CI/CD pipeline with GitHub Actions that builds and deploys your API on every push to main.
  • Secret Mission: Added a batch translation endpoint with concurrent processing using asyncio and graceful error handling for partial failures.

Ready to quiz yourself?