openaichatgptapi

July 19, 2026 · View on GitHub

CI crates.io docs.rs license

Typed async Rust client for the OpenAI API — Responses API, Chat Completions, Embeddings, Images (gpt-image), Audio (text-to-speech / transcription / translation), Moderations, and Models. Server-sent-event streaming, Structured Outputs (JSON Schema), function calling, and multimodal input included.

  • Current API surface (2026): built around the Responses API (/v1/responses), OpenAI's primary generation API. Retired endpoints (edits, legacy completions, DALL·E variations) are intentionally absent.
  • Typed end to end: builder-style requests, typed responses and errors — no serde_json::Value juggling for normal use.
  • Machine-readable summary: see llms.txt if you are an AI assistant (or feeding one).
APIEndpointSupport
Responses (primary)/v1/responsescreate / stream / retrieve / delete / cancel
Chat Completions/v1/chat/completionscreate / stream
Embeddings/v1/embeddingscreate
Images/v1/images/generations, /v1/images/editsgenerate / edit
Audio/v1/audio/speech, /v1/audio/transcriptions, /v1/audio/translationsspeech / transcribe / translate
Moderations/v1/moderationscreate
Models/v1/modelslist / retrieve / delete

Installation

cargo add openai_chatgpt_api
cargo add tokio --features full
cargo add futures-util   # only needed for streaming

Quick start

use openai_chatgpt_api::responses::ResponseRequest;
use openai_chatgpt_api::OpenAiClient;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // reads OPENAI_API_KEY (and optionally OPENAI_BASE_URL / OPENAI_ORG_ID / OPENAI_PROJECT_ID)
    let client = OpenAiClient::from_env()?;

    let request = ResponseRequest::new("gpt-5.6-terra", "Explain Rust ownership in three sentences.")
        .instructions("You are a concise assistant.")
        .max_output_tokens(500);

    let response = client.responses().create(&request).await?;
    println!("{}", response.output_text());
    Ok(())
}

Every example below is self-contained inside that main body. Runnable versions live in examples/ — try cargo run --example responses.

Which API should I use?

Use the Responses API (client.responses()) for new projects — it is OpenAI's recommended primary API, with built-in tools and server-side conversation state. Chat Completions (client.chat()) remains fully supported for existing message-array codebases.

How to stream output token by token

use futures_util::StreamExt;
use openai_chatgpt_api::responses::{ResponseRequest, ResponseStreamEvent};

let request = ResponseRequest::new("gpt-5.6-luna", "Write a haiku about Rust.");

let mut stream = client.responses().stream(&request).await?;
while let Some(event) = stream.next().await {
    match event? {
        ResponseStreamEvent::OutputTextDelta { delta, .. } => print!("{delta}"),
        ResponseStreamEvent::Completed { .. } => println!(),
        _ => {}
    }
}

client.chat().stream(&request) works the same way and yields ChatCompletionChunks (use chunk.delta_content()).

How to call functions (tools)

use openai_chatgpt_api::responses::{InputItem, ResponseRequest, Tool};
use serde_json::json;

let tools = vec![Tool::function(
    "get_weather",
    "Get the current weather for a city",
    json!({
        "type": "object",
        "properties": { "city": { "type": "string" } },
        "required": ["city"],
        "additionalProperties": false
    }),
)];

// 1. The model decides to call your function
let request = ResponseRequest::new("gpt-5.6-terra", "What's the weather in Tokyo?")
    .tools(tools.clone());
let response = client.responses().create(&request).await?;

// 2. Execute the calls and send the results back
let mut items = Vec::new();
for call in response.function_calls() {
    let result = json!({ "temperature_c": 22 }); // your real implementation here
    items.push(InputItem::function_call_output(&call.call_id, result.to_string()));
}
let follow_up = ResponseRequest::new("gpt-5.6-terra", items)
    .previous_response_id(&response.id)
    .tools(tools);

// 3. The model answers using the results
let final_response = client.responses().create(&follow_up).await?;
println!("{}", final_response.output_text());

Built-in OpenAI tools work too: Tool::WebSearch, Tool::FileSearch { .. }, Tool::CodeInterpreter { .. }, Tool::ImageGeneration.

How to get structured JSON output (Structured Outputs)

use openai_chatgpt_api::responses::ResponseRequest;
use serde_json::json;

let request = ResponseRequest::new("gpt-5.6-terra", "Extract: 'Alice, 30, Tokyo'")
    .json_schema("person", json!({
        "type": "object",
        "properties": {
            "name": { "type": "string" },
            "age":  { "type": "integer" },
            "city": { "type": "string" }
        },
        "required": ["name", "age", "city"],
        "additionalProperties": false
    }));

let response = client.responses().create(&request).await?;
let person: serde_json::Value = serde_json::from_str(&response.output_text())?;

The same .json_schema(name, schema) shorthand exists on ChatCompletionRequest.

How to send images to the model (vision / multimodal input)

use openai_chatgpt_api::responses::{InputContentPart, InputItem, ResponseRequest};

let request = ResponseRequest::new("gpt-5.6-terra", vec![InputItem::user(vec![
    InputContentPart::text("What is in this image?"),
    InputContentPart::image_url("https://example.com/photo.png"), // or a data: URI
])]);
let response = client.responses().create(&request).await?;

How to continue a conversation

let first = client.responses()
    .create(&ResponseRequest::new("gpt-5.6-terra", "My name is Alice."))
    .await?;

let second = client.responses()
    .create(&ResponseRequest::new("gpt-5.6-terra", "What's my name?")
        .previous_response_id(&first.id))
    .await?; // "Alice" — history is kept server-side

How to use Chat Completions

use openai_chatgpt_api::chat::{ChatCompletionRequest, ChatMessage};

let request = ChatCompletionRequest::new(
    "gpt-5.6-terra",
    vec![
        ChatMessage::system("You are a helpful assistant."),
        ChatMessage::user("Hello!"),
    ],
)
.max_completion_tokens(500); // note: max_tokens is deprecated upstream

let completion = client.chat().create(&request).await?;
println!("{}", completion.content().unwrap_or_default());

How to create embeddings

use openai_chatgpt_api::embeddings::EmbeddingsRequest;

let request = EmbeddingsRequest::new("text-embedding-3-small", "The food was delicious.")
    .dimensions(256); // optional truncation
let response = client.embeddings().create(&request).await?;
let vector: &Vec<f32> = &response.data[0].embedding;

How to generate and edit images

use openai_chatgpt_api::images::{ImageEditRequest, ImageGenerationRequest};
use openai_chatgpt_api::FilePart;

// Generation — gpt-image models return base64, not URLs
let request = ImageGenerationRequest::new("gpt-image-2", "A watercolor Japanese house")
    .size("1536x1024")
    .quality("high");
let images = client.images().generate(&request).await?;
for b64 in images.b64_images() { /* base64-decode and save (see examples/images.rs) */ }

// Editing
let request = ImageEditRequest::new(
    "gpt-image-2",
    FilePart::from_path("input.png")?,
    "Add a cat sitting on the porch",
);
let edited = client.images().edit(request).await?;

How to synthesize speech and transcribe audio

use openai_chatgpt_api::audio::{SpeechRequest, TranscriptionRequest, TranslationRequest};
use openai_chatgpt_api::FilePart;

// Text to speech (TTS)
let request = SpeechRequest::new("gpt-4o-mini-tts", "こんにちは!", "marin");
let mp3_bytes = client.audio().speech(&request).await?;
std::fs::write("voice.mp3", mp3_bytes)?;

// Speech to text (transcription)
let request = TranscriptionRequest::new("gpt-4o-transcribe", FilePart::from_path("voice.mp3")?)
    .language("ja");
let transcript = client.audio().transcribe(request).await?;
println!("{}", transcript.text);

// Translate any language to English
let request = TranslationRequest::new("whisper-1", FilePart::from_path("voice.mp3")?);
let translated = client.audio().translate(request).await?;

How to moderate content

use openai_chatgpt_api::moderations::ModerationRequest;

let request = ModerationRequest::new("Some user input").model("omni-moderation-latest");
let moderation = client.moderations().create(&request).await?;
if moderation.results[0].flagged { /* reject */ }

How to handle errors

use openai_chatgpt_api::OpenAiError;

match client.responses().create(&request).await {
    Ok(response) => println!("{}", response.output_text()),
    Err(OpenAiError::Api { status, message, code, .. }) => {
        // e.g. status=429 (rate limit), 401 (bad key), 400 (bad request)
        eprintln!("API error {status}: {message} (code: {code:?})");
    }
    Err(other) => eprintln!("{other}"), // Http / Parse / Config / Io
}

Client configuration

let client = OpenAiClient::builder("sk-...")
    .organization("org-...")                          // OpenAI-Organization header
    .project("proj_...")                              // OpenAI-Project header
    .base_url("https://my-proxy.example.com/v1")      // proxies / gateways / mocks
    .http_client(reqwest::Client::builder().build()?) // timeouts, proxies, ...
    .build();

Model quick reference (July 2026)

Model ids are plain strings — new models work without a crate update. Check the OpenAI model list for the latest.

TaskCurrent models
Text / reasoninggpt-5.6-sol (flagship), gpt-5.6-terra (balanced), gpt-5.6-luna (budget)
Image generationgpt-image-2, gpt-image-1-mini
Embeddingstext-embedding-3-large, text-embedding-3-small
Speech synthesisgpt-4o-mini-tts, tts-1-hd
Transcriptiongpt-4o-transcribe, gpt-4o-mini-transcribe, whisper-1
Moderationomni-moderation-latest

Testing

cargo test                       # offline: unit + doc tests
OPENAI_API_KEY=sk-... cargo test # additionally runs live API tests

Migrating from v0.1

v0.2 is a full rewrite for the current OpenAI API. The v0.1 types (ChatGpt, ChatGptRequestChatCompletions, ChatGptChatFormat, to_value(), ...) no longer exist — do not use them in new code.

v0.1v0.2
ChatGpt::new(key)OpenAiClient::new(key)
gpt.chat_completions(&req)client.chat().create(&req) or client.responses().create(&req)
ChatGptChatFormat::new_user(text)ChatMessage::user(text) / InputItem::user(text)
res.to_value()typed fields: response.output_text(), completion.content()
gpt.edits(..), gpt.completions_create(..), gpt.images_variations(..)removed — endpoints retired by OpenAI

License

MIT — see LICENSE. Contributions welcome via GitHub.