How to call foundation models on AWS from code: InvokeModel vs Converse (plus ConverseStream), the request and response shapes, tool use, system prompts, inference parameters, the boto3 and JavaScript SDKs, streaming, and how to handle throttling and errors.
"The Bedrock API" is two API families: a control plane to manage the service and a runtime to call models. For prompts and completions you only need the runtime.
The control plane (the bedrock service in the SDKs) manages the service: list models, create Guardrails, fine-tuning jobs, evaluations, Provisioned Throughput, Knowledge Bases and Agents. The runtime (the bedrock-runtime service) is where inference happens.
The runtime has four operations that matter day to day: InvokeModel and InvokeModelWithResponseStream (the original pair), and Converse and ConverseStream (the unified pair). Choosing between the two families is the most important API decision you make.
InvokeModel is provider-shaped. The request body is JSON in the format that model's provider defined (Anthropic, Meta and Amazon Nova all differ), and you parse a provider-specific response. You get every native parameter, but your code branches per provider and a model swap can be a rewrite.
Converse uses one request schema (messages, optional system prompt, inference config, optional tools) and returns one response schema for every provider. Multi-turn chat, system prompts, tool use and streaming are first-class, and switching models is usually a one-line modelId change. It is the recommended default for chat and agents. See the Converse API reference.
Use Converse for anything conversational. Use InvokeModel for the cases Converse does not cover.
Use Converse or ConverseStream for chatbots, assistants, agents and multi-turn apps; when you want to swap or fall back between models; and for tool use with a consistent shape.
Use InvokeModel for modalities Converse does not model: image generation (Stability, Nova Canvas), video (Nova Reel) and text embeddings (Titan, Cohere Embed), or for a provider-specific parameter Converse does not expose.
| If you need | Use | Request body | Streaming variant | Notes |
|---|---|---|---|---|
| Chat, multi-turn, agents | Converse | Unified schema | ConverseStream | Default for text and chat |
| Swap providers easily | Converse | Unified schema | ConverseStream | Change the modelId only |
| Tool use (function calling) | Converse | toolConfig | ConverseStream | Same shape across providers |
| Text embeddings | InvokeModel | Provider-specific | n/a | Titan, Cohere Embed |
| Image generation | InvokeModel | Provider-specific | n/a | Stability, Nova Canvas |
| Video generation | InvokeModel (async) | Provider-specific | n/a | Nova Reel, async invocation |
| A provider-only parameter | InvokeModel | Provider-specific | WithResponseStream | Only when Converse omits it |
Check model access, grant IAM permissions, pick a Region, then make one call.
1. Model access. AWS now enables access to many Bedrock models by default. The first call to a third-party model starts its AWS Marketplace subscription in the background, so the caller needs aws-marketplace:Subscribe (plus Unsubscribe and ViewSubscriptions) the first time. Anthropic models also need a one-time use-case form per account or organization, submitted from the model catalog or the PutUseCaseForModelAccess API. Details are in the model access guide.
2. IAM. Grant the runtime actions you use: bedrock:InvokeModel, bedrock:InvokeModelWithResponseStream for streaming, and the Converse actions, ideally scoped to specific model ARNs. 3. Region. Use a Region where the model is available. There is no API key: Bedrock uses normal AWS credentials (role, environment or profile).
Create a bedrock-runtime client and call converse with a modelId, messages and inferenceConfig. The text is at response["output"]["message"]["content"][0]["text"]; token counts are in response["usage"]. Model IDs below are illustrative; copy the exact ID from the console.
Use @aws-sdk/client-bedrock-runtime: create a BedrockRuntimeClient, build a ConverseCommand with the same fields, and send it. The text is at response.output.message.content[0].text.
# Python (boto3)
import boto3
brt = boto3.client("bedrock-runtime", region_name="us-east-1")
resp = brt.converse(
modelId="anthropic.claude-sonnet", # illustrative: copy the exact ID from the console
messages=[{"role": "user", "content": [{"text": "List three uses for embeddings."}]}],
inferenceConfig={"maxTokens": 512, "temperature": 0.2},
)
print(resp["output"]["message"]["content"][0]["text"])
print(resp["usage"]) # {inputTokens, outputTokens, totalTokens}
// JavaScript (@aws-sdk/client-bedrock-runtime v3)
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
const client = new BedrockRuntimeClient({ region: "us-east-1" });
const resp = await client.send(new ConverseCommand({
modelId: "anthropic.claude-sonnet", // illustrative
messages: [{ role: "user", content: [{ text: "List three uses for embeddings." }] }],
inferenceConfig: { maxTokens: 512, temperature: 0.2 },
}));
console.log(resp.output.message.content[0].text);
The shape is the same for every model. Learn it once and you can read or write any Bedrock chat call.
Request. modelId (required) is the model or inference-profile ID. messages (required) is an ordered array of turns, each { role: "user" | "assistant", content: [...] }, where content holds typed blocks: text, image, document, and for tools toolUse and toolResult. system (optional) holds system-prompt blocks, kept out of the message list. inferenceConfig holds sampling settings. toolConfig declares tools. guardrailConfig attaches a Guardrail, and additionalModelRequestFields passes provider-only parameters.
Response. output.message is the assistant turn, in the same shape as an input message, so you can append it straight back onto messages. stopReason says why generation stopped: end_turn, max_tokens, stop_sequence, tool_use or guardrail_intervened. usage reports input, output and total tokens; log them, because your bill and rate limits use them.
Bedrock keeps no server-side session. You own the transcript: send messages, append the reply and the next user turn, and call again. That lets you trim, summarize or cache history however you like.
| Field | Where | Required | Purpose |
|---|---|---|---|
| modelId | request | Yes | Model or cross-region inference-profile ID |
| messages | request | Yes | Ordered turns of typed content blocks |
| system | request | No | System-prompt blocks |
| inferenceConfig | request | No | maxTokens, temperature, topP, stopSequences |
| toolConfig | request | No | Tool declarations and tool-choice policy |
| guardrailConfig | request | No | Attach a Guardrail by ID and version |
| output.message | response | n/a | Assistant turn; append it to continue |
| stopReason | response | n/a | end_turn, max_tokens, stop_sequence, tool_use, guardrail_intervened |
| usage | response | n/a | inputTokens, outputTokens, totalTokens |
Two levers control every call: the system prompt and the sampling parameters.
The system prompt sets role, rules and output format. In Converse it is the top-level system field, such as [{ "text": "Answer only from the provided context. If unsure, say so." }]. A stable system prompt is a good candidate for prompt caching, which bills repeated context at a lower rate.
inferenceConfig works the same across providers: maxTokens caps response length (and output cost), temperature sets randomness (low for extraction and tool use, higher for creative drafts), topP is nucleus sampling (tune it instead of temperature, not with it), and stopSequences halts generation on given strings. Provider-only knobs such as topK go through additionalModelRequestFields.
A sound production default is a low temperature (0.0 to 0.2) and an explicit maxTokens. Leaving maxTokens unbounded is the most common cause of surprise output costs.
resp = brt.converse(
modelId="anthropic.claude-sonnet",
system=[{"text": "You are a precise support assistant. Answer only from the provided context; if unsure, say you don't know."}],
messages=[{"role": "user", "content": [{"text": "What is our refund window?"}]}],
inferenceConfig={"maxTokens": 400, "temperature": 0.1, "topP": 0.9, "stopSequences": ["\n\nUSER:"]},
)
The model asks your code to run a function, you run it, you return the result, and the model continues.
Declare tools in toolConfig.tools. Each toolSpec has a name, a description the model uses to decide when to call it, and an inputSchema in JSON Schema. toolChoice sets whether the model may call a tool (auto), must call one (any), or must call a specific one. The model never runs anything itself.
The loop: (1) send the user message with toolConfig; (2) the model returns stopReason: "tool_use" and a toolUse block { toolUseId, name, input }; (3) your code runs the function and sends a new user message with a toolResult block carrying the same toolUseId; (4) the model answers or asks for another tool.
Amazon Bedrock Agents automate this loop if you would rather configure tools than write it. Use raw Converse tool use when you want full control.
tool_config = {
"tools": [{
"toolSpec": {
"name": "get_order_status",
"description": "Look up the current status of a customer order by its ID.",
"inputSchema": {"json": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}}
}
}]
}
resp = brt.converse(modelId=MODEL, messages=messages, toolConfig=tool_config)
if resp["stopReason"] == "tool_use":
# run get_order_status(order_id), then append a user message
# with a toolResult block (same toolUseId) and call converse again.
For anything a person watches, stream tokens as they are generated.
ConverseStream returns an event stream: messageStart, a series of contentBlockDelta events carrying text chunks, contentBlockStop and messageStop (with the stopReason), and a final metadata event with token usage. Tool-use arguments also arrive across deltas; collect them before running the tool.
Streaming does not change pricing or the request shape. It only changes when you receive the output, and your code assembles the final message from events.
stream = brt.converse_stream(
modelId="anthropic.claude-sonnet",
messages=[{"role": "user", "content": [{"text": "Explain prompt caching in two sentences."}]}],
inferenceConfig={"maxTokens": 300},
)
for event in stream["stream"]:
if "contentBlockDelta" in event:
print(event["contentBlockDelta"]["delta"]["text"], end="")
elif "metadata" in event:
print(event["metadata"]["usage"])
The error you will meet most is throttling. Retry the right errors, and design for rate limits up front.
Bedrock limits requests per minute and tokens per minute per account, model and Region. Exceed either and you get ThrottlingException (HTTP 429). Retry with exponential backoff and jitter. The SDK retry modes cover a baseline; high-volume services add their own backoff, a concurrency limiter and a queue.
For more headroom: request a quota increase in Service Quotas, use cross-region inference profiles to spread load across Regions (see cross-region inference), or buy Provisioned Throughput for guaranteed capacity.
Most other errors are not retryable. AccessDeniedException: missing IAM or Marketplace permissions, or the Anthropic use-case form not yet processed. ValidationException: a malformed request. ResourceNotFoundException: a wrong or unavailable modelId for that Region. ModelTimeoutException and 5xx errors are transient and safe to retry with backoff.
| Exception | HTTP | Meaning | Retry? | Fix |
|---|---|---|---|---|
| ThrottlingException | 429 | Request or token rate exceeded | Yes, with backoff + jitter | Backoff; quota increase; cross-region; Provisioned Throughput |
| ModelTimeoutException | 408/5xx | Model took too long | Yes, with backoff | Retry; shorten prompt or maxTokens |
| ServiceUnavailable / Internal | 5xx | Transient server issue | Yes, with backoff | Retry with backoff |
| ValidationException | 400 | Malformed request | No | Fix the request |
| AccessDeniedException | 403 | Missing IAM or Marketplace permission, or model prerequisite | No | Fix IAM; submit the Anthropic use-case form |
| ResourceNotFoundException | 404 | Unknown modelId in this Region | No | Correct the modelId or Region |
Bedrock inference draws down AWS credits automatically. Here is who can get you funding for it.
Partner-filed funding: a vetted AWS partner can file POC (proof of concept) funding to test a new AI feature, or incremental workload (IW) funding if you already run on AWS and the feature is new work. For startups this is typically $50K to $150K. Depends on the workload; AWS approves.
AWS Activate: you apply yourself. Activate Founders gives self-funded startups $1,000 to start, up to $5,000. Activate Portfolio gives up to $200,000 with the Org ID from your VC, accelerator or angel group, if it is an Activate Provider. A consulting partner cannot file Activate for you.
CloudRoute matches you with one vetted AWS partner who can harden the integration and check which funding fits. Startups pay CloudRoute nothing. Partners pay CloudRoute a monthly membership plus 10% of AWS-approved funding.
| Funding | Who files it | Amount | When it fits |
|---|---|---|---|
| Migration Acceleration Program (MAP) | Your matched AWS partner | $50K to $150K for startups. Depends on the workload; AWS approves. | You are not on AWS yet and want to migrate. |
| Proof of concept (POC) funding | Your matched AWS partner | $50K to $150K for startups. Depends on the workload; AWS approves. | You want to test something, whether or not you are on AWS today. |
| Incremental workload (IW) funding | Your matched AWS partner | $50K to $150K for startups. Depends on the workload; AWS approves. | You are already on AWS and are launching new work: a new AI feature, a new customer or a new contract. |
| AWS Activate Founders | Founder applies | $1,000 to start, up to $5,000 | Self-funded startups with no investor or accelerator behind them. |
| AWS Activate Portfolio | Founder applies | up to $200,000 | Startups backed by a VC firm, accelerator or angel investor that is an AWS Activate Provider. |
| AWS Credits for AI Startups | Invite only (AWS account manager) | $200,000 or more, by invitation | AI startups ready to scale after Activate Portfolio, invited by AWS. |
"Unified" means the same shape for all chat models; "provider-specific" means each vendor's native format.
| Operation | Schema | Streaming? | Tool use | Best for | Avoid for |
|---|---|---|---|---|---|
| Converse | Unified | No | Built in | Chat, agents, portable text apps | Embeddings, images, video |
| ConverseStream | Unified | Yes | Built in (streamed) | Chat UIs, long answers | Offline batch jobs |
| InvokeModel | Provider-specific | No | Provider-defined | Embeddings, images, provider-only params | New portable chat apps |
| InvokeModelWithResponseStream | Provider-specific | Yes | Provider-defined | Streaming a provider-specific body | When Converse would do |
You fill in a short form: what you run today, what you want to move, test or build, your stage and your region.
We read your answers and pick one vetted AWS consulting partner that fits the work, the stack and the region. We reply within 24 hours; your matched partner reaches out within 5 business days.
Your matched partner scopes the test with you. Then it checks whether the test qualifies for POC (proof of concept) funding. A POC can fit whether or not you run on AWS today.
If the work qualifies, the partner files the funding request with AWS. AWS reviews it and decides. The amount depends on the workload.
CloudRoute is free for startups. Partners pay CloudRoute a monthly membership plus 10% of AWS-approved funding.
AWS Activate credits are separate, and you apply for them yourself. Use Activate Founders if no investor backs you. Use Activate Portfolio with the Org ID or invite from your VC, accelerator, incubator or angel group if it is an Activate Provider. A consulting partner cannot apply for Activate on your behalf.
A vetted AWS partner can build the production integration (Converse, tool use, streaming, retries, cost controls) and check whether the work qualifies for POC or IW funding: typically $50K to $150K for startups. Depends on the workload; AWS approves. CloudRoute is free for startups.