Expert guide

How to Call the OpenAI API from Excel VBA

Send each row to an AI model and write the answer back, with working, commented code

Short answer: on Windows Excel, VBA can call the OpenAI API by sending a JSON POST request with MSXML2.ServerXMLHTTP to the chat completions endpoint, passing your key in an Authorization: Bearer header read from an environment variable, then extracting the reply text from the JSON response. Loop over rows, add a short delay, and log errors per row. For team-wide use, route calls through a controlled Excel API integration rather than personal keys.

ChatGPT in a browser is fine for one question. When you have 3,000 product descriptions to rewrite, 800 support emails to categorise, or a column of free-text notes to summarise, you want the model inside Excel, processing one row at a time and writing the answer in the next column. VBA can do that with about 80 lines of code.

What You Need

  • An OpenAI API account with billing enabled

    API usage is billed separately from a ChatGPT Plus or Team subscription.

  • An API key stored outside the workbook

    Set a Windows user environment variable named OPENAI_API_KEY (Start → 'Edit environment variables for your account'), then restart Excel.

  • Excel for Windows with macros enabled

    Save the file as .xlsm. The HTTP objects used here are Windows-only.

  • A model name from OpenAI's current model list

    Models change regularly. Use a small, inexpensive model for row-by-row work and put its name in one constant so you can change it later.

The Complete VBA Code

Paste this into a standard module (Alt+F11 → Insert → Module). It reads prompts from column A of the active sheet, sends each one with a fixed instruction, and writes the reply to column B and any error to column C.

Option Explicit

Private Const API_URL As String = "https://api.openai.com/v1/chat/completions"
Private Const MODEL As String = "gpt-4.1-mini"   ' change to a current model
Private Const SYSTEM_PROMPT As String = _
    "You are a concise assistant. Answer in one short sentence."

Public Function AskOpenAI(ByVal userText As String) As String
    Dim http As Object, body As String, key As String

    key = Environ("OPENAI_API_KEY")
    If Len(key) = 0 Then Err.Raise vbObjectError + 1, , "OPENAI_API_KEY not set"

    body = "{""model"":""" & MODEL & """," & _
           """temperature"":0.2," & _
           """messages"":[" & _
           "{""role"":""system"",""content"":""" & JsonEscape(SYSTEM_PROMPT) & """}," & _
           "{""role"":""user"",""content"":""" & JsonEscape(userText) & """}]}"

    Set http = CreateObject("MSXML2.ServerXMLHTTP.6.0")
    http.setTimeouts 5000, 5000, 30000, 60000
    http.Open "POST", API_URL, False
    http.setRequestHeader "Content-Type", "application/json"
    http.setRequestHeader "Authorization", "Bearer " & key
    http.send body

    If http.Status <> 200 Then
        Err.Raise vbObjectError + http.Status, , _
            "HTTP " & http.Status & ": " & Left$(http.responseText, 300)
    End If

    AskOpenAI = ExtractContent(http.responseText)
End Function

Public Sub ProcessRows()
    Dim ws As Worksheet, lastRow As Long, r As Long
    Set ws = ActiveSheet
    lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row

    If MsgBox("Send " & lastRow - 1 & " rows to the API?", vbYesNo) <> vbYes Then Exit Sub

    For r = 2 To lastRow
        If Len(ws.Cells(r, "A").Value) > 0 And Len(ws.Cells(r, "B").Value) = 0 Then
            On Error Resume Next
            ws.Cells(r, "B").Value = AskOpenAI(CStr(ws.Cells(r, "A").Value))
            If Err.Number <> 0 Then ws.Cells(r, "C").Value = Err.Description
            Err.Clear
            On Error GoTo 0
            Application.StatusBar = "Row " & r & " of " & lastRow
            DoEvents
            Application.Wait Now + TimeSerial(0, 0, 1)   ' gentle rate limit
        End If
    Next r
    Application.StatusBar = False
End Sub

Private Function JsonEscape(ByVal s As String) As String
    s = Replace(s, "\", "\\")
    s = Replace(s, """", "\""")
    s = Replace(s, vbCrLf, "\n")
    s = Replace(s, vbCr, "\n")
    s = Replace(s, vbLf, "\n")
    s = Replace(s, vbTab, "\t")
    JsonEscape = s
End Function

' Minimal extraction of choices[0].message.content without a JSON library.
Private Function ExtractContent(ByVal json As String) As String
    Dim p As Long, q As Long, ch As String, out As String
    p = InStr(1, json, """content"":")
    If p = 0 Then ExtractContent = "": Exit Function
    p = InStr(p + 10, json, """") + 1
    q = p
    Do While q <= Len(json)
        ch = Mid$(json, q, 1)
        If ch = "\" Then
            Select Case Mid$(json, q + 1, 1)
                Case "n": out = out & vbLf
                Case "t": out = out & vbTab
                Case """": out = out & """"
                Case "\": out = out & "\"
                Case Else: out = out & Mid$(json, q + 1, 1)
            End Select
            q = q + 2
        ElseIf ch = """" Then
            Exit Do
        Else
            out = out & ch
            q = q + 1
        End If
    Loop
    ExtractContent = out
End Function

Run ProcessRows from the Macros dialog (Alt+F8). Rows that already have an answer in column B are skipped, so if the run stops halfway you can simply run it again.

How the Code Works

Building the request

The request body is JSON with three parts: the model name, a low temperature for consistent answers, and a messages array containing a system instruction and the row's text. JsonEscape is essential; a single quotation mark or line break in a cell would otherwise produce invalid JSON and an HTTP 400 error.

Reading the response

The reply arrives as JSON. ExtractContent pulls out the first message content and unescapes it. It is deliberately simple. For anything more complex, such as structured JSON output with several fields, use a proper JSON parser module. Our guide to parsing JSON in VBA applies to Excel as well as Access.

Errors per row, not per run

Each row is wrapped in its own error handler so one bad row (an empty response, a timeout) does not stop the batch. The error text goes into column C where you can filter for it afterwards.

Practical Uses in Business Workbooks

Use caseColumn A containsSystem prompt example
Categorise expensesBank transaction descriptionReturn one category from: Fuel, Meals, Software, Travel, Office, Other.
Summarise notesFree-text call or job notesSummarise in under 20 words. Mention any promised follow-up date.
Standardise namesMessy company namesReturn the company's legal-style name without suffix variations.
Draft repliesCustomer email textDraft a polite two-sentence reply. Do not promise refunds.
Extract fieldsUnstructured address textReturn Street | City | State | ZIP separated by pipes.

Controlling Cost and Quality

  • Test on 50 rows first

    Check the answers and the usage dashboard, then multiply before running 10,000 rows.

  • Constrain the answer

    A fixed list of allowed categories or a strict format produces answers you can filter and pivot.

  • Keep the temperature low

    0 to 0.3 for classification and extraction; higher only for creative writing.

  • Do not send what you do not need

    Strip names, account numbers and other personal data from the text before sending it.

  • Cache answers

    Skipping filled rows, as the code does, means you never pay twice for the same row.

When to Move Beyond a Macro

A personal macro with a personal key is fine for experimenting. It is not how a business should run AI on customer or financial data. Move to a managed setup when:

  • More than one person needs to run it, so keys would be shared.

  • The data includes personal, health or financial information.

  • The process should run on new data automatically, not when someone clicks a button.

  • You need logs of what was sent, what came back, and what it cost.

We build that layer as part of AI workflow automation projects: a controlled service holds the key, applies data rules, logs every call, and returns results to Excel, Access or SQL Server. If you also need Access databases to call AI, see connecting MS Access to the OpenAI API.

FAQ

OpenAI API and Excel VBA FAQ

Setup, security, cost and common errors

Yes. On Windows, VBA can send an HTTPS POST request to the OpenAI API using the MSXML2.ServerXMLHTTP or WinHttp.WinHttpRequest objects, include your API key in the Authorization header, and read the JSON response. The same approach works for other AI providers that offer a REST API.

Not in the workbook. Anyone who opens the VBA editor can read it. Store it in a Windows user environment variable and read it with Environ("OPENAI_API_KEY"), or for teams, route requests through a small internal service that holds the key and logs usage.

Not as written. Excel for Mac does not include the Windows MSXML and WinHttp objects. On Mac you would need AppleScript with curl, Office Scripts in Excel for the web, or an add-in. For mixed Windows and Mac teams an Office Scripts or Power Automate approach is usually simpler.

You pay per token processed. Short classification or rewriting tasks on a few thousand rows typically cost cents to a few dollars with smaller models, but costs scale with prompt length and row count. Test on 50 rows, check the usage dashboard, then extrapolate before running the full sheet.

401 means the API key is missing, wrong, or revoked. 429 means you hit a rate limit or ran out of credit. Add a short pause between rows, retry 429 responses after a delay, and confirm billing is active on the API account.