AWS SDK AI Examples: Rekognition Image Labels and Bedrock Text Generation (Python, PHP, Node.js)
AWS has ready-made AI you call like any other API. From Python, PHP and Node.js we detect labels and text in photos with Rekognition, moderate uploads, and generate text with Amazon Bedrock using the Converse API, including streaming. The example: a second-hand marketplace with Giulia in Milan and Meera in Kochi.
What you'll learn in this chapter
- Detect labels in an image
- Read text and moderate content
- Call Bedrock Converse
- Stream model output
- Control cost and safety
1. The big picture: the shop assistant
Why do we need this?
A seller uploads a photo of a bicycle. One assistant looks at it and says "bicycle, wheel, outdoor". Another writes a friendly listing: "Lightly used city bike, €180". Rekognition is the first assistant, Bedrock the second.
What is it?
Rekognition takes an Image (bytes or S3Object) and returns labels with Confidence. Bedrock Converse takes a modelId and messages and returns the reply plus token usage. Enable model access in the Bedrock console first.
How to do it (step by step)
2. Detect labels
Why do we need this?
Giulia wants each photo tagged so buyers can search "bicycle" or "sofa".
What is it?
DetectLabels with MaxLabels and MinConfidence. Use S3Object for photos already in S3 (same region).
How to do it (step by step)
- Python.
import boto3
rk = boto3.client("rekognition", region_name="eu-west-1")
r = rk.detect_labels(Image={"S3Object": {"Bucket": "market-photos-mi", "Name": "bike-221.jpg"}},
MaxLabels=5, MinConfidence=80)
for l in r["Labels"]: print(l["Name"], round(l["Confidence"]))
Expected output:
Bicycle 99
Wheel 98
Vehicle 97
Outdoors 88
City 82
- Node.js: from local bytes.
import { readFileSync } from "node:fs";
import { RekognitionClient, DetectLabelsCommand } from "@aws-sdk/client-rekognition";
const rk = new RekognitionClient({ region: "ap-south-1" });
const r = await rk.send(new DetectLabelsCommand({ Image: { Bytes: readFileSync("sofa-88.jpg") }, MaxLabels: 3 }));
console.log(r.Labels.map(l => l.Name).join(", "));
Expected output:
Couch, Furniture, Living Room
- PHP.
<?php
require "vendor/autoload.php";
$rk = new Aws\Rekognition\RekognitionClient(["region" => "eu-west-1"]);
$r = $rk->detectLabels(["Image" => ["Bytes" => file_get_contents("lamp.jpg")], "MaxLabels" => 2]);
echo implode(", ", array_column($r["Labels"], "Name")), PHP_EOL;
Expected output:
Lamp, Lampshade
- Verify with the CLI.
aws rekognition detect-labels --image "S3Object={Bucket=market-photos-mi,Name=bike-221.jpg}" --max-labels 1 --region eu-west-1 --query "Labels[0].Name" --output text
Expected output:
Bicycle
3. Read text and moderate
Why do we need this?
Meera blocks unsafe photos and reads the price tag in a photo of a Kochi spice tin: ₹350.
What is it?
DetectText returns LINE and WORD items. DetectModerationLabels returns categories like "Violence"; an empty list means it looks safe.
How to do it (step by step)
- Python: text lines.
r = rk.detect_text(Image={"Bytes": open("tin.jpg", "rb").read()})
print([t["DetectedText"] for t in r["TextDetections"] if t["Type"] == "LINE"])
Expected output:
['KERALA PEPPER', 'MRP ₹350', '200 g']
- Node.js: moderation.
import { DetectModerationLabelsCommand } from "@aws-sdk/client-rekognition";
const r = await rk.send(new DetectModerationLabelsCommand({ Image: { Bytes: readFileSync("sofa-88.jpg") }, MinConfidence: 60 }));
console.log(r.ModerationLabels.length === 0 ? "safe" : r.ModerationLabels.map(m => m.Name));
Expected output:
safe
- PHP: reject in an upload handler.
<?php
$m = $rk->detectModerationLabels(["Image" => ["Bytes" => file_get_contents($_FILES["p"]["tmp_name"])], "MinConfidence" => 70]);
if (count($m["ModerationLabels"])) { http_response_code(422); exit("Photo not allowed"); }
echo "accepted", PHP_EOL;
Expected output:
accepted
- Verify: a test image.
aws rekognition detect-text --image-bytes fileb://tin.jpg --region ap-south-1 --query "TextDetections[?Type=='LINE'] | [1].DetectedText" --output text
Expected output:
MRP ₹350
4. Generate text with Bedrock
Why do we need this?
From labels and a price, write a short, honest listing in English or Italian.
What is it?
Converse uses the same request shape for every model: messages, system, inferenceConfig. Use a model ID or inference profile enabled in your account.
How to do it (step by step)
- Python.
br = boto3.client("bedrock-runtime", region_name="eu-west-1")
r = br.converse(modelId="amazon.nova-lite-v1:0",
system=[{"text": "You write short marketplace listings. Max 25 words. No made-up facts."}],
messages=[{"role": "user", "content": [{"text": "Labels: Bicycle, City. Price €180. Condition: lightly used. Italian."}]}],
inferenceConfig={"maxTokens": 120, "temperature": 0.4})
print(r["output"]["message"]["content"][0]["text"]); print(r["usage"])
Expected output:
Bici da città poco usata, pronta per Milano. €180.
{'inputTokens': 41, 'outputTokens': 17, 'totalTokens': 58}
- Node.js.
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
const br = new BedrockRuntimeClient({ region: "ap-south-1" });
const r = await br.send(new ConverseCommand({ modelId: "apac.amazon.nova-lite-v1:0",
messages: [{ role: "user", content: [{ text: "Write a 20-word listing: Couch, 3-seater, ₹9,500, Kochi pickup." }] }],
inferenceConfig: { maxTokens: 100 } }));
console.log(r.output.message.content[0].text);
Expected output:
Comfortable 3-seater couch in great shape. ₹9,500. Pickup in Kochi.
- PHP.
<?php
$br = new Aws\BedrockRuntime\BedrockRuntimeClient(["region" => "eu-west-1"]);
$r = $br->converse(["modelId" => "amazon.nova-lite-v1:0",
"messages" => [["role" => "user", "content" => [["text" => "One-line listing: Lamp, brass, £25, London."]]]],
"inferenceConfig" => ["maxTokens" => 60]]);
echo $r["output"]["message"]["content"][0]["text"], PHP_EOL;
Expected output:
Vintage brass lamp, warm light, £25 — collect in London.
- Verify: model access is enabled.
aws bedrock list-foundation-models --region eu-west-1 --by-provider amazon --query "modelSummaries[?modelId=='amazon.nova-lite-v1:0'].modelId" --output text
Expected output:
amazon.nova-lite-v1:0
5. Stream the answer
Why do we need this?
In the chat helper, buyers in Dubai should see words appear at once, not wait 5 seconds.
What is it?
ConverseStream returns an event stream: contentBlockDelta events carry text pieces; metadata carries usage.
How to do it (step by step)
- Python.
r = br.converse_stream(modelId="amazon.nova-lite-v1:0",
messages=[{"role": "user", "content": [{"text": "Is a used bike for AED 700 a fair price? 2 lines."}]}])
for ev in r["stream"]:
if "contentBlockDelta" in ev: print(ev["contentBlockDelta"]["delta"]["text"], end="", flush=True)
print()
Expected output:
For a lightly used city bike, AED 700 is fair.
Check brakes and gears before paying.
- Node.js.
import { ConverseStreamCommand } from "@aws-sdk/client-bedrock-runtime";
const r = await br.send(new ConverseStreamCommand({ modelId: "apac.amazon.nova-lite-v1:0",
messages: [{ role: "user", content: [{ text: "Give 3 tips to sell a sofa fast." }] }] }));
for await (const ev of r.stream) if (ev.contentBlockDelta) process.stdout.write(ev.contentBlockDelta.delta.text);
Expected output:
1. Clear photos in daylight. 2. Fair price. 3. Offer pickup times.
- PHP.
<?php
$r = $br->converseStream(["modelId" => "amazon.nova-lite-v1:0",
"messages" => [["role" => "user", "content" => [["text" => "Say hello in Italian and Malayalam."]]]]]);
foreach ($r["stream"] as $ev) if (isset($ev["contentBlockDelta"])) echo $ev["contentBlockDelta"]["delta"]["text"];
echo PHP_EOL;
Expected output:
Ciao! നമസ്കാരം!
- Verify: token usage metric.
aws cloudwatch get-metric-statistics --namespace AWS/Bedrock --metric-name OutputTokenCount --dimensions Name=ModelId,Value=amazon.nova-lite-v1:0 --statistics Sum --period 3600 --start-time $(date -u -d "-1 hour" +%FT%TZ) --end-time $(date -u +%FT%TZ) --region eu-west-1 --query "Datapoints[0].Sum"
Expected output:
112.0
6. The same call in Java, Go, .NET and the CLI
Why do we need this?
Teams often mix languages. Once you know the call in Python, PHP and Node.js, the other SDKs are the same API with different syntax.
What is it?
All of them call the Converse API. Use the CLI line to test it quickly from a terminal.
How to do it (step by step)
| Stack | Code |
|---|---|
| AWS CLI | aws bedrock-runtime converse --model-id amazon.nova-lite-v1:0 --messages '[{"role":"user","content":[{"text":"Hi"}]}]' |
| Java v2 | var r = br.converse(b -> b.modelId("amazon.nova-lite-v1:0").messages(Message.builder().role(ConversationRole.USER).content(ContentBlock.fromText("Hi")).build())); |
| Go v2 | out, _ := client.Converse(ctx, &bedrockruntime.ConverseInput{ModelId: aws.String("amazon.nova-lite-v1:0"), Messages: msgs}) |
| .NET | var r = await br.ConverseAsync(new ConverseRequest { ModelId = "amazon.nova-lite-v1:0", Messages = msgs }); |
7. Clean up so nothing keeps costing
Why do we need this?
Lab resources left running cost money and can be a security risk.
What is it?
Delete in reverse order: children first, then parents.
How to do it (step by step)
- Nothing to delete for on-demand calls; remove test photos.
aws s3 rm s3://market-photos-mi/bike-221.jpg
Expected output:
(no output means success)
Ravindra Bagale's Tip
Always set maxTokens and log usage; an unbounded prompt in a loop can cost real money fast.
8. Interview questions and answers
Q. Rekognition input options?
Image bytes (up to 5 MB) or an S3Object in the same region.
Q. Which client calls models?
bedrock-runtime (Converse, InvokeModel); bedrock is for management.
Q. Why Converse over InvokeModel?
One request format for all models, plus tools and streaming.
Q. How do you stream?
ConverseStream and read contentBlockDelta events.
Q. What does an empty ModerationLabels mean?
No unsafe content above MinConfidence.
Try at home
- Chain it: Python reads labels for an S3 photo and asks Bedrock for a listing in Marathi.
- Add a 1-cent budget guard in Node.js that stops when totalTokens > 2,000.
In short
- DetectLabels with confidence.
- DetectText / Moderation.
- Converse = one format.
- ConverseStream for chat.
- Limit tokens, log usage.
Practice Tasks
Bengaluru HealthTech is a health technology business in Bengaluru. Arjun Rao looks after its IT and cloud work. They count money in INR and run their AWS resources in the ap-south-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.
0 / 10
Easy List Bedrock models
Why: See which foundation models are offered in your Region.
What to do: List the foundation models available in Amazon Bedrock.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Easy List SageMaker endpoints
Why: Endpoints cost money every hour; list them to find forgotten ones.
What to do: List SageMaker endpoints.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Easy How many addresses in a /16?
Why: Picking a VPC size needs this count.
What to do: Bengaluru HealthTech plans the VPC 10.130.0.0/16. How many IP addresses does it hold in total?
- Write down the numbers the task gives you.
- Work it out step by step on paper.
- Type only the final answer in the box.
- Click Check answer.
Easy Port for SSH
Why: Security group rules need the right port.
What to do: Which TCP port must the security group open for SSH?
- Write down the numbers the task gives you.
- Work it out step by step on paper.
- Type only the final answer in the box.
- Click Check answer.
Easy Show where you are
Why: Before you copy or delete anything, you must know which folder you are in.
What to do: Arjun Rao logged in to the Bengaluru HealthTech server and is not sure which folder the terminal is in. Print the current folder path.
- Find the Linux command that does this job.
- Add the options (like -r or -h) the task needs.
- Add the file names, folders or users in the right order.
- Use sudo if the job changes system files or users. Click Check answer.
Easy Read-only access to one bucket
Why: Least privilege: give only the actions and the bucket the job needs.
What to do: Write an identity policy that lets the user read objects (s3:GetObject) only in bucket blrhealthtech-data.
- Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
- In each statement write Effect, Action and Resource.
- Give only what the task needs (least privilege).
- Use double quotes and commas correctly. Click Check answer.
Medium Detect labels in an image
Why: Rekognition names objects in a photo, e.g. to tag product images.
What to do: Detect labels in s3://blrhealthtech-media/shop/p1.jpg (Bucket=blrhealthtech-media, Name=shop/p1.jpg), max 10 labels.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Medium Allow one Bedrock model
Why: Limit apps to the approved model so costs stay predictable.
What to do: Allow bedrock:InvokeModel on arn:aws:bedrock:ap-south-1::foundation-model/amazon.nova-lite-v1:0 only.
- Start with { "Version": "2012-10-17", "Statement": [ ... ] }.
- In each statement write Effect, Action and Resource.
- Give only what the task needs (least privilege).
- Use double quotes and commas correctly. Click Check answer.
Medium Usable IPs in an AWS /24 subnet
Why: AWS keeps 5 addresses in every subnet.
What to do: How many addresses can Bengaluru HealthTech use for servers in subnet 10.130.1.0/24 on AWS?
- Write down the numbers the task gives you.
- Work it out step by step on paper.
- Type only the final answer in the box.
- Click Check answer.
Hard Ask a model a question
Why: Converse is one API shape for many models.
What to do: Call converse on model amazon.nova-lite-v1:0 with the messages in msgs.json.
- Read the task and decide which AWS service it is about.
- Write aws, then the service name, then the operation (the action word).
- Add each needed flag (--name) followed by its value from the task.
- Flags can be in any order. Click Check answer.
Revision, comparison and interview cards
Revise this lesson in a few minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens this lesson.




More cards: Ravi Sir's Cards.