Bedrock Prompt Caching in Node.js: Converse Setup and Costs

Bedrock prompt caching in Node.js: add cachePoint blocks to Converse API calls, cut Claude input costs, and fix the gotchas that keep cache reads at zero.

TL;DR

  • What: Amazon Bedrock can reuse a static prompt prefix so Claude doesn’t reprocess it on every call.
  • Why it matters: Agents and RAG apps resend the same system prompt, tools and documents on every turn.
  • What to do: Add cachePoint blocks after static content in the Converse API and log the cache usage fields.
  • Key stat: AWS says cache hits can cut input token costs by up to 90%.

Bedrock prompt caching lets Amazon Bedrock store a repeated prompt prefix, such as a system prompt, tool list or document, and read it back on later calls at a discounted rate. In Node.js, add a cachePoint block after the static content in a Converse request, keep everything before it byte-identical, and confirm hits with cacheReadInputTokens.

Bedrock prompt caching is an Amazon Bedrock feature that stores the processed state of a prompt prefix for a short time to live and reuses it when a later request starts with exactly the same prefix. Cached tokens are billed at a lower cache-read rate, writes can cost more than normal input, and the developer marks cacheable sections with checkpoints.

Bedrock prompt caching is the cheapest optimisation most Node.js teams running Claude on AWS haven’t switched on. Every chat turn and every agent step resends the same system prompt, tool schemas and reference documents. AWS’s own example puts a number on it. A 10,000-token contract sent with 50 user questions is 500,000 input tokens. All of them are billed at full price for content the model has already seen.

The feature is not automatic for explicit caching, and that is where developers get stuck. A GitHub issue on the Hermes agent project describes a Bedrock Converse integration that never sent cachePoint markers. Caching was “silently disabled”, and every turn paid full input price. Other teams add the markers and still see zero cache reads because one dynamic value sits in front of the checkpoint.

This guide covers the TypeScript setup, the pricing maths and the six reasons cache hits stay at zero.

What is Bedrock prompt caching and how does it work?

The cache stores the processed prefix before a checkpoint and reuses it when the next request starts with exactly the same content. The AWS documentation describes two modes. Implicit caching needs no markers but is best effort. Explicit caching uses checkpoints you place yourself. For Claude, explicit is the only option: requests without cache controls aren’t cached.

Three rules control every cache decision:

  • Order is fixed. Checkpoints are processed tools → system → messages, per AWS. Changing tools invalidates the system and message caches too.
  • There is a minimum size. Minimums range from 512 to 4,096 tokens per checkpoint, counted cumulatively across all three sections.
  • The TTL is short. Many models default to a 5-minute TTL that resets on every hit. Supported Claude models also offer 1 hour.

Claude models allow up to 4 checkpoints per request. Bedrock also checks earlier block boundaries automatically, looking back about 20 content blocks from your checkpoint to find the longest cached prefix. That lookback is why one checkpoint at the end of your static content is usually enough. Running several agents? Our guide to AI agent orchestration shows how each extra agent multiplies prefix resends.

How do you enable Bedrock prompt caching in Node.js?

You enable it in Node.js by adding { cachePoint: { type: "default" } } content blocks to the Converse API request through @aws-sdk/client-bedrock-runtime. No account setting or console toggle is needed for API calls.

  1. Install the current SDK: npm i @aws-sdk/client-bedrock-runtime. Current npm package types include an optional ttl on CachePointBlock. A re:Post thread shows older versions lacked it.
  2. Put the static content first: tool definitions, then the system prompt, then any fixed reference document.
  3. Append a cachePoint block after the last static item in each section you want cached.
  4. Put the user’s question and anything dynamic after the final checkpoint.
  5. Log cacheReadInputTokens and cacheWriteInputTokens from response.usage on every call.
import { readFileSync } from "node:fs";
import {
  BedrockRuntimeClient,
  ConverseCommand,
  type Tool,
} from "@aws-sdk/client-bedrock-runtime";

const client = new BedrockRuntimeClient({ region: "us-east-1" });
// Use the exact model or inference profile ID shown in your Bedrock console.
const MODEL_ID = process.env.BEDROCK_MODEL_ID!;

// Static: must be byte-identical on every request.
const SYSTEM_PROMPT = readFileSync("./prompts/support-agent.md", "utf8");

const tools: Tool[] = [
  {
    toolSpec: {
      name: "lookup_order",
      description: "Fetch an order by ID from MongoDB",
      inputSchema: {
        json: {
          type: "object",
          properties: { orderId: { type: "string" } },
          required: ["orderId"],
        },
      },
    },
  },
  { cachePoint: { type: "default" } }, // checkpoint 1: tools
];

export async function ask(question: string) {
  const res = await client.send(
    new ConverseCommand({
      modelId: MODEL_ID,
      toolConfig: { tools },
      system: [
        { text: SYSTEM_PROMPT },
        { cachePoint: { type: "default" } }, // checkpoint 2: tools + system
      ],
      messages: [{ role: "user", content: [{ text: question }] }],
      inferenceConfig: { maxTokens: 1024 },
    })
  );

  const u = res.usage ?? {};
  console.log({
    uncachedInput: u.inputTokens,
    cacheRead: u.cacheReadInputTokens ?? 0,
    cacheWrite: u.cacheWriteInputTokens ?? 0,
  });
  return res;
}

Call ask() twice within five minutes. The first call should show a large cacheWrite and zero cacheRead. The second should flip. In the Hermes issue’s verification run on Claude Opus 4.7, call one wrote 10,870 tokens and call two read 10,853 from cache.

Is there an automatic mode?

Yes. AWS documents automatic caching: one top-level cache_control field, passed in Converse through additionalModelRequestFields. Claude then moves the checkpoint forward as the conversation grows. It is supported on Haiku 4.5, Sonnet 4.6, Opus 4.7 and newer, and it uses one of the four checkpoint slots.

new ConverseCommand({
  modelId: MODEL_ID,
  system: [{ text: SYSTEM_PROMPT }],
  messages,
  additionalModelRequestFields: { cache_control: { type: "ephemeral" } },
});

Python and LangChain users

The JSON shape is identical in boto3. AWS notes that Boto3 1.43.0 or later is required for the ttl field, and LangChain’s ChatBedrockConverse.create_cache_point() inserts the same block for you.

How much does Bedrock prompt caching save?

Caching saves roughly 90% on every cached input token after the first write, minus a write premium. Anthropic’s pricing docs set the multipliers: 5-minute writes cost 1.25x base input, 1-hour writes cost 2x, and cache hits cost 0.1x on most models.

Here are Bedrock list prices from Anthropic’s platform rate card (effective 31 August 2026, USD per million tokens). Check the Amazon Bedrock pricing page for your Region before budgeting.

Claude on Amazon Bedrock: input and cache prices per million tokens (Standard tier)
Model and scope Base input 5-min write 1-hour write Cache hit Min tokens per checkpoint
Claude Sonnet 4.6, Global cross-Region $3.00 $3.75 $6.00 $0.30 1,024
Claude Sonnet 4.6, In-Region and Geo cross-Region $3.30 $4.125 $6.60 $0.33 1,024
Claude Haiku 4.5, Global cross-Region $1.00 $1.25 $2.00 $0.10 4,096
Claude Opus 4.8, Global cross-Region $5.00 $6.25 $10.00 $0.50 1,024
Claude Sonnet 5, Global cross-Region $2.00 $2.50 $4.00 $0.20 1,024

In-Region and Geo cross-Region endpoints cost 10% more on every line than Global cross-Region. And Haiku 4.5 needs 4,096 tokens before a checkpoint, four times Sonnet 4.6’s minimum.

A worked example with AWS’s 50-question scenario

Take the AWS example of a 10,000-token document and 50 questions. Use Sonnet 4.6 Global pricing from the rate card. Without caching, 500,000 prefix tokens at $3.00 per million cost $1.50. With a 5-minute cache, one write of 10,000 tokens costs $0.0375 and 49 reads of 490,000 tokens cost $0.147. That is $0.1845, about 88% less for the prefix. Output tokens are unchanged.

The break-even maths follows from the multipliers. A 5-minute write adds 0.25x, and one hit saves 0.9x, so it pays back after a single read. A 1-hour write adds 1x, so it needs two reads. A real session report in the Hermes issue measured about 84% lower cost: roughly $7.24 down to $1.13 over 25 API calls.

Why are Bedrock cache reads stuck at zero?

Cache reads stay at zero when the prefix before your checkpoint changes, is too short, or expires. A zero on call one is expected, because the cache must be written first. A zero on call two points to one of six bugs.

  1. Prefix below the minimum. AWS says the request still succeeds but nothing is cached. On Haiku 4.5, a 3,000-token system prompt will never cache.
  2. Dynamic content before the checkpoint. A timestamp, user name or request ID in the system prompt makes every prefix unique.
  3. Tools changed. Anthropic’s invalidation table shows that modifying tool definitions invalidates the entire cache, and changing tool_choice or adding images invalidates the message cache.
  4. Calls spaced beyond the TTL. An entry expires if no hit lands inside the window.
  5. No checkpoints sent. Wrappers and SDK adapters sometimes drop them, as the Hermes issue showed.
  6. Parallel fan-out. AWS explains that an entry lives on the server that processed the request. Requests sent before an identical request has started responding write a new entry instead of reading one.

The sixth one surprises teams that fire ten Promise.all calls with the same document. Warm the cache with one request, then fan out:

// First call writes the cache; the rest can read it
const [first, ...rest] = questions;
await ask(first);
const answers = await Promise.all(rest.map(ask));

The second bug is the most common one in real code. Here is the broken version:

// BROKEN: the date changes every request, so the cached prefix never matches
system: [
  { text: `Today is ${new Date().toISOString()}.\n${SYSTEM_PROMPT}` },
  { cachePoint: { type: "default" } },
],
messages: [{ role: "user", content: [{ text: question }] }],

Move the dynamic part after the checkpoint:

// FIXED: static system prompt is cached; volatile context rides in the message
system: [
  { text: SYSTEM_PROMPT },
  { cachePoint: { type: "default" } },
],
messages: [
  {
    role: "user",
    content: [
      { text: `Current time: ${new Date().toISOString()}` },
      { text: question },
    ],
  },
],

One more trap hides in your metrics. AWS states that inputTokens only counts non-cached tokens once caching is on. Total input is inputTokens + cacheReadInputTokens + cacheWriteInputTokens. A cost dashboard that multiplies inputTokens by the base rate will under-report spend and hide the write premium.

When should you use the 1-hour cache TTL?

Use the 1-hour TTL when the same prefix is reused less often than every 5 minutes but more often than every hour. AWS recommends it for slow chat users and long-running sub-agents. If a prompt is hit more often than every five minutes, the 5-minute cache keeps refreshing at no extra charge.

You can mix both in one request with one rule: longer TTLs must come before shorter ones. Cache your tools and system prompt for an hour, then the growing conversation for five minutes:

system: [
  { text: SYSTEM_PROMPT },
  { cachePoint: { type: "default", ttl: "1h" } },  // long-lived
],
messages: [
  ...history,
  { role: "user", content: [{ text: question }, { cachePoint: { type: "default" } }] }, // 5m
],

Only send ttl to models that support it. AWS warns that including it for a 5-minute-only model can cause a ValidationException. The Amazon Nova models are listed as 5-minute only.

How do you isolate cached prompts per tenant?

Isolate tenants by making each tenant’s cached prefix different. AWS’s guidance is that caches are scoped by account and Region, but within one account and Region entries might be shared across requests. Sharing a generic system prompt is harmless. Sharing tenant-specific context may not be acceptable.

AWS’s pattern prepends a SHA-256 hash of the tenant ID, which it says costs about 16 tokens of overhead:

import { createHash } from "node:crypto";

function tenantSystem(tenantId: string, tenantDocs: string) {
  const tag = createHash("sha256").update(tenantId).digest("hex");
  return [
    { text: `tenant:${tag}\n${SYSTEM_PROMPT}\n${tenantDocs}` },
    { cachePoint: { type: "default" as const } },
  ];
}

Every tenant now pays its own cache write. For a shared system prompt with per-tenant data, a two-checkpoint layout keeps the shared prefix cached once and isolates only the tenant block. Designing that split is the kind of decision covered in our AI solution architect skills map.

Does cross-region inference affect cache hits?

Yes, it can raise your cache-write count. AWS documents that caching works with cross-region inference, but at times of high demand routing “may lead to increased cache writes.” A request that lands in a different Region can’t read a cache written elsewhere.

My recommendation: start on a Global cross-Region profile, which is 10% cheaper per token. Then compare cache writes to reads over a week. If writes stay high during peak traffic, test an in-Region model ID for that workload.

The NexGismo verdict: every Node.js app sending Claude more than 1,024 tokens of repeated context through Bedrock should have explicit cachePoint blocks. I’d put one after tools and one after the system prompt. Keep timestamps out of both, and alert when daily cache writes exceed reads. The 1-hour TTL is worth it only for slow, long sessions.

  • Amazon Bedrock bills Claude cache hits at 0.1x base input on most Claude models, per Anthropic’s pricing docs.
  • Explicit caching needs cachePoint blocks in the Converse API; without them, every turn pays full input price.
  • Checkpoints are evaluated in the order tools, system, messages, and changing an earlier section invalidates later caches.
  • Claude Haiku 4.5 needs at least 4,096 tokens before a checkpoint, while Sonnet 4.6 needs 1,024.
  • With caching on, Bedrock’s inputTokens excludes cached tokens, so cost dashboards must add cache read and write fields.
  • Prompt caching is not available for Bedrock batch inference, only on-demand calls.

Frequently Asked Questions

Does Bedrock prompt caching work with cross-region inference?

Yes. AWS documents that prompt caching works together with cross-region inference profiles. The catch is that cross-region routing can send requests to different Regions during high demand, which AWS says may increase cache writes. Watch cacheWriteInputTokens on follow-up calls, and if it stays high, compare hit rates against an in-Region model ID.

How long does the Bedrock prompt cache last?

The default time to live is 5 minutes, and it resets every time a request hits the cache. Supported Claude models also accept a 1-hour TTL by adding ttl: “1h” to the cachePoint. If no request reuses the prefix inside the TTL window, the entry expires and the next call pays the cache-write price again.

Is prompt caching enabled by default on Amazon Bedrock?

Not for Claude. AWS states that Claude requests without cache controls aren’t cached, so you must add cachePoint blocks in Converse, cache_control in InvokeModel, or the top-level automatic cache_control field. Some other models, such as Amazon Nova, get best-effort implicit caching with no guaranteed hits. Always confirm with the usage fields.

Why is cacheReadInputTokens 0 in my Bedrock response?

The first call always writes rather than reads, so zero is normal on request one. If it stays zero, check six things. Is the prefix above the model’s minimum? Is a timestamp or other dynamic value before the cachePoint? Did tools change or calls exceed the TTL? Were parallel calls sent before the first one finished writing, or is your client dropping cachePoint blocks?

Does Bedrock prompt caching work with batch inference?

No. AWS states that prompt caching is only supported for on-demand inference and not with the batch inference API. Batch jobs already get discounted token prices on many Claude models. For offline workloads, compare the batch rate against on-demand pricing with cache reads.

Bedrock prompt caching is a small code change with a large bill impact. Add the checkpoints, move volatile context below them, and track the three usage fields from day one. Then let real traffic tell you whether the 1-hour TTL or a different Region pays off. If you hit a caching edge case not covered here, leave a comment with your usage numbers.

Sources