Providers
OpenAI, Anthropic, Gemini, Groq, and Together all go through the same endpoint, the same virtual key, and the same client code. The only thing that changes between them is the model field.
How this works
FitGuard exposes one endpoint, shaped like OpenAI's chat completions API: POST /v1/chat/completions.
It reads the model field, works out which upstream provider that model belongs to, translates
the request into that provider's native format if needed, and translates the response back. Your client only ever speaks
the OpenAI shape. It never sees Anthropic's or Gemini's actual request format, and it never sees your real provider keys.
Every example on this page uses the same two values: your FitGuard URL as base_url, and a
virtual key from fitguard init as the API key. Switching providers means changing one string,
the model name. Nothing else in your code changes.
OpenAI
Model names pass through unchanged: gpt-4o, gpt-4o-mini, o1, and so on. Vision and tool calling work exactly as documented in OpenAI's own API reference, since no translation is needed.
curl https://your-fitguard-host/v1/chat/completions \
-H "Authorization: Bearer sk-guard-..." \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4o",
"messages": [{"role": "user", "content": "hello"}]
}'
from openai import OpenAI
client = OpenAI(base_url="https://your-fitguard-host/v1",
api_key="sk-guard-...")
resp = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://your-fitguard-host/v1",
apiKey: "sk-guard-...",
});
const resp = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "hello" }],
});
console.log(resp.choices[0].message.content);
using System.Net.Http.Json;
// Initialize the HTTP client with the AI guard host URL and API key.
var client = new HttpClient
{
BaseAddress = new Uri("https://your-fitguard-host/v1/"),
};
// Set the Authorization header with the AI guard API key.
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue
("Bearer", "sk-guard-...");
// Define the request payload.
var payload = new {
model = "gpt-4o",
messages = new[]
{
new { role = "user", content = "hello" },
new { role = "system", content = "You are an assistant." }
}
};
// Send the POST request to the AI guard host.
var response = await client.PostAsJsonAsync
("chat/completions", payload);
// Read the response body as a string.
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import java.util.concurrent.CompletableFuture;
// Create the HTTP client.
HttpClient client = HttpClient.newHttpClient();
// Create the request body string.
String json = """
{
"model": "gpt-4o",
"messages": [
{ "role": "user", "content": "hello" },
{ "role": "system", "content": "You are an assistant." }
]
}
""";
// Create the request body publisher.
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://your-fitguard-host/v1/chat/completions"))
.header("Authorization", "Bearer sk-guard-...")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
// Send the request and get the response.
HttpResponse<String> response = client.send
(request, HttpResponse.BodyHandlers.ofString());
// Read the response body as a string.
System.out.println(response.body());
body, _ := json.Marshal(map[string]any{
"model": "gpt-4o",
"messages": []map[string]string{
{"role": "user", "content": "hello"},
{"role": "system", "content": "You are an assistant."},
},
})
req, _ := http.NewRequest("POST", "https://your-fitguard-host/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer sk-guard-...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
let client = reqwest::blocking::Client::new();
let resp = client.post("https://your-fitguard-host/v1/chat/completions")
.bearer_auth("sk-guard-...")
.json(&serde_json::json!({
"model": "gpt-4o",
"messages": [
{"role": "user", "content": "hello"},
{"role": "system", "content": "You are an assistant."}
]
}))
.send()?;
Anthropic
Use Claude model names: claude-sonnet-5, claude-haiku-4-5, and so on.
FitGuard translates the request into Anthropic's Messages API and translates the response back into the OpenAI shape your
client already expects, so the client code below is identical to the OpenAI examples except for the model name.
anthropic.Anthropic(...), .messages.create(...)), see
Already using Anthropic's own SDK? in the getting started guide.
It's not a drop-in swap the way switching OpenAI models is: Anthropic's client speaks a different request shape than
FitGuard's OpenAI-compatible endpoint expects, so that section walks through the actual change needed.
curl https://your-fitguard-host/v1/chat/completions \
-H "Authorization: Bearer sk-guard-..." \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-5",
"messages": [{"role": "user", "content": "hello"}]
}'
from openai import OpenAI
client = OpenAI(base_url="https://your-fitguard-host/v1",
api_key="sk-guard-...")
resp = client.chat.completions.create(
model="claude-sonnet-5",
messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://your-fitguard-host/v1",
apiKey: "sk-guard-...",
});
const resp = await client.chat.completions.create({
model: "claude-sonnet-5",
messages: [{ role: "user", content: "hello" }],
});
console.log(resp.choices[0].message.content);
using System.Net.Http.Json;
var client = new HttpClient
{
BaseAddress = new Uri("https://your-fitguard-host/v1/"),
};
// Set the authorization header.
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer",
"sk-guard-...");
var payload = new {
model = "claude-sonnet-5",
messages = new[] { new { role = "user", content = "hello" } }
};
var response = await client.PostAsJsonAsync("chat/completions",
payload);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
HttpClient client = HttpClient.newHttpClient();
String json = """
{
"model":"claude-sonnet-5",
"messages":[
{"role":"user","content":"hello"},
{"role":"system","content":"You are an assistant."},
]
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://your-fitguard-host/v1/chat/completions"))
.header("Authorization", "Bearer sk-guard-...")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
body, _ := json.Marshal(map[string]any{
"model": "claude-sonnet-5",
"messages": []map[string]string{
{"role": "user", "content": "hello"},
{"role": "system", "content": "You are an assistant."},
},
})
req, _ := http.NewRequest("POST", "https://your-fitguard-host/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer sk-guard-...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
let client = reqwest::blocking::Client::new();
let resp = client.post("https://your-fitguard-host/v1/chat/completions")
.bearer_auth("sk-guard-...")
.json(&serde_json::json!({
"model": "claude-sonnet-5",
"messages": [
{"role": "user", "content": "hello"},
{"role": "system", "content": "You are an assistant."}
]
}))
.send()?;
Gemini
Use Gemini model names: gemini-2.5-pro, gemini-2.5-flash, gemini-1.5-pro, and so on. FitGuard translates the request into Gemini's generateContent format and translates the response back, the same way it does for Anthropic.
curl https://your-fitguard-host/v1/chat/completions \
-H "Authorization: Bearer sk-guard-..." \
-H "Content-Type: application/json" \
-d '{
"model": "gemini-2.5-flash",
"messages": [{"role": "user", "content": "hello"}]
}'
from openai import OpenAI
client = OpenAI(base_url="https://your-fitguard-host/v1",
api_key="sk-guard-...")
resp = client.chat.completions.create(
model="gemini-2.5-flash",
messages=[{"role": "user", "content": "hello"}],
)
print(resp.choices[0].message.content)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://your-fitguard-host/v1",
apiKey: "sk-guard-...",
});
const resp = await client.chat.completions.create({
model: "gemini-2.5-flash",
messages: [{ role: "user", content: "hello" }],
});
console.log(resp.choices[0].message.content);
using System.Net.Http.Json;
var client = new HttpClient {
BaseAddress = new Uri("https://your-fitguard-host/v1/"),
};
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer",
"sk-guard-...");
var payload = new {
model = "gemini-2.5-flash",
messages = new[] { new { role = "user", content = "hello" } }
};
var response = await client.PostAsJsonAsync("chat/completions",
payload);
var body = await response.Content.ReadAsStringAsync();
Console.WriteLine(body);
HttpClient client = HttpClient.newHttpClient();
String json = """
{
"model": "gemini-2.5-flash",
"messages": [
{
"role": "user",
"content": "hello"
}
]
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://your-fitguard-host/v1/chat/completions"))
.header("Authorization", "Bearer sk-guard-...")
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(json))
.build();
HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());
System.out.println(response.body());
body, _ := json.Marshal(map[string]any{
"model": "gemini-2.5-flash",
"messages": []map[string]string{{"role": "user", "content": "hello"}},
})
req, _ := http.NewRequest("POST", "https://your-fitguard-host/v1/chat/completions", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer sk-guard-...")
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
let client = reqwest::blocking::Client::new();
let resp = client.post("https://your-fitguard-host/v1/chat/completions")
.bearer_auth("sk-guard-...")
.json(&serde_json::json!({
"model": "gemini-2.5-flash",
"messages": [{"role": "user", "content": "hello"}]
}))
.send()?;
image_url.url pointing
at https://...), Gemini rejects it. Gemini only accepts inline base64 image data or a file
already uploaded through its own File API, and FitGuard does not fetch remote URLs on your behalf to convert them,
since that would let a request make FitGuard's server fetch arbitrary attacker-supplied URLs. Send images as base64
data URIs (data:image/png;base64,...) when using Gemini. OpenAI and Anthropic both accept
remote image URLs directly, so this restriction is specific to Gemini.
Groq
Use Groq's hosted model names: llama-3.3-70b-versatile, llama-3.1-8b-instant, mixtral-8x7b-32768, and so on. Groq's API is already OpenAI-compatible, so requests pass through with no translation.
curl https://your-fitguard-host/v1/chat/completions \
-H "Authorization: Bearer sk-guard-..." \
-H "Content-Type: application/json" \
-d '{
"model": "llama-3.3-70b-versatile",
"messages": [{"role": "user", "content": "hello"}]
}'
Together AI
Use Together's model names, which are namespaced by publisher, e.g. meta-llama/Llama-3-70b-chat-hf. Together's API is also OpenAI-compatible. A model name containing a / that isn't a recognized provider prefix (openai/, anthropic/, etc.) routes to Together by default, since that's the convention its own model names use.
curl https://your-fitguard-host/v1/chat/completions \
-H "Authorization: Bearer sk-guard-..." \
-H "Content-Type: application/json" \
-d '{
"model": "meta-llama/Llama-3-70b-chat-hf",
"messages": [{"role": "user", "content": "hello"}]
}'
Mixing providers in one app
Nothing about the client changes between providers, so a single app can call all five without any conditional logic. The only difference is which string you put in model:
await client.chat.completions.create({ model: "gpt-4o-mini", ... }); // OpenAI
await client.chat.completions.create({ model: "claude-haiku-4-5", ... }); // Anthropic
await client.chat.completions.create({ model: "gemini-2.5-flash", ... }); // Gemini
await client.chat.completions.create({ model: "llama-3.3-70b-versatile", ... }); // Groq
await client.chat.completions.create({ model: "meta-llama/Llama-3-70b-chat-hf", ... }); // Together
Each of these still goes through the same cache, the same budget check, and the same cost log. Adding a provider you don't already have configured is one command: fitguard add-provider. See Fallback in the configuration reference if you want FitGuard to try a different provider automatically when the primary one fails.