Skip to content
ExponaBLOG
AWS FOR DEVELOPERS · DAY 095 / 100

Bedrock or a direct model API: a AWS guide for developers

Both work. Choose by constraints.

Expona Admin7 min read

Bedrock or a direct model API?

When you're building AI features into your AWS infrastructure, you'll eventually face a fundamental decision: should you route model requests through AWS Bedrock, or call model provider APIs directly? I've built production systems both ways, and the answer isn't obvious until you map your actual constraints.

Both approaches work. The choice comes down to your security model, team ownership, feature velocity, and wallet. Let me walk you through the real tradeoffs I've encountered.

Bedrock: IAM, VPC and AWS billing integration

AWS Bedrock is a managed service that abstracts model access behind your existing AWS infrastructure. You don't get API keys scattered across your codebase—you get IAM roles, VPC endpoints, and unified billing.

The security story is compelling. In my ride-hailing platform, we needed to call Claude for real-time ride description summaries without exposing OpenAI credentials to our application tier. Bedrock let us:

  • Attach an IAM role to our ECS task with minimal permissions
  • Route traffic through a VPC endpoint so model calls never left our network
  • Audit every invocation via CloudTrail
  • Consolidate billing with our AWS invoice

Here's what that looked like in practice:

bash
const client = new BedrockRuntimeClient({
  region: 'us-east-1'
});

const response = await client.send(new InvokeModelCommand({
  modelId: 'anthropic.claude-3-sonnet-20240229-v1:0',
  body: JSON.stringify({
    anthropic_version: 'bedrock-2023-06-01',
    max_tokens: 1024,
    messages: [
      {
        role: 'user',
        content: `Summarize this ride: ${rideDetails}`
      }
    ]
  })
}));

No credentials. No external secrets manager. Just IAM. That's worth something operationally.

The VPC endpoint isolation mattered most in our e-commerce platform where compliance required that model API calls never traverse the public internet. AWS Bedrock with PrivateLink was the only way to satisfy that without building a proxy layer ourselves.

Billing consolidation also eliminated vendor management. One invoice. One cost center. One contract. That simplicity scales when you have multiple teams building AI features.

The downside: Bedrock lags behind direct model APIs. Claude 3.5 Sonnet is available on Bedrock, but only after a delay. If your feature depends on the latest model capabilities or a model that Bedrock doesn't yet support, you're blocked.

Direct APIs: newest features first

When I was building a trading platform's market intelligence feature, we needed access to Claude 3.5 Sonnet the week Anthropic released it. Bedrock hadn't listed it yet. Direct API calls were the only option.

Provider APIs move faster. OpenAI's API, Anthropic's API, and others expose new models, features, and parameters weeks—sometimes months—before they appear on Bedrock. If you're optimizing for feature velocity or need capabilities not yet available through AWS, direct APIs are your path forward.

Here's the equivalent code calling Anthropic directly:

bash
import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY
});

const response = await client.messages.create({
  model: 'claude-3-5-sonnet-20241022',
  max_tokens: 1024,
  messages: [
    {
      role: 'user',
      content: `Analyze market sentiment: ${marketData}`
    }
  ]
});

Simpler syntax. Direct feature access. But now you own credential management, rate limiting, and retries.

The tradeoff: you lose the AWS infrastructure story. API keys live in your secrets manager. Requests cross the public internet. Billing is separate. Audit trails require custom logging. Your team manages a new vendor relationship.

For our SaaS platform, we accepted this because our feature required models that Bedrock didn't support yet, and our security posture was based on encryption in transit and strict role-based access, not network isolation.

Abstract the provider in your code

The smartest choice I've made in production: never hardcode a provider. Build an abstraction layer that lets you swap providers without touching business logic.

Here's why: you might start with Bedrock for security, then migrate to direct APIs for feature velocity. Or vice versa. You might use Bedrock for some models and direct APIs for others. Your abstraction should make that possible.

A simple interface looks like this:

bash
interface ModelProvider {
  invoke(params: {
    modelId: string;
    prompt: string;
    maxTokens?: number;
  }): Promise;
}

class BedrockProvider implements ModelProvider {
  async invoke(params) {
    const client = new BedrockRuntimeClient();
    const response = await client.send(new InvokeModelCommand({
      modelId: params.modelId,
      body: JSON.stringify({
        max_tokens: params.maxTokens || 1024,
        messages: [{ role: 'user', content: params.prompt }]
      })
    }));
    return JSON.parse(response.body.toString()).content[0].text;
  }
}

class DirectApiProvider implements ModelProvider {
  async invoke(params) {
    const client = new Anthropic({
      apiKey: process.env.ANTHROPIC_API_KEY
    });
    const response = await client.messages.create({
      model: params.modelId,
      max_tokens: params.maxTokens || 1024,
      messages: [{ role: 'user', content: params.prompt }]
    });
    return response.content[0].text;
  }
}

Now your business logic doesn't care which provider you're using:

bash
const provider = process.env.MODEL_PROVIDER === 'bedrock' 
  ? new BedrockProvider() 
  : new DirectApiProvider();

const summary = await provider.invoke({
  modelId: 'claude-3-sonnet',
  prompt: userInput,
  maxTokens: 500
});

I've used this pattern in three production systems. It's saved me weeks of refactoring when requirements changed or models became available on new platforms.

Compare latency and pricing

Numbers matter. Let me break down what I've observed in production.

Latency: Direct APIs are typically 50–200ms faster because they skip AWS's invocation overhead. Bedrock adds a layer of indirection. For our CRM platform processing customer inquiries, the difference was noticeable: 200ms vs 350ms. That mattered for user-facing features. For async batch jobs, it didn't.

Pricing: Bedrock charges per 100K input and output tokens, with a small markup over provider pricing. Direct APIs charge provider rates directly. Bedrock's markup varies by model but often runs 10–20% higher. Over 10 million tokens monthly (plausible for any serious AI feature), that's $300–600 extra.

Here's a comparison for our actual usage:

Method 10M input tokens 5M output tokens Total monthly
Direct Claude API $3.00 $15.00 $18.00
Bedrock (with ~15% markup) $3.45 $17.25 $20.70
Difference — — +$2.70

Scale to 100M input tokens (which we hit with our travel platform's itinerary AI), and you're looking at $270 extra monthly. That compounds.

However, don't let a few dollars override security requirements. In our compliance-heavy e-commerce system, the cost of Bedrock was trivial compared to the cost of satisfying audit requirements. The VPC endpoint isolation was worth every penny of that markup.

Common mistakes

Mistake 1: Choosing based on provider alone. I've seen teams pick Bedrock because "it's AWS" without checking if their needed model was available. They built features, hit GA, and discovered the model they needed wasn't on Bedrock yet. Start with a compatibility matrix.

Mistake 2: Ignoring credential sprawl. Direct APIs mean API keys everywhere: environment files, secrets managers, rotation policies. If you're not disciplined about credential lifecycle, you'll leak keys. Use a centralized secrets manager and enforce rotation. Bedrock removes this burden entirely.

Mistake 3: No abstraction layer. Hardcoding provider-specific code makes migrations painful. Even if you're 100% sure you'll never change providers, you'll waste weeks refactoring when you inevitably do.

Mistake 4: Overlooking quota management. Both Bedrock and direct APIs have rate limits. Bedrock's are managed by AWS; direct APIs are managed by the provider. Build retry logic with exponential backoff. Queue long-running requests. Don't assume unlimited throughput.

Mistake 5: Not benchmarking latency locally. I've built features where model latency dominated the critical path, and I didn't know it until production. Test with real token volumes. 50ms overhead compounds when you're chaining multiple calls.

Key takeaways

  • Bedrock wins for security and consolidation. IAM, VPC endpoints, unified billing, and CloudTrail auditing. Choose Bedrock if your constraint is infrastructure security or compliance.
  • Direct APIs win for velocity and features. Access new models first, avoid markup costs, and skip credential management overhead. Choose direct APIs if your constraint is feature freshness or cost at scale.
  • Build an abstraction layer. One interface, multiple implementations. Swapping providers should be a config change, not a refactor.
  • Model availability is the tiebreaker. If your target model isn't on Bedrock, the decision is made. Check AWS Bedrock's model list first.
  • Latency and pricing matter, but not as much as security and compliance. The 50ms latency difference and 15% cost markup are real, but they're secondary to whether your deployment architecture meets your security requirements.
  • Start with a matrix. Map your models, security constraints, compliance requirements, and expected token volume. Let the data decide, not the brand.
ShareXLinkedInReddit

Found this useful? Support the writing.

Buy me a coffee
Written by
Expona Admin
Site administrator

Comments

Loading comments…

Leave a comment

Comments are reviewed before they appear. Be kind and stay on topic.

Related articles