Last9

OpenTelemetry API vs SDK: Understanding the Architecture

Understand how the OpenTelemetry API and SDK work together, clean instrumentation in code, and flexible data processing in configuration.

Aug 25th, ‘25
OpenTelemetry API vs SDK: Understanding the Architecture
See How Last9 Works

Unified observability for all your telemetry. Open standards. Simple pricing.

Talk to an Expert

When you're instrumenting applications with OpenTelemetry, you'll encounter two core components: the API and the SDK. The API defines what telemetry data looks like and how it is created, while the SDK handles how that data is processed and exported.

Understanding this split helps you build more maintainable observability and avoid tight coupling between your business logic and telemetry infrastructure.

This post breaks down the practical differences, talks about when to use each component, and covers patterns that keep your instrumentation clean and flexible.

What Is the OpenTelemetry API?

The OpenTelemetry API provides the interface for creating telemetry data in your application code. It defines the contracts for traces, metrics, and logs without implementing the underlying behavior.

Consider it as the specification layer; it tells you what methods are available, but doesn't dictate how they work. The API handles context propagation automatically, ensuring trace context flows through your application and across service boundaries.

When you start a span, the API manages parent-child relationships and carries baggage items that need to propagate with requests. The span context contains the trace ID, span ID, and trace flags that identify the request across distributed traces.

Here's what the API gives you:

from opentelemetry import trace

# Get a tracer from the API
tracer = trace.get_tracer(__name__)

# Create spans using API methods
with tracer.start_as_current_span("process_order") as span:
    span.set_attribute("order.id", order_id)
    span.set_attribute("order.amount", amount)
    # Your business logic here

The API layer stays stable across OpenTelemetry versions, which means your instrumentation code won't break when you upgrade SDKs or switch between different SDK implementations. You're calling the same methods whether you're using the Python SDK, a custom implementation, or even a no-op version for testing.

💡
If you’re configuring your SDK to send data through a collector, this guide on the OpenTelemetry Collector explains how it processes and routes telemetry.

What Is the OpenTelemetry SDK?

The OpenTelemetry SDK is the implementation layer of the OpenTelemetry specification. If the OpenTelemetry API defines what telemetry data can be captured (metrics, traces, logs) and how instrumentation code should look, the SDK is the part that actually makes it work. It provides the logic to collect, process, batch, and export telemetry to your chosen backend.

In short, the API is the contract, the SDK is the engine.

Instrumentation: Adding Data Collection to Your App

Instrumentation is how you insert telemetry capture points into your code. The SDK supports both manual and automatic instrumentation, giving you flexibility in how you capture data.

Manual instrumentation gives you full control over what gets measured:

from opentelemetry import metrics
import time
meter = metrics.get_meter(__name__)
request_counter = meter.create_counter(
    "http_requests_total",
    description="Total HTTP requests",
    unit="1"
)
order_processing_histogram = meter.create_histogram(
    "order_processing_duration",
    description="Time spent processing orders",
    unit="s"
)
def handle_request(request):
    request_counter.add(1, {"method": request.method, "endpoint": request.path})
    
    start_time = time.time()
    try:
        result = process_request(request)
        request_counter.add(1, {"status": "success"})
        return result
    finally:
        duration = time.time() - start_time
        order_processing_histogram.record(duration)

Auto-instrumentation captures telemetry from supported libraries without code changes. For Python apps, you can instrument frameworks like Flask, Django, and requests with one command:

opentelemetry-instrument python myapp.py

Where the SDK Fits in

While the API defines where you place measurement points, the SDK decides:

  • How metrics are aggregated — in-process or exported raw for backend aggregation
  • What traces are collected via sampling
  • When data is batched and exported
  • Where telemetry gets sent (Jaeger, Prometheus, OTLP Collector, etc.)

The SDK also handles resource detection, span lifecycle management, error handling when exports fail, and efficient batching to keep resource usage predictable.

SDK Building Blocks

  • Tracer Provider – Creates and manages tracers.
  • Span Processors – Handle span lifecycle events, such as batching before export.
  • Samplers – Decide which traces to keep.
  • Exporters – Send telemetry to your observability backend.
  • Resource – Identifies the service, version, and environment.

Example: Configuring a Tracer with OTLP Export

from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
trace.set_tracer_provider(TracerProvider())
tracer_provider = trace.get_tracer_provider()
otlp_exporter = OTLPSpanExporter(
    endpoint="http://localhost:4317",
    headers={"authorization": "Bearer your-token"}
)
span_processor = BatchSpanProcessor(otlp_exporter)
tracer_provider.add_span_processor(span_processor)
tracer = trace.get_tracer(__name__)

Key Architectural Differences Between the API and SDK

OpenTelemetry’s API and SDK are designed to work together, but they have very different responsibilities.

1. Separation of Concerns

API → Your Code’s Interface to Telemetry
The API is a contract. It defines how your application asks to record telemetry, starting spans, recording metrics, and logging events — without embedding any logic about what happens to that data.

Example:

span = tracer.start_span("checkout")

From your code’s point of view, this call “records a span.”
From reality’s point of view, it could:

  • Be sent to a Jaeger backend
  • Be exported via OTLP to a collector that fans it out to multiple systems
  • Be dropped entirely because of sampling rules
  • Be processed locally in-memory for a debug build

SDK → The Operational Engine
The SDK is where the actual work happens:

  • Batching spans to reduce network chatter and CPU usage
  • Sampling to control telemetry volume
  • Retry logic to handle flaky exporters
  • Exporters to send data to different backends

Because of this separation, you can swap your backend from Jaeger to Tempo or Last9 without touching application code — just change the SDK configuration.

💡
If you’re aiming to keep logs, metrics, and traces connected—especially under stress conditions—this guide on designing telemetry you can trust shows how consistent signal modeling and correlation strategies help you actually debug when things go sideways.

2. Configuration: Static vs. Environment-Specific

API Configuration: Minimal and Stable
You set up tracers and meters once, usually during application startup, and they don’t change unless your instrumentation changes:

from opentelemetry import trace, metrics

tracer = trace.get_tracer("user-service", "1.2.0")
meter = metrics.get_meter("user-service", "1.2.0")

This is code-level configuration. It travels with your source code and rarely changes across environments.

SDK Configuration: Flexible and Deployment-Specific
The SDK’s settings adapt to the environment you’re running in. This is where you tune sampling rates, add metadata about your service, and configure exporters.

Example:

from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.sampling import TraceIdRatioBased
from opentelemetry.sdk.resources import Resource
import socket

# Sample 10% of traces
sampler = TraceIdRatioBased(0.1)

# Add service metadata for better trace context
resource = Resource.create({
    "service.name": "user-service",
    "service.version": "1.2.0",
    "deployment.environment": "production",
    "service.instance.id": socket.gethostname()
})

tracer_provider = TracerProvider(sampler=sampler, resource=resource)

Here’s the practical impact:

  • Production might use 10% sampling for cost control.
  • Staging might run 100% sampling for validation.
  • Local development might use an in-memory exporter for quick feedback.

3. Performance Impact and Tuning

API Overhead: Minimal
If you call the API without configuring an SDK, it effectively becomes a no-op — your code still compiles and runs, but no telemetry is recorded. This is intentional, so you can safely ship instrumentation to environments where observability isn’t needed.

SDK Overhead: Real but Tunable
The SDK is where CPU, memory, and network costs live:

  • Span Processors consume memory to buffer pending exports.
  • Exporters serialize and send telemetry over the network.
  • Samplers run logic on every trace start to decide whether to keep it.

You can reduce this impact by:

  • Lowering batch sizes (less memory usage, potentially more network calls)
  • Increasing export intervals (fewer network calls, more data buffered)
  • Adjusting sampling rates (direct control over volume and cost)

For high-throughput services, these tuning knobs are critical — misconfigured SDK settings can easily add milliseconds of latency or spikes in CPU usage.

4. Why This Matters

If you’re a developer:

  • The API is what you use when writing business logic. It should be stable and not require constant changes.
  • The SDK is what your ops or platform team tweaks when telemetry needs to be routed differently, scaled, or optimized.

If you’re a platform owner:

  • The API lets you standardize instrumentation across services.
  • The SDK lets you standardize operational behavior without forcing developers to change their code.
💡
Prometheus 3.0 introduces changes that make OTLP metric exports more accurate—here’s how it fixes resource attributes for OpenTelemetry users.

When to Use API vs SDK Components

OpenTelemetry’s API and SDK aren’t interchangeable — they’re meant for different layers of your application lifecycle. The API is for writing instrumentation, the SDK is for running it.

Use the API for Instrumentation

The API is your application’s interface to OpenTelemetry. Write all instrumentation — manual or library-based, against the API. This ensures:

  • Portability – You can swap exporters or collectors without changing your business logic.
  • Testability – You can run unit tests without pulling in heavy SDK dependencies.
  • Minimal Overhead – If no SDK is configured, API calls become no-ops.

Example: manual instrumentation for a payment service.

from opentelemetry import trace
import logging, time

logger = logging.getLogger(__name__)

def process_payment(order_id, amount):
    tracer = trace.get_tracer(__name__)
    
    with tracer.start_as_current_span("process_payment") as span:
        # Add contextual attributes
        span.set_attribute("payment.order_id", order_id)
        span.set_attribute("payment.amount", amount)
        span.add_event("payment_started", {"timestamp": time.time()})
        
        try:
            result = charge_card(amount)
            span.set_attribute("payment.result", "success")
            return result
        except PaymentError as e:
            span.set_status(trace.Status(trace.StatusCode.ERROR, str(e)))
            logger.error(f"Payment failed for order {order_id}: {e}")
            raise

This approach:

  • Captures business-level events (payment_started)
  • Adds rich attributes for later filtering (payment.order_id)
  • Produces data that will appear in distributed traces for cross-service correlation

Use the SDK for Configuration

The SDK is where you wire up the pipeline — what gets sampled, where it’s exported, and how often it’s batched. This happens at application startup or via deployment configuration (e.g., environment variables, config files).

Example: centralized telemetry setup.

# config/telemetry.py
from opentelemetry import trace, metrics
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.sdk.metrics import MeterProvider
from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter

def setup_telemetry(service_name, otlp_endpoint):
    # Configure tracing
    trace.set_tracer_provider(TracerProvider())
    tracer_provider = trace.get_tracer_provider()
    
    otlp_exporter = OTLPSpanExporter(endpoint=otlp_endpoint)
    tracer_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
    
    # Configure metrics
    metrics.set_meter_provider(MeterProvider())
    meter_provider = metrics.get_meter_provider()
    
    metric_exporter = OTLPMetricExporter(endpoint=otlp_endpoint)
    meter_provider.add_metric_reader(
        PeriodicExportingMetricReader(metric_exporter)
    )

Why this is important:

  • Operational flexibility – Change exporters or sampling rates without touching code.
  • Environment-specific tuning – Production might use OTLP → collector, staging might log to console.
  • Centralized configuration – Keeps application code clean.

Working with Multiple Backends

The API abstraction means instrumentation doesn’t care how many places your telemetry is going. The SDK can fan out to multiple destinations in parallel.

Example: Exporting to both Jaeger and OTLP.

from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter

jaeger_exporter = JaegerExporter(
    agent_host_name="jaeger",
    agent_port=6831,
)
otlp_exporter = OTLPSpanExporter(
    endpoint="http://otel-collector:4317"
)

tracer_provider.add_span_processor(BatchSpanProcessor(jaeger_exporter))
tracer_provider.add_span_processor(BatchSpanProcessor(otlp_exporter))

With this setup:

  • Your payment span from above shows up in Jaeger for developers to debug the latency
  • The same span flows to an OTLP collector for long-term storage and correlation with metrics/logs

Comparing Application Insights SDK and OpenTelemetry

Both Application Insights SDK and OpenTelemetry help you collect application telemetry — but their design goals, flexibility, and integration paths differ significantly.

1. Telemetry Data Handling

Application Insights SDK

  • Automatically collects telemetry for .NET, Java, Node.js, and Python.
  • Destination is fixed: all telemetry is sent to Azure Monitor.
  • Uses a proprietary API and schema.
// Application Insights approach
services.AddApplicationInsightsTelemetry();

// Fixed destination: always Azure Monitor
public void TrackEvent(string eventName, Dictionary<string, string> properties)
{
    telemetryClient.TrackEvent(eventName, properties);
}

OpenTelemetry

  • Also supports automatic instrumentation, but via vendor-neutral APIs.
  • You choose where data goes: Azure Monitor, Jaeger, Prometheus, Last9, etc.
# OpenTelemetry approach
with tracer.start_as_current_span("operation") as span:
    span.set_attribute("custom.property", value)

2. Semantic Conventions and Schema Stability

  • Application Insights uses Microsoft-specific attribute names and conventions.
    Migrating to OpenTelemetry or enabling multi-cloud observability requires remapping attributes and handling schema differences.
  • OpenTelemetry uses standardized semantic conventions supported across vendors and languages.
    Changes are gradual — breaking changes go through a formal deprecation process with migration documentation.

3. Implementation Flexibility

Application Insights SDK

  • Tightly coupled to Azure Monitor.
  • Microsoft now offers an OpenTelemetry-compatible distribution that can export to Application Insights, but flexibility is still limited.

OpenTelemetry

  • Full control over exporters, processors, and pipelines.
  • You can send the same telemetry to Application Insights and other backends.
from azure.monitor.opentelemetry.exporter import AzureMonitorTraceExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor

# Send OpenTelemetry data to Application Insights
azure_exporter = AzureMonitorTraceExporter(
    connection_string="InstrumentationKey=your-key"
)
tracer_provider.add_span_processor(BatchSpanProcessor(azure_exporter))

4. Open Source and Ecosystem

  • Application Insights – Proprietary, slower to adopt community-driven standards.
  • OpenTelemetry – Open source, broad language and vendor support, faster iteration.
  • No vendor lock-in: switch backends without touching instrumentation.

5. Testing and Development Workflows

The API/SDK split in OpenTelemetry makes testing and dev environments easier:

Testing Without Real Backends
You can skip SDK configuration for no-op behavior in tests, or plug in a custom in-memory exporter to validate spans.

from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.test.spantestutil import SpanTestCase

class TestPaymentInstrumentation(SpanTestCase):
    def setUp(self):
        tracer_provider = TracerProvider()
        tracer_provider.add_span_processor(
            SimpleSpanProcessor(self.memory_exporter)
        )
        trace.set_tracer_provider(tracer_provider)
    
    def test_payment_span_attributes(self):
        process_payment("order-123", 29.99)
        spans = self.memory_exporter.get_finished_spans()
        self.assertEqual(spans[0].attributes["payment.order_id"], "order-123")

Lightweight Local Development
For quick feedback without running an observability stack, send telemetry to stdout:

from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

console_exporter = ConsoleSpanExporter()
tracer_provider.add_span_processor(SimpleSpanProcessor(console_exporter))

A Quick Summary

Feature / Aspect Application Insights SDK OpenTelemetry SDK
Default Backend Azure Monitor only Any backend (Azure, Jaeger, Prometheus, Last9, etc.)
Instrumentation API Microsoft-specific Vendor-neutral, standard semantic conventions
Schema Stability Tied to Microsoft updates Open standard, formal deprecation process
Vendor Lock-in Yes No
Testing Flexibility Limited Built-in no-op mode, custom exporters
Ecosystem Proprietary Large open-source community
💡
If you’re using Serilog today, here’s how you can hook it up to OpenTelemetry and keep your logs, metrics, and traces connected: Serilog and OpenTelemetry.

Backend Integration and Pipeline Configuration

In OpenTelemetry, the SDK’s exporter and pipeline components bridge the gap between your instrumented application and your observability backends. Exporters know how to talk to different systems (OTLP, Prometheus, Jaeger, Zipkin, etc.), while pipeline components manage how data moves — buffering, batching, retrying, and sending.

You can run multiple exporters in parallel, so the same telemetry stream feeds several systems at once.

Common Exporter Types

1. OTLP Exporters

The OpenTelemetry Protocol (OTLP) is the native way to send telemetry to collectors or compatible backends.

from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.exporter.otlp.proto.grpc.metric_exporter import OTLPMetricExporter

trace_exporter = OTLPSpanExporter(
    endpoint="http://otel-collector:4317",
    headers={"api-key": "your-api-key", "schema-version": "1.0"}
)

metric_exporter = OTLPMetricExporter(
    endpoint="http://otel-collector:4317"
)

Why use OTLP?

  • Works with any OpenTelemetry Collector or OTLP-compatible backend
  • Supports gRPC and HTTP transports
  • Can carry authentication headers and custom metadata

2. Prometheus Exporters

For teams already invested in Prometheus, you can expose metrics directly in Prometheus format.

from opentelemetry.exporter.prometheus import PrometheusMetricReader

prometheus_reader = PrometheusMetricReader()
meter_provider.add_metric_reader(prometheus_reader)

This creates an HTTP scrape endpoint so your Prometheus server can pull metrics without additional infrastructure.

3. Jaeger and Zipkin Exporters

Both Jaeger and Zipkin are popular tracing backends, but they use different protocols.

from opentelemetry.exporter.jaeger.thrift import JaegerExporter
from opentelemetry.exporter.zipkin.proto.http import ZipkinExporter

jaeger_exporter = JaegerExporter(
    agent_host_name="jaeger-agent",
    agent_port=6831,
)

zipkin_exporter = ZipkinExporter(
    endpoint="http://zipkin:9411/api/v2/spans"
)

You might use these when:

  • You already have Jaeger or Zipkin in production
  • You want to compare the trace visualization between tools
  • You’re migrating to OTLP but keeping legacy compatibility

Pipeline Configuration

Exporters handle where the telemetry goes. Pipelines control how it gets there.

A BatchSpanProcessor, for example, buffers spans in memory and sends them in configurable batches — reducing network calls and smoothing load spikes.

from opentelemetry.sdk.trace.export import BatchSpanProcessor

batch_processor = BatchSpanProcessor(
    trace_exporter,
    max_queue_size=2048,
    schedule_delay_millis=500,
    max_export_batch_size=512,
    export_timeout_millis=30000
)

Tuning tips:

  • max_queue_size – Increase for high-throughput apps to avoid dropping data.
  • schedule_delay_millis – Lower for near-real-time exports, raise to save bandwidth.
  • max_export_batch_size – Prevents single flushes from being too large.
  • export_timeout_millis – Controls how long to wait before abandoning a batch.

Kubernetes Deployment Patterns

In Kubernetes, it’s common to send telemetry to an OpenTelemetry Collector running:

  • As a sidecar – per pod, keeping data processing close to the source.
  • As a DaemonSet – per node, collecting from all workloads on that node.
  • As a centralized service – one collector for the whole cluster.

The collector can then fan out telemetry to multiple backends based on a YAML routing config, without changing app code.

Testing and Development Patterns

One of the underrated benefits of OpenTelemetry’s API/SDK split is how it simplifies testing and local development. Since instrumentation calls the API, you can run tests without the SDK at all (no telemetry overhead) or plug in lightweight exporters for controlled verification.

1. Testing Instrumentation Without a Backend

For unit and integration tests, you often want to validate that spans are created and tagged correctly — without spinning up a collector or backend.

Using an in-memory span exporter lets you capture spans inside your test process:

# test_payment.py
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.test.spantestutil import SpanTestCase

class TestPaymentInstrumentation(SpanTestCase):
    def setUp(self):
        tracer_provider = TracerProvider()
        tracer_provider.add_span_processor(
            SimpleSpanProcessor(self.memory_exporter)  # Provided by SpanTestCase
        )
        trace.set_tracer_provider(tracer_provider)
    
    def test_payment_span_attributes(self):
        process_payment("order-123", 29.99)
        
        spans = self.memory_exporter.get_finished_spans()
        payment_span = spans[0]
        
        self.assertEqual(payment_span.name, "process_payment")
        self.assertEqual(payment_span.attributes["payment.order_id"], "order-123")
        self.assertEqual(payment_span.attributes["payment.amount"], 29.99)

Why it’s useful:

  • No need for network calls or backend services
  • Fully deterministic test environment
  • Immediate feedback on instrumentation changes

2. Local Development Without a Full Observability Stack

When you just want to see spans and metrics instantly while coding, the ConsoleSpanExporter prints telemetry in a human-readable format to stdout.

from opentelemetry.sdk.trace.export import ConsoleSpanExporter, SimpleSpanProcessor

console_exporter = ConsoleSpanExporter()
tracer_provider.add_span_processor(SimpleSpanProcessor(console_exporter))

This is ideal for:

  • Debugging span structure and attributes
  • Validating that automatic instrumentation is picking up expected operations
  • Avoiding the setup cost of a collector during rapid prototyping

3. Switching Between Modes

You can keep the same instrumentation code, but choose exporters dynamically:

  • No SDK – API calls become no-ops, zero performance impact.
  • Test Exporter – Validate spans in memory for assertions.
  • Console Exporter – Debug locally without backend services.
  • OTLP Exporter – Send production traffic to a collector/backend.

This flexibility means your observability setup can adapt to:

  • Development – quick iteration with console output
  • CI/CD pipelines – run tests with in-memory exporters
  • Production – high-performance, batched OTLP exports

Choosing Between API and SDK Approaches

Your focus in OpenTelemetry depends on what role you play and what problems you’re solving:

  • Application Developers → Instrument with the API.
    Add spans, metrics, and events in code so that business operations are observable — without worrying about where or how the data is stored.
  • Platform / Observability Engineers → Configure and tune the SDK.
    Optimize exporters, sampling, batching, and backend integrations for scale, cost, and performance.

This separation keeps application logic independent from observability operations — allowing either side to evolve without breaking the other.

💡
Before you decide whether to send telemetry straight from your app or route it through a collector, read this Collector vs Exporter guide—it’ll help you pick the setup that saves you both effort and headaches later.

Environment Variables for Configuration

OpenTelemetry supports rich environment variable configuration, letting you change exporter endpoints, service metadata, and sampling rates without code changes.

This is especially powerful in Kubernetes deployments, where you can set these in YAML manifests or Helm charts:

# Identify the service
export OTEL_SERVICE_NAME=user-service
export OTEL_SERVICE_VERSION=1.2.0

# Configure OTLP exporter
export OTEL_EXPORTER_OTLP_ENDPOINT=http://collector:4317
export OTEL_EXPORTER_OTLP_HEADERS="api-key=your-key,schema-version=1.0"

# Configure sampling (10% of traces)
export OTEL_TRACES_SAMPLER=traceidratio
export OTEL_TRACES_SAMPLER_ARG=0.1

Why it matters:

  • No need to redeploy code to change telemetry behavior
  • Environment-specific tuning (e.g., staging = 100% sampling, production = 10%)
  • Works consistently across all OpenTelemetry-supported languages

Versioning and Compatibility

  • API Changes → Rare and follow semantic versioning. Breaking changes are infrequent.
  • SDK Changes → More frequent due to new exporters, processors, and performance improvements.

Best practice:

  • Pin API versions conservatively to avoid instrumentation churn.
  • Update SDK versions more aggressively to gain performance fixes and backend compatibility.
  • Always review OpenTelemetry release notes for potential breaking changes in exporters or processors.

Error Handling in the SDK

SDK configuration is also where you protect your app from telemetry-related failures. You can control timeouts, retries, and queue behavior so that telemetry never blocks core business logic.

from opentelemetry.sdk.trace.export import BatchSpanProcessor

processor = BatchSpanProcessor(
    exporter,
    export_timeout_millis=10000,  # Timeout on exports
    max_queue_size=2048,          # Drop spans if queue fills
    schedule_delay_millis=1000    # Frequent flush to reduce data loss
)

SDK Responsibilities Beyond Exporting

The SDK isn’t just a delivery mechanism — it also:

  • Handles HTTP/gRPC requests to exporters
  • Maintains accurate timestamps for telemetry events
  • Preserves span context across service and network boundaries
  • Coordinates batching and memory management for high-throughput scenarios

This is why SDK tuning is critical for production — it’s the difference between a low-overhead observability pipeline and one that slows down your app under load.

Wrapping Up

OpenTelemetry’s architecture makes it easy to split responsibilities — instrument once with the API, then fine-tune exports, batching, and sampling in the SDK without touching your application code. This separation means you can swap or scale backends without rewriting instrumentation.

That’s exactly where Last9 fits. Since Last9 is fully OpenTelemetry-native, you can keep your API-level instrumentation intact while the SDK sends data directly to Last9 for:

  • 20M+ active time series per metric without sampling, so you don’t have to drop dimensions.
  • Sub-second queries for high-cardinality workloads, even under heavy load.
  • Unified metrics, logs, and traces so context is preserved from your spans to your dashboards.
  • Real-time cost controls so SDK configuration changes don’t trigger surprise bills.

Instrument with the API, configure your SDK once, and let Last9 handle the scale, performance, and cost side — no re-instrumentation required.

FAQs

What is the difference between API and SDK?

The API defines the interface for creating telemetry data—it's the contract your code calls. The SDK implements that interface and handles processing, sampling, and exporting the data to backends. The API stays stable while SDKs can be swapped or upgraded.

What is the difference between Application Insights SDK and OpenTelemetry?

Application Insights SDK is Microsoft's proprietary telemetry solution that sends data directly to Azure Monitor. OpenTelemetry is vendor-neutral and can export to any backend. You can use OpenTelemetry to send data to Application Insights, but the reverse isn't true.

What is SDK in OpenTelemetry?

The OpenTelemetry SDK is the implementation layer that processes telemetry data created through the API. It includes tracer providers, span processors, samplers, exporters, and resource detection—everything needed to collect and ship telemetry to your observability backend.

What is the Djangotelemetry SDK?

A telemetry SDK is the runtime component that handles collecting, processing, and exporting observability data from your application. It bridges the gap between instrumentation code (API calls) and your monitoring infrastructure.

What is a SDK vs API?

An API defines what you can do (the interface), while an SDK provides the implementation of how it's done. In OpenTelemetry, you instrument code using API methods, and the SDK determines where that telemetry data goes and how it's processed.

Is it possible to move between SDKs while retaining the same API instrumentation?

Yes, that's the core design principle. Your instrumentation code uses the API, so you can switch between the standard OpenTelemetry SDK, vendor SDKs, or custom implementations without changing application code.

Can I use the OpenTelemetry API without the SDK?

Yes. Without an SDK, API calls become no-ops—your code runs normally but produces no telemetry data. This is useful for testing or environments where you don't want telemetry overhead.

What are the preferred observability backends to work with?

OpenTelemetry works with any backend that supports OTLP or standard formats. Last9 offers managed observability with built-in OpenTelemetry support, while open-source options include Jaeger for tracing and Prometheus for metrics. The choice depends on your infrastructure and budget.

Why is there no Python auto-instrument module for OpenTelemetry?

There is—it's called opentelemetry-auto-instrumentation. Install it with pip install opentelemetry-distro[otlp] and run your app with opentelemetry-instrument python myapp.py. It automatically instruments popular libraries like requests, flask, and Django. Each instrumentation library handles different dependencies and callback patterns.

Why OpenTelemetry compared to Prometheus+syslog+request tagging?

OpenTelemetry correlates metrics, logs, and traces using the same context propagation and span context. With separate tools, you'd need custom correlation logic to connect HTTP requests across services. OpenTelemetry also provides standardized semantic conventions, making data more consistent across services, teams, and programming languages.

How do the OpenTelemetry API and SDK work together for observability?

The API captures telemetry in your code—spans, metrics, and logs with business context and timestamp information. The SDK takes that data, applies sampling and processing rules, and then exports it through the configured pipeline to your observability backend. This separation keeps instrumentation code clean while making telemetry infrastructure configurable through environment variables or YAML configuration.

Authors
Anjali Udasi

Anjali Udasi

Helping to make the tech a little less intimidating. I

Contents

Do More with Less

Unlock unified observability and faster triaging for your team.