On the first-party Claude API, an image content block accepts three source types: base64 (bytes inline in the request), url (the platform fetches the image server-side), and file (a Files API file_id from a prior upload). Per Anthropic's vision documentation, Amazon Bedrock and Google Cloud currently support base64 only. Claude Platform on AWS, as the same-day-parity surface, supports the full set; Microsoft Foundry documents image support, with the Files API path limited to Hosted-on-Anthropic deployments (see the Files API portability gap).
Why the convenient sources exist
URL sources save you from downloading and re-uploading images your system references anyway (product photos on a CDN, say), and file_id sources keep request bodies small when the same image recurs across requests — helpful because you can hit the 32 MB request-size limit well before the per-request image-count limit. Losing both on Bedrock and Vertex means your application becomes the fetcher and the encoder.
The adapter layer
The clean pattern is a normalization function that converts any internal image reference into the richest source type the current platform supports, falling back to base64 by fetching and encoding:
import base64, mimetypes, urllib.request
def image_block(ref: str, platform: str) -> dict:
"""ref: an https URL or local path; platform: 'anthropic',
'claude_aws', 'bedrock', 'vertex', 'foundry'."""
if platform in ("anthropic", "claude_aws") and ref.startswith("https://"):
return {"type": "image", "source": {"type": "url", "url": ref}}
if ref.startswith("https://"):
data = urllib.request.urlopen(ref).read() # fetch ourselves
else:
data = open(ref, "rb").read()
media = mimetypes.guess_type(ref)[0] or "image/jpeg"
return {"type": "image", "source": {"type": "base64",
"media_type": media, "data": base64.b64encode(data).decode()}}
In production you'd add a timeout, retries, and content-type validation from the HTTP response rather than the filename — but the shape is the point: prompt-building code asks for "an image block for this reference" and never mentions source types.
Limits that differ once you go inline
Fetching and inlining moves you under stricter ceilings, and they differ by platform:
| Constraint | Claude API | Bedrock | Vertex AI |
|---|---|---|---|
| Max image size (base64) | 10 MB | 5 MB | 5 MB |
| Request payload limit | 32 MB | 20 MB | 30 MB |
| Images per request | 600 (100 on 200K-context models) | Vertex documents up to 100 images per request | |
Supported formats are JPEG, PNG, GIF, and WebP everywhere; animations aren't supported (only the first frame is used), and maximum dimensions are 8000×8000 pixels. Two cost notes worth wiring into the adapter: images are billed in 28×28-pixel patches (ceil(width/28) × ceil(height/28) visual tokens — a 1000×1000 image costs 1,296 tokens), and high-resolution models (Fable 5, Opus 4.8/4.7, Sonnet 5) accept up to a 2576-pixel long edge and can use roughly 3x more visual tokens per image than standard-resolution models. Downscaling before encoding — which the adapter is perfectly placed to do — cuts both payload size and token cost. Also mind Anthropic's many-image rule: requests with more than 20 image/document blocks impose a stricter 2000-pixel per-dimension limit.
file_id are optimizations to layer on where the platform supports them — never the assumption.Practical placement notes
Whatever the source type, Claude works best with images placed before text within the message content — keep that ordering in the prompt builder, not the adapter. And if your image set is large and repetitive on a platform with Files API support, prefer file_ids to keep payloads small; on Bedrock and Vertex, the equivalent play is caching the encoded base64 in your own store so you fetch and encode each source image once, not per request.
One more reason the fetch-and-encode path matters even on platforms that accept URL sources: a url source is fetched by the platform, server-side. Images that live behind your VPN, on internal hosts, or behind authentication aren't reachable that way, so internal assets go through your own fetch-and-encode path everywhere — the URL source is really for publicly reachable images only. Also note that inline image uploads are ephemeral: the bytes aren't stored beyond the request, which is friendly for data-handling reviews but means there's nothing to reuse server-side on the base64 path — reuse lives entirely in your adapter's cache.
Where to go next
The image inputs reference covers block anatomy and token math; the vision resize strategy quantifies the downscaling win; and the PDF source adapter applies this same pattern to document blocks.