By Fikra DevRel • 4 min read

Rust OpenAI SDK Alternative: Integrating with Fikra API

Rust OpenAI SDK Alternative: Integrating with Fikra API

Overview

Fikra API provides an OpenAI-compatible chat-completions interface. While there is no official "Rust OpenAI SDK" that natively supports Fikra out of the box, you can use standard Rust HTTP clients (such as reqwest) to interact with the Fikra API using the same request/response structure as OpenAI's chat-completions endpoint.

This guide demonstrates how to configure a Rust application to call Fikra API, including streaming support and error handling.

Prerequisites

  • Rust toolchain installed (rustc and cargo).
  • A Fikra API key (fk_live_...) obtained from the Fikra dashboard.
  • Basic familiarity with Rust async programming (using tokio and reqwest).

Environment Variable Setup

Store your Fikra API key in an environment variable to avoid hardcoding secrets.

export FIKRA_API_KEY="fk_live_your_key_here"

Rust Integration Example

Below is a complete example using reqwest and tokio to send a non-streaming request to the Fikra API.

1. Add Dependencies

In your Cargo.toml, add:

[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = { version = "0.11", features = ["json", "stream"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

2. Implementation

use reqwest::Client;
use serde::{Deserialize, Serialize};
use std::env;

#[derive(Serialize)]
struct ChatRequest {
    model: String,
    messages: Vec<Message>,
    stream: bool,
}

#[derive(Serialize, Deserialize)]
struct Message {
    role: String,
    content: String,
}

#[derive(Deserialize)]
struct ChatResponse {
    choices: Vec<Choice>,
}

#[derive(Deserialize)]
struct Choice {
    message: Message,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let api_key = env::var("FIKRA_API_KEY")
        .expect("FIKRA_API_KEY environment variable not set");

    let client = Client::new();

    let request_body = ChatRequest {
        model: "fikra-flash".to_string(),
        messages: vec![
            Message {
                role: "user".to_string(),
                content: "Hello, Fikra!".to_string(),
            },
        ],
        stream: false,
    };

    let response = client
        .post("https://api.fikraapi.co.ke/v1/chat/completions")
        .bearer_auth(api_key)
        .json(&request_body)
        .send()
        .await?;

    let status = response.status();
    if !status.is_success() {
        let error_text = response.text().await?;
        eprintln!("Error: {} - {}", status, error_text);
        return Ok(());
    }

    let chat_response: ChatResponse = response.json().await?;
    println!("Response: {}", chat_response.choices[0].message.content);

    Ok(())
}

Streaming Guidance

To enable streaming, set stream: true in the request body. The response will use Server-Sent Events (SSE). In Rust, you can read the response as a stream of bytes and parse the SSE lines.

// In the request body, set stream: true
let request_body = ChatRequest {
    model: "fikra-flash".to_string(),
    messages: vec![
        Message {
            role: "user".to_string(),
            content: "Tell me a short story.".to_string(),
        },
    ],
    stream: true,
};

let response = client
    .post("https://api.fikraapi.co.ke/v1/chat/completions")
    .bearer_auth(api_key)
    .json(&request_body)
    .send()
    .await?;

let mut stream = response.bytes_stream();

use futures::StreamExt;
while let Some(chunk) = stream.next().await {
    let chunk = chunk?;
    // Parse SSE lines: lines starting with "data: " contain JSON chunks
    for line in chunk.split(|&b| b == b'\n') {
        if line.starts_with(b"data: ") {
            let data = &line[6..];
            if data == b"[DONE]" {
                break;
            }
            // Deserialize the JSON chunk and extract the content delta
            // Note: The structure for streaming chunks is similar to OpenAI's
            // { "choices": [{ "delta": { "content": "..." } }] }
            println!("{}", std::str::from_utf8(data).unwrap_or(""));
        }
    }
}

Common Errors

  • 400 Bad Request: Malformed JSON, missing required fields, or invalid model alias. Check the model registry for available aliases.
  • 401 Unauthorized: Invalid or missing API key. Ensure Authorization: Bearer fk_live_... is correctly set.
  • 402 Payment Required: Insufficient token credits in your Fikra wallet. Top up via the Fikra dashboard.
  • 429 Too Many Requests: Rate limit exceeded. Unverified accounts are limited to 30 RPM; trusted accounts to 100 RPM. Implement exponential backoff.
  • 503 Service Unavailable: Temporary server issue. Retry with backoff.

Production Checklist

  1. API Key Management: Store FIKRA_API_KEY in a secure environment variable or secrets manager. Never hardcode keys.
  2. Rate Limiting: Implement client-side rate limiting and exponential backoff for 429 responses.
  3. Timeouts: Set appropriate connection and read timeouts for reqwest.
  4. Error Handling: Log and handle non-2xx responses gracefully. Distinguish between retryable (429, 503) and non-retryable (400, 401, 402) errors.
  5. Streaming: For streaming, ensure your client can handle partial responses and SSE parsing.
  6. Model Alias: Use only publicly listed Fikra aliases (e.g., fikra-flash, fikra-pro-20b). Check the model registry for availability.
  7. Monitoring: Monitor API response times and error rates in production.
  8. Billing: Regularly check token credit balance to avoid 402 errors during peak usage.

JM
// The Founder

James Miano

CTO & ML Engineer at Lacesse Ventures. James specializes in low-overhead LLM quantization processes, custom ternary weights architectures, and localized server optimization.

Stop paying for overpriced round-trip latency

Why route queries over Western servers when you can use low-overhead hardware located in Nairobi? Save 87% on your monthly inference spend. No minimum credit limits. M-Pesa ready.

usagestatusdashboard