Back to Blog
6 min read

Azure OpenAI vs OpenAI API: Which One Should You Choose?

With Azure OpenAI Service now GA, many teams are wondering: should we use Azure OpenAI or the OpenAI API directly? Let’s do a comprehensive comparison to help you decide.

Quick Comparison Matrix

FeatureOpenAI APIAzure OpenAI
AccessPublic signupRequest-based approval
ModelsGPT-4, GPT-3.5, DALL-E, WhisperGPT-3.5, Codex, DALL-E (GPT-4 coming)
Data PrivacyUsed for training (opt-out available)Not used for training
ComplianceSOC 2SOC 2, HIPAA, GDPR, FedRAMP
NetworkPublic internet onlyPrivate endpoints, VNet
SLABest effort99.9% uptime
SupportCommunity/paid tiersEnterprise support
PricingPay-as-you-goPay-as-you-go + commitments

Code Comparison

The APIs are similar but have key differences:

# OpenAI Direct API
import openai

openai.api_key = "sk-your-key-here"

# Using model name directly
response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello!"}
    ]
)

# Azure OpenAI API
import openai

openai.api_type = "azure"
openai.api_base = "https://your-resource.openai.azure.com/"
openai.api_version = "2023-03-15-preview"
openai.api_key = "your-azure-key"

# Using deployment name (not model name)
response = openai.ChatCompletion.create(
    engine="my-gpt35-deployment",  # Your deployment name
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Hello!"}
    ]
)

Building an Abstraction Layer

To support both backends, create an abstraction:

from abc import ABC, abstractmethod
from typing import List, Dict, Optional
from dataclasses import dataclass
import openai

@dataclass
class ChatMessage:
    role: str  # "system", "user", or "assistant"
    content: str

@dataclass
class ChatResponse:
    content: str
    tokens_used: int
    model: str
    finish_reason: str

class LLMProvider(ABC):
    """Abstract base class for LLM providers."""

    @abstractmethod
    def chat(self, messages: List[ChatMessage], **kwargs) -> ChatResponse:
        pass

    @abstractmethod
    def complete(self, prompt: str, **kwargs) -> str:
        pass

class OpenAIProvider(LLMProvider):
    """Direct OpenAI API provider."""

    def __init__(self, api_key: str, model: str = "gpt-3.5-turbo"):
        openai.api_key = api_key
        self.model = model

    def chat(self, messages: List[ChatMessage], **kwargs) -> ChatResponse:
        response = openai.ChatCompletion.create(
            model=self.model,
            messages=[{"role": m.role, "content": m.content} for m in messages],
            **kwargs
        )

        return ChatResponse(
            content=response.choices[0].message.content,
            tokens_used=response.usage.total_tokens,
            model=response.model,
            finish_reason=response.choices[0].finish_reason
        )

    def complete(self, prompt: str, **kwargs) -> str:
        response = openai.Completion.create(
            model="text-davinci-003",
            prompt=prompt,
            **kwargs
        )
        return response.choices[0].text

class AzureOpenAIProvider(LLMProvider):
    """Azure OpenAI API provider."""

    def __init__(
        self,
        endpoint: str,
        api_key: str,
        deployment: str,
        api_version: str = "2023-03-15-preview"
    ):
        openai.api_type = "azure"
        openai.api_base = endpoint
        openai.api_version = api_version
        openai.api_key = api_key
        self.deployment = deployment

    def chat(self, messages: List[ChatMessage], **kwargs) -> ChatResponse:
        response = openai.ChatCompletion.create(
            engine=self.deployment,
            messages=[{"role": m.role, "content": m.content} for m in messages],
            **kwargs
        )

        return ChatResponse(
            content=response.choices[0].message.content,
            tokens_used=response.usage.total_tokens,
            model=self.deployment,
            finish_reason=response.choices[0].finish_reason
        )

    def complete(self, prompt: str, **kwargs) -> str:
        response = openai.Completion.create(
            engine=self.deployment,
            prompt=prompt,
            **kwargs
        )
        return response.choices[0].text

# Factory function
def create_provider(provider_type: str, **config) -> LLMProvider:
    """Create an LLM provider based on configuration."""
    if provider_type == "openai":
        return OpenAIProvider(
            api_key=config["api_key"],
            model=config.get("model", "gpt-3.5-turbo")
        )
    elif provider_type == "azure":
        return AzureOpenAIProvider(
            endpoint=config["endpoint"],
            api_key=config["api_key"],
            deployment=config["deployment"]
        )
    else:
        raise ValueError(f"Unknown provider: {provider_type}")

# Usage - same code works with either provider
provider = create_provider(
    "azure",
    endpoint="https://my-openai.openai.azure.com",
    api_key="my-key",
    deployment="gpt35"
)

messages = [
    ChatMessage(role="system", content="You are a helpful assistant."),
    ChatMessage(role="user", content="What is Azure?")
]

response = provider.chat(messages, max_tokens=500)
print(response.content)

When to Choose OpenAI Direct

Choose OpenAI’s API when:

  1. Rapid Prototyping: Instant signup, no approval wait
  2. Latest Models: Access to GPT-4, Whisper, newer features first
  3. Consumer Applications: Less stringent compliance requirements
  4. Cost Flexibility: Pay-as-you-go without Azure subscription
# Quick prototype with OpenAI
import openai
openai.api_key = "sk-..."

# Access to latest models immediately
response = openai.ChatCompletion.create(
    model="gpt-4",  # GPT-4 available
    messages=[{"role": "user", "content": "Hello!"}]
)

When to Choose Azure OpenAI

Choose Azure OpenAI when:

  1. Enterprise Requirements: Compliance, SLA, support
  2. Data Privacy: Sensitive data that can’t leave your control
  3. Network Security: Need private endpoints, VNet integration
  4. Azure Integration: Already using Azure services
  5. Regulatory Compliance: HIPAA, FedRAMP, GDPR requirements
# Enterprise setup with Azure
from azure.identity import DefaultAzureCredential
import requests

class EnterpriseAzureOpenAI:
    """Enterprise-grade Azure OpenAI client."""

    def __init__(self, endpoint: str):
        self.endpoint = endpoint
        self.credential = DefaultAzureCredential()

    def _get_headers(self):
        token = self.credential.get_token(
            "https://cognitiveservices.azure.com/.default"
        )
        return {
            "Authorization": f"Bearer {token.token}",
            "Content-Type": "application/json"
        }

    def chat(self, deployment: str, messages: list):
        url = f"{self.endpoint}/openai/deployments/{deployment}/chat/completions"

        response = requests.post(
            url,
            headers=self._get_headers(),
            params={"api-version": "2023-03-15-preview"},
            json={"messages": messages}
        )

        return response.json()

Hybrid Architecture

Some organizations use both:

from enum import Enum
from typing import Optional

class WorkloadType(Enum):
    INTERNAL = "internal"      # Sensitive internal data
    EXTERNAL = "external"      # Public-facing, non-sensitive
    EXPERIMENTAL = "experimental"  # R&D, prototyping

class HybridLLMRouter:
    """Route requests to appropriate provider based on workload type."""

    def __init__(self):
        self.azure_provider = AzureOpenAIProvider(
            endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
            api_key=os.getenv("AZURE_OPENAI_KEY"),
            deployment="gpt35"
        )

        self.openai_provider = OpenAIProvider(
            api_key=os.getenv("OPENAI_API_KEY"),
            model="gpt-4"  # Use latest model for experiments
        )

    def chat(
        self,
        messages: List[ChatMessage],
        workload_type: WorkloadType = WorkloadType.INTERNAL,
        **kwargs
    ) -> ChatResponse:
        """Route chat request to appropriate provider."""

        if workload_type == WorkloadType.INTERNAL:
            # Sensitive data - must use Azure
            return self.azure_provider.chat(messages, **kwargs)

        elif workload_type == WorkloadType.EXPERIMENTAL:
            # R&D - use OpenAI for latest models
            return self.openai_provider.chat(messages, **kwargs)

        else:
            # External/non-sensitive - use cheaper option
            return self.azure_provider.chat(messages, **kwargs)

# Usage
router = HybridLLMRouter()

# Internal document analysis - routes to Azure
internal_response = router.chat(
    messages=[ChatMessage("user", "Summarize this HR document: ...")],
    workload_type=WorkloadType.INTERNAL
)

# Experimental feature - routes to OpenAI (GPT-4)
experiment_response = router.chat(
    messages=[ChatMessage("user", "Test this new prompt...")],
    workload_type=WorkloadType.EXPERIMENTAL
)

Cost Comparison

Pricing is similar but Azure offers committed use discounts:

def compare_costs(
    monthly_tokens: int,
    model: str = "gpt-3.5-turbo"
) -> dict:
    """Compare costs between providers."""

    # Pricing per 1K tokens (approximate, check current rates)
    pricing = {
        "gpt-3.5-turbo": {
            "openai": 0.002,
            "azure_paygo": 0.002,
            "azure_committed": 0.0015  # With provisioned throughput
        },
        "gpt-4": {
            "openai": 0.03,
            "azure_paygo": 0.03,
            "azure_committed": 0.025
        }
    }

    rates = pricing.get(model, pricing["gpt-3.5-turbo"])
    tokens_in_thousands = monthly_tokens / 1000

    return {
        "openai_monthly": tokens_in_thousands * rates["openai"],
        "azure_paygo_monthly": tokens_in_thousands * rates["azure_paygo"],
        "azure_committed_monthly": tokens_in_thousands * rates["azure_committed"],
        "savings_with_commitment": (
            tokens_in_thousands * rates["azure_paygo"] -
            tokens_in_thousands * rates["azure_committed"]
        )
    }

# Example: 10 million tokens/month
costs = compare_costs(10_000_000)
print(f"OpenAI: ${costs['openai_monthly']:.2f}/month")
print(f"Azure Pay-as-you-go: ${costs['azure_paygo_monthly']:.2f}/month")
print(f"Azure Committed: ${costs['azure_committed_monthly']:.2f}/month")
print(f"Savings with commitment: ${costs['savings_with_commitment']:.2f}/month")

Migration Considerations

If migrating from OpenAI to Azure:

  1. API Changes: model -> engine, authentication changes
  2. Deployment Model: Must deploy models before using
  3. Quotas: Different default quotas, may need increases
  4. Feature Parity: Some features may lag behind OpenAI

My Recommendation

  • Startups/Prototypes: Start with OpenAI for speed
  • Enterprise Production: Use Azure OpenAI for security and compliance
  • Best of Both: Build abstraction layer, use both strategically

The right choice depends on your specific requirements. For most enterprise scenarios, Azure OpenAI’s security and compliance features make it the clear winner, even if you have to wait for approval.

Resources

Michael John Peña

Michael John Peña

Senior Data Engineer based in Sydney. Writing about data, cloud, and technology.