The Converse API vs InvokeModel
When to use the unified Converse API vs model-specific InvokeModel payloads.
Busque em todas as páginas da documentação
When to use the unified Converse API vs model-specific InvokeModel payloads.
Bedrock Runtime offers two ways to run inference, and they solve different problems. Converse gives you one request shape that works across model families. InvokeModel gives you direct access to each model's own request and response JSON. This page shows both against the same prompt so the trade-off is concrete, then gives you a rule for choosing.
Quick-reference recipe card - copy-paste ready.
# --- Python (boto3) ---
import boto3, json
brt = boto3.client("bedrock-runtime", region_name="us-east-1")
# Converse: model-agnostic, no body-building.
c = brt.converse(
modelId="amazon.nova-lite-v1:0",
messages=[{"role": "user", "content": [{"text": "Hi"}]}],
)
print(c["output"]["message"]["content"][0]["text"])// --- TypeScript (AWS SDK v3) ---
import { BedrockRuntimeClient, ConverseCommand } from "@aws-sdk/client-bedrock-runtime";
const brt = new BedrockRuntimeClient({ region: "us-east-1" });
// Converse: model-agnostic, no body-building.
const c = await brt.send(new ConverseCommand({
modelId: "amazon.nova-lite-v1:0",
messages: [{ role: "user", content: [{ text: "Hi" }] }],
}));
console.log(c.output?.message?.content?.[0]?.text);When to reach for each:
bedrock:InvokeModel IAM permission and billed by tokens.Here is the same single-turn prompt, first through Converse, then through InvokeModel. Notice InvokeModel forces you to build and parse the model family's own body - here, the Anthropic Claude schema on Bedrock.
# --- Python (boto3) ---
import boto3, json
brt = boto3.client("bedrock-runtime", region_name="us-east-1")
claude_id = "anthropic.claude-3-5-haiku-20241022-v1:0" # representative; may need an inference profile
# 1. Converse - model-agnostic shape.
conv = brt.converse(
modelId=claude_id,
messages=[{"role": "user", "content": [{"text": "Give me a fun fact."}]}],
inferenceConfig={"maxTokens": 100},
)
print("Converse:", conv["output"]["message"]["content"][0]["text"])
# 2. InvokeModel - you hand-build Claude's own request body.
body = {
"anthropic_version": "bedrock-2023-05-31",
"max_tokens": 100,
"messages": [{"role": "user", "content": [{"type": "text", "text": "Give me a fun fact."}]}],
}
raw = brt.invoke_model(modelId=claude_id, body=json.dumps(body))
parsed = json.loads(raw["body"].read())
print("InvokeModel:", parsed["content"][0]["text"])// --- TypeScript (AWS SDK v3) ---
import {
BedrockRuntimeClient, ConverseCommand, InvokeModelCommand,
} from "@aws-sdk/client-bedrock-runtime";
const brt = new BedrockRuntimeClient({ region: "us-east-1" });
const claudeId = "anthropic.claude-3-5-haiku-20241022-v1:0"; // representative; may need an inference profile
// 1. Converse - model-agnostic shape.
const conv = await brt.send(new ConverseCommand({
modelId: claudeId,
messages: [{ role: "user", content: [{ text: "Give me a fun fact." }] }],
inferenceConfig: { maxTokens: 100 },
}));
console.log("Converse:", conv.output?.message?.content?.[0]?.text);
// 2. InvokeModel - you hand-build Claude's own request body.
const body = {
anthropic_version: "bedrock-2023-05-31",
max_tokens: 100,
messages: [{ role: "user", content: [{ type: "text", text: "Give me a fun fact." }] }],
};
const raw = await brt.send(new InvokeModelCommand({
modelId: claudeId,
body: JSON.stringify(body),
contentType: "application/json",
}));
const parsed = JSON.parse(new TextDecoder().decode(raw.body));
console.log("InvokeModel:", parsed.content[0].text);What this demonstrates:
claude_id for a Nova or Llama id.anthropic_version, max_tokens, and typed content blocks.body must be read and JSON-parsed by hand.InvokeModel came first. Every model provider defined its own request and response JSON, and Bedrock simply passed that JSON through. That is maximally flexible but pushes all the differences onto you: to support three families you learn three schemas.
Converse was added as a normalization layer. AWS defined one canonical shape - messages, system, inferenceConfig, toolConfig - and Bedrock translates it into whatever each model expects, then translates the reply back. You trade a little per-model expressiveness for portability and a much smaller surface to learn.
content array that also carries images, documents, tool calls, and tool results.inferenceConfig - maxTokens, temperature, topP, stopSequences under one set of names.toolConfig - a single tool-definition and tool-result contract that works across families that support tools.output.message, stopReason, and usage, no matter the model.Converse is chat-shaped, so it does not cover models that are not conversational. Text-embedding models (which return vectors) and image-generation models (which return images) are reached through InvokeModel with their own bodies. Some families also expose parameters that Converse does not surface; if you need one, InvokeModel is the escape hatch. There is also additionalModelRequestFields on Converse for a middle ground - passing a few model-specific knobs while keeping the Converse shape.
Both surfaces stream. ConverseStream emits normalized delta events; InvokeModelWithResponseStream emits raw model-specific chunks you parse yourself. The same portability trade-off applies - the streaming page uses ConverseStream for exactly this reason.
body must be a JSON string in, and the response body is a stream/bytes out. Fix: json.dumps / JSON.stringify on the way in, .read() + json.loads or TextDecoder + JSON.parse on the way out.bedrock control-plane client and verify at build time.additionalModelRequestFields on Converse, or InvokeModel, when you truly need them.contentType on InvokeModel - some models expect application/json. Fix: set contentType (and accept) explicitly.| Approach | Use When | Don't Use When |
|---|---|---|
| Converse (this section's default) | Chat, tool use, multimodal, multi-model code | You need a non-chat modality or a hidden model param |
| InvokeModel | Embeddings, image generation, model-specific fields | You want portability across model families |
Converse + additionalModelRequestFields | Mostly-portable code that needs a few model knobs | The parameter set is heavily model-specific |
| ConverseStream / InvokeModelWithResponseStream | You need incremental output for UX | A single batched response is fine |
Converse. It is model-agnostic, so the same code works across families and you avoid learning each model's JSON schema. Drop to InvokeModel only for a concrete reason.
Reach non-chat models (text embeddings, image generation) and certain model-specific parameters. Those do not fit the chat-shaped Converse contract.
Billing is by tokens either way, and both require the same bedrock:InvokeModel permission. Only the request and response shape differs, not the pricing model.
The response body is a stream/bytes, not parsed JSON. Read it (.read() in boto3, TextDecoder in v3) and json.loads / JSON.parse it, matching the model family's response schema.
Yes, via additionalModelRequestFields. It lets you keep the portable Converse shape while forwarding a few parameters a specific model understands.
It is close but Bedrock-specific: the body uses anthropic_version: "bedrock-2023-05-31" and omits the model field (the model is the modelId). Do not assume the exact hosted-API shape.
Only if the new model lacks a feature you use (tools, vision). The base text call is portable - you change the modelId string and nothing else.
toolConfig.Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+). Bedrock model IDs and inference profiles are region/version-specific - verify against the current Bedrock catalog at build time.
Revisado por Chris St. John·Última atualização: 24 de jul. de 2026