> ## Documentation Index
> Fetch the complete documentation index at: https://arize-ax.mintlify.dev/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# OpenAI

> Trace OpenAI Python, Node.js, and Go SDK calls (including Azure OpenAI) with OpenInference and send spans to Arize AX for LLM observability.

[OpenAI](https://openai.com/) provides the GPT family of large language models through the [OpenAI Python SDK](https://github.com/openai/openai-python), [OpenAI Node.js SDK](https://github.com/openai/openai-node), and the [official OpenAI Go SDK](https://github.com/openai/openai-go). Arize AX captures every OpenAI SDK call — chat completions, tool calls, and token usage — via the OpenInference instrumentors for [Python](https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai), [JavaScript / TypeScript](https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-openai), and [Go](https://github.com/Arize-ai/openinference/tree/main/go/openinference-instrumentation-openai-go). The Python, TypeScript, and Go instrumentors all cover Azure OpenAI.

<CardGroup>
  <Card horizontal icon="https://storage.googleapis.com/arize-phoenix-assets/assets/images/phoenix-docs-images/gc.ico" href="http://colab.research.google.com/github/Arize-ai/tutorials/blob/main/python/llm/tracing/openai/openai-tracing.ipynb" title="OpenAI Python Tracing Tutorial (Google Colab)" />

  <Card horizontal icon="github" href="https://github.com/Arize-ai/tutorials/tree/main/python/llm/tracing/openai" title="OpenAI Python Tracing Tutorials on GitHub" />
</CardGroup>

## Prerequisites

* Python 3.9+, Node.js 18+, or Go 1.25+
* An Arize AX account ([sign up](https://arize.com/sign-up/))
* An `OPENAI_API_KEY` from the [OpenAI Platform](https://platform.openai.com/api-keys), or Azure OpenAI credentials

## Launch Arize AX

1. Sign in to your [Arize AX account](https://app.arize.com/).
2. From **Space Settings**, copy your **Space ID** and **API Key**. You will set them as `ARIZE_SPACE_ID` and `ARIZE_API_KEY` below.

## Install

<CodeGroup>
  ```bash Python theme={null}
  pip install arize-otel openinference-instrumentation-openai openai
  ```

  ```bash TypeScript theme={null}
  npm install openai \
    @arizeai/openinference-instrumentation-openai \
    @arizeai/openinference-semantic-conventions \
    @opentelemetry/api \
    @opentelemetry/exporter-trace-otlp-proto \
    @opentelemetry/instrumentation \
    @opentelemetry/resources \
    @opentelemetry/sdk-trace-base \
    @opentelemetry/sdk-trace-node \
    @opentelemetry/semantic-conventions
  ```

  ```bash Go theme={null}
  go get \
    github.com/Arize-ai/arize-otel-go \
    github.com/Arize-ai/openinference/go/openinference-instrumentation-openai-go \
    github.com/openai/openai-go
  ```
</CodeGroup>

## Configure credentials

<Tabs>
  <Tab title="OpenAI">
    ```bash theme={null}
    export ARIZE_SPACE_ID="<your-space-id>"
    export ARIZE_API_KEY="<your-api-key>"
    export ARIZE_PROJECT_NAME="openai-tracing-example"
    export OPENAI_API_KEY="<your-openai-api-key>"
    ```
  </Tab>

  <Tab title="Azure OpenAI">
    ```bash theme={null}
    export ARIZE_SPACE_ID="<your-space-id>"
    export ARIZE_API_KEY="<your-api-key>"
    export ARIZE_PROJECT_NAME="openai-tracing-example"
    export AZURE_OPENAI_API_KEY="<your-azure-key>"
    export AZURE_OPENAI_ENDPOINT="<your-azure-endpoint>"
    export OPENAI_API_VERSION="<api-version>"
    ```

    In the example below, swap the standard OpenAI client for the Azure client — `openai.AzureOpenAI()` in Python, `new AzureOpenAI(...)` in TypeScript, or `openai.NewClient(azure.WithEndpoint(...), azure.WithAPIKey(...), option.WithMiddleware(...))` using the [`openai-go/azure`](https://pkg.go.dev/github.com/openai/openai-go/azure) helpers in Go. The same instrumentor covers all three. The Go middleware recognizes Azure host suffixes (`*.openai.azure.com`, `*.services.ai.azure.com`, `*.cognitiveservices.azure.com`) and sets `llm.provider=azure` on those spans so backend queries can distinguish them from direct OpenAI traffic. The Python and TypeScript Azure clients read the API version from `OPENAI_API_VERSION` automatically; the Go Azure helper takes it as the second argument to `azure.WithEndpoint(endpoint, apiVersion)`.
  </Tab>
</Tabs>

## Setup tracing

<CodeGroup>
  ```python Python theme={null}
  # instrumentation.py
  import os

  from arize.otel import register
  from openinference.instrumentation.openai import OpenAIInstrumentor

  tracer_provider = register(
      space_id=os.environ["ARIZE_SPACE_ID"],
      api_key=os.environ["ARIZE_API_KEY"],
      project_name=os.environ["ARIZE_PROJECT_NAME"],
  )

  OpenAIInstrumentor().instrument(tracer_provider=tracer_provider)
  print("Arize AX tracing initialized for OpenAI.")
  ```

  ```typescript TypeScript theme={null}
  // instrumentation.ts
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
  import { resourceFromAttributes } from "@opentelemetry/resources";
  import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
  import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
  import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
  import {
    SEMRESATTRS_PROJECT_NAME,
  } from "@arizeai/openinference-semantic-conventions";
  import { registerInstrumentations } from "@opentelemetry/instrumentation";
  import {
    OpenAIInstrumentation,
  } from "@arizeai/openinference-instrumentation-openai";
  import OpenAI from "openai";

  const projectName =
    process.env.ARIZE_PROJECT_NAME ?? "openai-tracing-example";

  export const provider = new NodeTracerProvider({
    resource: resourceFromAttributes({
      [ATTR_SERVICE_NAME]: projectName,
      [SEMRESATTRS_PROJECT_NAME]: projectName,
    }),
    spanProcessors: [
      new SimpleSpanProcessor(
        new OTLPTraceExporter({
          url: "https://otlp.arize.com/v1/traces",
          headers: {
            "arize-space-id": process.env.ARIZE_SPACE_ID ?? "",
            "arize-api-key": process.env.ARIZE_API_KEY ?? "",
          },
        }),
      ),
    ],
  });

  provider.register();

  const instrumentation = new OpenAIInstrumentation();
  instrumentation.manuallyInstrument(OpenAI);

  registerInstrumentations({ instrumentations: [instrumentation] });

  console.log("Arize AX tracing initialized for OpenAI.");
  ```

  ```go Go theme={null}
  // main.go
  //
  // Go integrations live in a single file — the tracer setup, the
  // instrumented middleware, and the chat call all sit inside main().
  // The `Run OpenAI` step below is just `go run main.go`.
  package main

  import (
      "context"
      "fmt"
      "log"
      "os"
      "time"

      arizeotel "github.com/Arize-ai/arize-otel-go"
      openaiotel "github.com/Arize-ai/openinference/go/openinference-instrumentation-openai-go"
      "github.com/openai/openai-go"
      "github.com/openai/openai-go/option"
      "github.com/openai/openai-go/shared"
      "go.opentelemetry.io/otel"
  )

  func main() {
      ctx := context.Background()

      projectName := os.Getenv("ARIZE_PROJECT_NAME")
      if projectName == "" {
          projectName = "openai-tracing-example"
      }

      // Register reads ARIZE_SPACE_ID / ARIZE_API_KEY from the environment.
      tp, err := arizeotel.Register(ctx, arizeotel.Options{
          ProjectName: projectName,
      })
      if err != nil {
          log.Printf("register tracer: %v", err)
          return
      }
      defer func() {
          shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
          defer cancel()
          _ = tp.Shutdown(shutdownCtx)
      }()

      fmt.Println("Arize AX tracing initialized for OpenAI.")

      // The middleware wraps every /v1/chat/completions request in an
      // LLM-kind span. Reads OPENAI_API_KEY from the environment.
      client := openai.NewClient(
          option.WithAPIKey(os.Getenv("OPENAI_API_KEY")),
          option.WithMiddleware(openaiotel.Middleware(otel.Tracer(projectName))),
      )

      resp, err := client.Chat.Completions.New(ctx, openai.ChatCompletionNewParams{
          Model: shared.ChatModel("gpt-5"),
          Messages: []openai.ChatCompletionMessageParamUnion{
              openai.UserMessage("Write a haiku about observability."),
          },
      })
      if err != nil {
          log.Printf("openai: %v", err)
          return
      }

      fmt.Println(resp.Choices[0].Message.Content)
  }
  ```
</CodeGroup>

<Note>
  **Go SDK** Only `/v1/chat/completions` is instrumented today; embeddings, responses, and image endpoints fall through unwrapped. Streaming responses pass through unchanged, but `output.value` and token counts are not populated for streaming spans yet.
</Note>

## Run OpenAI

<CodeGroup>
  ```python Python theme={null}
  # example.py

  # Importing instrumentation first ensures tracing is set up
  # before `openai` is imported.
  from instrumentation import tracer_provider

  import openai

  # The client reads OPENAI_API_KEY from the environment.
  client = openai.OpenAI()

  response = client.chat.completions.create(
      model="gpt-5",
      messages=[
          {
              "role": "user",
              "content": "Write a haiku about observability.",
          },
      ],
  )

  print(response.choices[0].message.content)
  ```

  ```typescript TypeScript theme={null}
  // example.ts

  // Importing instrumentation first ensures tracing is set up
  // before the OpenAI client is used.
  import { provider } from "./instrumentation";
  import OpenAI from "openai";

  // The client reads OPENAI_API_KEY from the environment.
  const client = new OpenAI();

  const response = await client.chat.completions.create({
    model: "gpt-5",
    messages: [
      { role: "user", content: "Write a haiku about observability." },
    ],
  });

  console.log(response.choices[0].message.content);

  // Flush any pending spans before the process exits.
  await provider.forceFlush();
  ```

  ```bash Go theme={null}
  # The full example lives in main.go above.
  go run main.go
  ```
</CodeGroup>

### Expected output

```text wrap theme={null}
Arize AX tracing initialized for OpenAI.
Logs whisper softly,
metrics rise like morning mist —
truth in every span.
```

## Verify in Arize AX

1. Open your Arize AX space and select project **`openai-tracing-example`**.
2. You should see a new trace within \~30 seconds containing an LLM span — `ChatCompletion` for the Python SDK, `OpenAI Chat Completions` for the Node.js SDK, or `openai.chat.completions.create` for the Go SDK — with the prompt, response, and token usage attached.
3. If no traces appear, see [Troubleshooting](#troubleshooting).

<Frame>
  ![OpenAI tracing in Arize AX](https://storage.googleapis.com/arize-phoenix-assets/assets/images/arize-docs-images/openai-tracing.gif)
</Frame>

## Troubleshooting

* **No traces in Arize AX.** Confirm `ARIZE_SPACE_ID` and `ARIZE_API_KEY` are set in the same shell that runs the example. Enable OpenTelemetry debug logs with `export OTEL_LOG_LEVEL=debug` and re-run.
* **OpenAI spans missing but other spans present (Python).** `OpenAIInstrumentor().instrument(...)` must run before any `import openai` in the application. Make sure `instrumentation.py` is the first import in your entry point.
* **OpenAI spans missing but other spans present (TypeScript).** `instrumentation.manuallyInstrument(OpenAI)` must run before any code creates an `OpenAI` client. Make sure `import { provider } from "./instrumentation"` (or a side-effect-only `import "./instrumentation"`) is the first import in your entry point.
* **`401` from OpenAI.** Verify `OPENAI_API_KEY` is set and has access to the model in the example. Swap `gpt-5` for a model your key can call.
* **Azure OpenAI returns `Resource not found`.** Confirm `AZURE_OPENAI_ENDPOINT` points to your deployment, `OPENAI_API_VERSION` matches a version your deployment supports, and the example uses the Azure client constructor (`openai.AzureOpenAI()` / `new AzureOpenAI()`) rather than the standard OpenAI client.
* **TypeScript process exits before spans flush.** With `SimpleSpanProcessor`, spans are sent immediately, but make sure to `await provider.forceFlush()` (or call `provider.shutdown()`) before the process exits to avoid losing trailing spans.
* **Go process exits before spans flush.** `arize-otel-go` uses a batched span processor by default. The `defer tp.Shutdown(...)` block in `main.go` is what flushes the batch — without it, short-lived programs lose their last spans. Pass `SimpleProcessor: true` to `arizeotel.Register` if you want synchronous export instead.
* **Go example wraps the wrong endpoint.** The Go middleware only instruments `/v1/chat/completions`. Embeddings, responses, completions, and image endpoints pass through to the next middleware unchanged — no span is emitted for them in v0.

## Resources

<CardGroup>
  <Card icon="github" href="https://github.com/openai/openai-python" title="OpenAI Python SDK" horizontal />

  <Card icon="github" href="https://github.com/openai/openai-node" title="OpenAI Node.js SDK" horizontal />

  <Card icon="terminal" href="https://github.com/Arize-ai/openinference/tree/main/python/instrumentation/openinference-instrumentation-openai" title="OpenInference OpenAI Instrumentor (Python)" horizontal />

  <Card icon="terminal" href="https://github.com/Arize-ai/openinference/tree/main/js/packages/openinference-instrumentation-openai" title="OpenInference OpenAI Instrumentor (JS/TS)" horizontal />

  <Card icon="github" href="https://github.com/openai/openai-go" title="OpenAI Go SDK" horizontal />

  <Card icon="terminal" href="https://github.com/Arize-ai/openinference/tree/main/go/openinference-instrumentation-openai-go" title="OpenInference OpenAI Instrumentor (Go)" horizontal />
</CardGroup>
