Complétions de chat
Générez des réponses de chat sur plus de 100 modèles à partir d'une seule API. Compatible avec les complétions de chat OpenAI, les messages Anthropic et les réponses Anthropic.
Airforce parle à la fois les formats wire OpenAI Chat Completions et Anthropic Messages sur le même ensemble de modèles. Choisissez le SDK que vous utilisez déjà et changez seulement la base URL — les modèles non-Claude sont transmis de façon transparente derrière l'une ou l'autre surface.
Cette page couvre l'authentification, les formes de requête et de réponse pour les deux surfaces, le streaming, le tool calling, la vision, le reasoning et le prompt caching. Nouveau ici ? Commencez par l'exemple de base ci-dessous, faites fonctionner un appel, puis ajoutez le streaming, les tools ou le caching une fois que c'est bon.
Authentification
Chaque requête nécessite un jeton Bearer (votre clé API Airforce). L'en-tête Anthropic x-api-key l'en-tête est également accepté sur /v1/messages pour la compatibilité du SDK.
Authorization: Bearer sk-air-YOUR_API_KEY
# alt for /v1/messages:
x-api-key: sk-air-YOUR_API_KEYPOST /v1/chat/completions
Chat Completions compatible OpenAI. Fonctionne avec le SDK officiel openai SDK en remplaçant base_url à https://api.airforce/v1.
https://api.airforce/v1/chat/completionsCorps de la demande
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Required | ID du modèle. Utilisez GET /v1/models pour découvrir les ID disponibles. |
| messages | array | Required | Historique des conversations. Chaque entrée a { role: "system" | "user" | "assistant" | "tool", content }. Le contenu est une chaîne ou un tableau de blocs de contenu (vision, voir ci-dessous). |
| max_tokens | integer | Optional | Nombre maximum de jetons à générer. Plafonné au max_output_tokens du modèle. |
| temperature | float | Optional | Température d'échantillonnage, 0–2. Lower est plus déterministe. La valeur par défaut dépend du fournisseur en amont. |
| top_p | float | Optional | Échantillonnage de noyau. Utilisez soit temperature, soit top_p, pas les deux. |
| stream | boolean | Optional | Lorsque cela est vrai, la réponse est un flux d'événements envoyés par le serveur. Voir « Diffusion en continu » ci-dessous. |
| models | array | Optional | Fallback models (max 3), e.g. ["deepseek-v3.2", "gpt-4o-mini"]. If every channel of the primary model fails, each candidate is tried in order. You are billed for — and response.model reports — the model that actually answered. Unknown or plan-gated candidates are skipped. With the OpenAI SDK pass it via extra_body. |
| transforms | array | Optional | Prompt transforms. Supported: ["middle-out"] — when the conversation overflows the model's context window, whole messages are dropped from the middle (system prompts, the first message and the most recent turns are kept), so long roleplay or agent histories keep working instead of erroring. Opt-in; off by default. |
| stream_options | object | Optional | { include_usage: boolean }. L’usage est toujours inclus dans le dernier fragment du stream ; ce champ est accepté pour la compatibilité OpenAI mais ne peut pas le désactiver. |
| stop | string | array | Optional | Jusqu'à 4 séquences d'arrêt. La génération s'arrête dès qu'une est produite. |
| tools | array | Optional | Définitions de fonctions que le modèle peut appeler. Voir « Appel d'outil » ci-dessous. |
| tool_choice | string | object | Optional | "auto" (par défaut), "none" ou { type: "function", function: { name } } pour forcer un appel spécifique. |
| response_format | object | Optional | { type: "json_object" } force le modèle à émettre du JSON valide. Ignoré pour les modèles qui ne le prennent pas en charge. |
| reasoning_effort | string | Optional | Reasoning depth: "low" | "medium" | "high" | "xhigh" | "max". Any model with supports_reasoning: true (Claude, OpenAI o/GPT-5, Gemini, Qwen, DeepSeek, …). See "Reasoning & thinking". |
| thinking | string | object | Optional | Cross-model thinking switch. "on" | "off" | "auto"; Anthropic-style { type: "enabled", budget_tokens: N }; hybrid { type: "enabled" | "disabled" }. See "Reasoning & thinking". |
| thinking_budget | integer | Optional | Plafond de jeton pour la trace de raisonnement du modèle (lorsque le fournisseur en expose une). |
| ignore_defaults | boolean | Optional | Ignorez les paramètres par défaut par modèle enregistrés par l'utilisateur (configurés dans le tableau de bord) pour cette demande. |
| skill | string | Optional | ID of a single marketplace skill to apply to this request. The skill transforms your messages/parameters before the upstream call and overrides any installed-skill defaults. Consumed by Airforce, never forwarded upstream. See the Skills catalog at /docs/api/extend. |
| skills | array | Optional | Array of marketplace skill IDs applied in order, for stacking multiple skills on one request. |
Exemple de base
curl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.1-chat",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is the capital of France?"}
],
"max_tokens": 200,
"temperature": 0.7
}'Forme de réponse
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Optional | ID de complétion stable, par ex. "chatcmpl-abc123". |
| object | string | Optional | "chat.completion" pour les réponses non diffusées, "chat.completion.chunk" pour les réponses diffusées en continu. |
| created | integer | Optional | Horodatage Unix (secondes). |
| model | string | Optional | Echo de l'ID du modèle demandé. |
| choices | array | Optional | Tableau de candidats de complétion : [{ index, message: { role, content, tool_calls? }, finish_reason }]. |
| choices[].finish_reason | string | Optional | "stop" | "length" | "tool_calls" | "content_filter". |
| usage | object | Optional | { prompt_tokens, completion_tokens, total_tokens, completion_tokens_details?, prompt_tokens_details?, cache_creation_input_tokens?, cache_creation? }. completion_tokens_details.reasoning_tokens est défini lorsque le modèle a produit une trace de raisonnement. Les champs de cache apparaissent lorsque l'upstream renvoie des informations de cache de prompt : prompt_tokens_details.cached_tokens rapporte les lectures de cache (standard OpenAI), cache_creation_input_tokens agrège les écritures, et cache_creation.ephemeral_5m_input_tokens / ephemeral_1h_input_tokens donnent la répartition par TTL. |
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1710000000,
"model": "gpt-5.1-chat",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": "The capital of France is Paris."
},
"finish_reason": "stop"
}],
"usage": {
"prompt_tokens": 20,
"completion_tokens": 8,
"total_tokens": 28
}
}Raisonnement et réflexion
Reasoning/thinking is a cross-model feature for every model ID with supports_reasoning: true — Claude, OpenAI o-series/GPT-5, Gemini, Qwen, DeepSeek, and others. You send the same canonical parameters; Airforce maps them to each provider's native shape. This is not a DeepSeek-only API.
Truth source: check supports_reasoning: true sur un modèle en GET /v1/models (or GET /api/models/{id}/allowed-params). Prefer that flag over guessing from the model name.
Modèles avec support de raisonnement
…· liveParamètres canoniques
| Parameter | Type | Required | Description |
|---|---|---|---|
| reasoning_effort | string | Optional | "low" | "medium" | "high" | "xhigh" | "max". Accepted on every model with supports_reasoning: true. Some upstreams only honour a subset (e.g. high/max); others clamp unsupported levels to the nearest served value. |
| thinking | string | object | Optional | Three accepted shapes (we normalise): "on" | "off" | "auto"; Anthropic-style { type: "enabled", budget_tokens: N }; hybrid { type: "enabled" | "disabled" }. Mapped onto Claude extended thinking, OpenAI effort profiles, Gemini thinking_config, Qwen enable_thinking, DeepSeek hybrid, etc. |
| thinking_budget | integer | Optional | Maximum tokens the model may spend reasoning before emitting visible output. Mirrors budget_tokens when the upstream exposes a budget; takes precedence over reasoning_effort when both are sent and a budget is available. |
What differs by family (mapping only)
Parameters are the same everywhere. Only how we map them (and how hard "off" is) differs:
- Claude — Thinking on/off + budget; often also reasoning_effort via the gateway.
- OpenAI (o1/o3, GPT-5) — Mainly reasoning_effort. A full "thinking off" is often not available — you control how strongly the model reasons, not always whether it reasons at all.
- Gemini — thinking_config / budget mapped internally.
- Qwen / Xiaomi / Alibaba — thinking + enable_thinking-style controls.
- DeepSeek (generic) — Hybrid on/off is especially clear: thinking: { type: enabled|disabled } plus optional reasoning_effort.
- Resellers / other — Often generic passthrough of the same canonical fields.
Controlling where the trace appears
An optional reasoning object on the request decides what happens to the thinking trace. It is consumed by Airforce and never forwarded upstream.
| Parameter | Type | Required | Description |
|---|---|---|---|
| reasoning.format | string | Optional | "separate" (default) puts the trace in message.reasoning (and delta.reasoning while streaming). "inline" keeps the legacy inline <think>…</think> form inside content. |
| reasoning.exclude | boolean | Optional | When true, the reasoning trace is dropped entirely from the response. Reasoning tokens are still counted and billed if the model produced them. |
"reasoning": { "format": "separate", "exclude": false }Effort de raisonnement (style OpenAI)
Primary control for o-series and GPT-5: how much the model may reason. Same canonical field as on every other supports_reasoning model — OpenAI is included, but behaviour is not 1:1 with DeepSeek's hard on/off.
curl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "o3-mini",
"messages": [{"role": "user", "content": "Prove the Pythagorean theorem."}],
"reasoning_effort": "high"
}'Pensée étendue (style Anthropic)
Budget-based thinking for Claude (and gateways that accept the Anthropic shape). You can still send reasoning_effort; we map when the channel supports it.
curl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4.6",
"messages": [{"role": "user", "content": "Plan a 7-day Italy trip."}],
"thinking": {"type": "enabled", "budget_tokens": 4000}
}'Hybrid thinking (e.g. DeepSeek V3.2/V4)
Example of a hybrid model family with a clear Thinking / Non-Thinking switch — not a separate protocol. deepseek-v3.2, deepseek-v4-flash and deepseek-v4-pro accept the same canonical fields as every other supports_reasoning model. Toggle thinking and optionally set effort in one request:
curl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "deepseek-v4-pro",
"messages": [{"role": "user", "content": "Solve this step by step: integrate x^2 * e^x."}],
"thinking": {"type": "enabled"},
"reasoning_effort": "high"
}'Turn thinking off (faster, cheaper when you only need the final answer) — this hard off is clearer on hybrid models than on many OpenAI o-series profiles:
"thinking": {"type": "disabled"}
// or simply: "thinking": "off"Native docs for this family often list effort levels such as "high" and "max". We accept the full low…max scale and map unsupported levels to the nearest value that reaches the model. Prefer the hybrid IDs above over retired deepseek-chat / deepseek-reasoner names when you need an explicit on/off switch.
La trace du raisonnement elle-même apparaît dans choices[0].message.reasoning (forme OpenAI) ou comme thinking bloque dans content (Format Anthropic). Les jetons de raisonnement sont facturés et signalés dans usage.completion_tokens_details.reasoning_tokens.
Ce détail completion_tokens_details.reasoning_tokens n'est présent que lorsque le fournisseur en amont le rapporte. Sur une réponse en stream, la trace arrive sur delta.reasoning_content à chaque chunk.
Saisie de vision et d'image
Modèles avec supports_vision: true accepter les images intégrées en tant que blocs de contenu. Soit une URL publique, soit une URL de données base64 fonctionne ; les limites de taille dépendent du modèle en amont.
Modèles avec prise en charge de la vision
…· livecurl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.1-chat",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "What is in this image?"},
{"type": "image_url", "image_url": {"url": "https://example.com/cat.jpg"}}
]
}]
}'Appel d'outil
Modèles avec supports_tools: true peut appeler les fonctions que vous définissez. Le modèle renvoie un tool_calls tableau; vous exécutez l'appel, puis renvoyez le résultat dans un tool message.
Modèles avec prise en charge des appels d'outils
…· liveDemande
curl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.1-chat",
"messages": [{"role": "user", "content": "What is the weather in Paris?"}],
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City name"}
},
"required": ["location"]
}
}
}],
"tool_choice": "auto"
}'Réponse avec appel d'outil
{
"id": "chatcmpl-abc123",
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_1",
"type": "function",
"function": {
"name": "get_weather",
"arguments": "{\"location\":\"Paris\"}"
}
}]
},
"finish_reason": "tool_calls"
}]
}Suivi du résultat de l'outil
{
"model": "gpt-5.1-chat",
"messages": [
{"role": "user", "content": "What is the weather in Paris?"},
{
"role": "assistant",
"content": null,
"tool_calls": [{
"id": "call_1",
"type": "function",
"function": {"name": "get_weather", "arguments": "{\"location\":\"Paris\"}"}
}]
},
{"role": "tool", "tool_call_id": "call_1", "content": "{\"temp_c\": 14, \"sky\": \"cloudy\"}"}
]
}Assistant prefill
End your messages array with an assistant message that already contains some text, and the model continues from it instead of starting a fresh turn. This is a reliable way to force a response to begin a specific way — a leading "{" for JSON, a chosen language, or a fixed prefix. The same trick works on /v1/messages. Providers that reject native prefill are handled automatically: the gateway retries once with a compatible rewrite, so you do not have to special-case them.
{
"model": "claude-sonnet-4.6",
"messages": [
{"role": "user", "content": "List three primary colors as a JSON array."},
{"role": "assistant", "content": "["}
]
}Structured outputs
Set response_format to make the model return JSON. Two modes are supported:
{ "type": "json_object" }— the response is a single valid JSON value.{ "type": "json_schema", "json_schema": { "name", "schema", "strict" } }— the model is steered to produce JSON that matches your JSON Schema.
curl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.1-chat",
"messages": [{"role": "user", "content": "Extract the city and country: I live in Paris, France."}],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "location",
"schema": {
"type": "object",
"properties": { "city": {"type": "string"}, "country": {"type": "string"} },
"required": ["city", "country"]
}
}
}
}'Reliability: even when a model wraps its answer in prose or a markdown code fence, Airforce extracts the JSON payload so you always receive parseable content. If no valid JSON can be recovered, the original text is returned unchanged — so the guarantee never makes a response worse. This applies to non-streamed responses; streamed responses are passed through unchanged.
Streaming
Définissez stream: true pour recevoir des achèvements partiels en tant qu'événements envoyés par le serveur. Chaque événement est un morceau JSON ayant la même forme que la réponse non diffusée, sauf message est remplacé par delta. Le flux se termine par data: [DONE].
curl https://api.airforce/v1/chat/completions \
-H "Authorization: Bearer sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.1-chat",
"messages": [{"role": "user", "content": "Write a haiku about Berlin."}],
"stream": true,
"stream_options": {"include_usage": true}
}'Format de fil
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1710000000,"model":"gpt-5.1-chat","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1710000000,"model":"gpt-5.1-chat","choices":[{"index":0,"delta":{"content":"Cold "},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1710000000,"model":"gpt-5.1-chat","choices":[{"index":0,"delta":{"content":"stone "},"finish_reason":null}]}
…
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1710000000,"model":"gpt-5.1-chat","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":17,"total_tokens":29}}
data: [DONE]Reliability & smart routing
Every model ID resolves to a pool of upstream providers behind the scenes. If the first one errors or times out, the request is automatically retried against the next provider for the same model, in order, before any failure is returned — you do not configure or trigger this. The model field in the response always reports the variant that actually answered. This is independent of the optional models / fallbacks array, which adds your own cross-model candidates on top: first the primary model exhausts its own provider chain, then each fallback model exhausts its chain.
POST /v1/messages
API Messages compatible Anthropic. Fonctionne avec le SDK officiel @anthropic-ai/sdk en définissant baseURL à https://api.airforce. Transfère vers OpenAI/Google/etc. de manière transparente pour les modèles non Claude.
https://api.airforce/v1/messagesCorps de la demande
| Parameter | Type | Required | Description |
|---|---|---|---|
| model | string | Required | ID du modèle (format Anthropic ou alias routé). |
| messages | array | Required | Chaque entrée : { role: "user" | "assistant", content: string | array }. |
| max_tokens | integer | Required | Requis par Anthropic. Plafond de jeton pour la réponse. |
| system | string | array | Optional | Invite du système. Transmettez un tableau de { type : "text", text, cache_control ? } blocs pour marquer les segments de préfixe mis en cache. Voir « Mise en cache des invites ». |
| temperature | float | Optional | 0-1. |
| top_p | float | Optional | Échantillonnage de noyau. |
| top_k | integer | Optional | Limitez le pool d’échantillonnage aux K premiers jetons. |
| stop_sequences | array | Optional | Jusqu'à 4 séquences d'arrêt. |
| stream | boolean | Optional | Lorsque c'est vrai, émet un flux d'événements SSE de style Anthropic (voir « Streaming »). |
| fallbacks | array | Optional | Fallback models (max 3) in Anthropic form: [{"model": "gpt-4o-mini"}]. If every channel of the primary model fails, each candidate is tried in order; you are billed for — and the response model field reports — the model that actually answered. A plain models string array is accepted too. |
| tools | array | Optional | Définitions des outils Anthropic : { name, description, input_schema }. La réponse peut contenir des blocs de contenu tool_use. |
| tool_choice | object | Optional | { type: "auto" | "any" | "tool", name? }. |
| thinking | object | Optional | Pensée étendue Anthropic : { type : "enabled", budget_tokens : N }. |
Exemple
curl https://api.airforce/v1/messages \
-H "x-api-key: sk-air-YOUR_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4.6",
"max_tokens": 256,
"system": "You are a helpful assistant.",
"messages": [
{"role": "user", "content": "Hello, Claude!"}
]
}'Forme de réponse
| Parameter | Type | Required | Description |
|---|---|---|---|
| id | string | Optional | ID du message, par ex. "msg_01ABCxyz". |
| type | string | Optional | Toujours "message". |
| role | string | Optional | Toujours "assistant". |
| content | array | Optional | Tableau de blocs de contenu : { type: "text" | "tool_use" | "thinking", … }. |
| model | string | Optional | Echo du modèle demandé. |
| stop_reason | string | Optional | "end_turn" | "max_tokens" | "stop_sequence" | "tool_use". |
| usage | object | Optional | { input_tokens, output_tokens, cache_read_input_tokens?, cache_creation_input_tokens?, cache_creation? }. Les champs de cache apparaissent lorsque le cache de prompt a été utilisé. cache_creation.ephemeral_5m_input_tokens et ephemeral_1h_input_tokens donnent la répartition des écritures par TTL. |
Événements en streaming
Anthropic SSE utilise des événements nommés au lieu de morceaux JSON uniques. Chaque événement a à la fois un event: nom et un data: Charge utile JSON.
event: message_start
data: {"type":"message_start","message":{"id":"msg_01","role":"assistant","content":[],"model":"claude-sonnet-4.6","stop_reason":null,"usage":{"input_tokens":12,"output_tokens":1}}}
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: message_delta
data: {"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":17}}
event: message_stop
data: {"type":"message_stop"}POST /v1/messages/count_tokens
Anthropic-compatible token counting. Send the same system / messages / tools you would pass to /v1/messages and get an input-token estimate back without running the model — nothing is billed.
https://api.airforce/v1/messages/count_tokenscurl https://api.airforce/v1/messages/count_tokens \
-H "x-api-key: sk-air-YOUR_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "Content-Type: application/json" \
-d '{
"model": "claude-sonnet-4.6",
"system": "You are a helpful assistant.",
"messages": [{"role": "user", "content": "Hello, Claude!"}]
}'
# → {"input_tokens": 34}The count is a fast character-based estimate (about 4 characters per token) over system, messages and tools — close enough for context-budget checks, not an exact tokenizer run.
Mise en cache des prompts
Sur /v1/messages avec les modèles Claude, marquer un préfixe comme mis en cache en passant system comme un tableau de blocs où le segment mis en cache transporte cache_control: { type: "ephemeral" }. Les requêtes ultérieures commençant par le même préfixe facturent le tarif de lecture du cache le moins cher. Modèles avec supports_caching: true dans /v1/models prennent cela en charge.
Modèles avec mise en cache des prompts
…· live{
"model": "claude-sonnet-4.6",
"max_tokens": 1024,
"system": [
{"type": "text", "text": "You are a senior staff engineer at Airforce."},
{
"type": "text",
"text": "<repository-snapshot>...</repository-snapshot>",
"cache_control": {"type": "ephemeral"}
}
],
"messages": [
{"role": "user", "content": "Where is rate limiting enforced?"}
]
}Comment les compteurs de cache sont rapportés dans la réponse
Les compteurs de tokens de cache sont transmis dans la forme native de chaque format, de sorte que les SDK (openai, @anthropic-ai/sdk, @google/genai) les lisent sans code personnalisé. Les champs sont omis lorsque la valeur est zéro, gardant les réponses non mises en cache légères.
/v1/chat/completions (forme OpenAI)
"usage": {
"prompt_tokens": 2104,
"completion_tokens": 147,
"total_tokens": 2251,
"prompt_tokens_details": { "cached_tokens": 1980 },
"cache_creation_input_tokens": 124,
"cache_creation": {
"ephemeral_5m_input_tokens": 124,
"ephemeral_1h_input_tokens": 0
}
}/v1/messages (forme Anthropic)
"usage": {
"input_tokens": 2104,
"output_tokens": 147,
"cache_read_input_tokens": 1980,
"cache_creation_input_tokens": 124,
"cache_creation": {
"ephemeral_5m_input_tokens": 124,
"ephemeral_1h_input_tokens": 0
}
}/v1beta/.../generateContent (forme Gemini)
"usageMetadata": {
"promptTokenCount": 2104,
"candidatesTokenCount": 147,
"totalTokenCount": 2251,
"cachedContentTokenCount": 1980
}Où le cache s’applique
Les marqueurs cache_control explicites sont pris en compte sur /v1/messages et /v1/chat/completions pour les modèles Claude — placez-les sur les blocs de contenu system ou message. Beaucoup d’autres fournisseurs (famille OpenAI, DeepSeek, Gemini) mettent en cache automatiquement : vous n’envoyez aucun marqueur et voyez simplement cached_tokens dans la réponse dès qu’un préfixe assez long est réutilisé.
Durée du cache : 5 minutes ou 1 heure
Un préfixe en cache vit 5 minutes par défaut et le minuteur se réinitialise à chaque accès. Pour un préfixe plus durable, ajoutez ttl: "1h" au marqueur. La réponse indique chaque TTL séparément sous cache_creation.
"cache_control": { "type": "ephemeral", "ttl": "1h" }Exemple : d’abord l’écriture, puis la lecture
Envoyez exactement la même requête deux fois (l’exemple de cache ci-dessus). Le premier appel qui voit le préfixe paie une écriture de cache unique ; les appels identiques dans la TTL paient la lecture de cache bien moins chère.
Premier appel — écriture de cache (extrait usage) :
"usage": {
"input_tokens": 2104,
"output_tokens": 12,
"cache_creation_input_tokens": 1980,
"cache_read_input_tokens": 0
}Deuxième appel identique dans la TTL — lecture de cache :
"usage": {
"input_tokens": 2104,
"output_tokens": 12,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 1980
}Limites et coût
- Claude exige un préfixe minimum (environ 1024 tokens ; plus pour certains modèles). Les préfixes plus courts ne sont tout simplement pas mis en cache.
- Jusqu’à 4 points de cache par requête, et le préfixe en cache doit être identique octet pour octet entre les appels — même un changement d’un caractère manque le cache.
- Les écritures de cache coûtent plus que l’entrée normale (5m ≈ 1,25×, 1h ≈ 2×) ; les lectures coûtent bien moins (≈ 0,1×). Voir les prix de cache de chaque modèle sur la page tarifs.
POST /v1/responses
Surface OpenAI Responses-API pour les conversations à état. Même authentification Bearer/x-api-key. Les compteurs de cache apparaissent comme input_tokens_details.cached_tokens (lecture) plus le cache_creation_input_tokens à plat + cache_creation.ephemeral_* (écritures) pour la parité avec /v1/chat/completions.
https://api.airforce/v1/responsesPOST /v1beta/models/{model}:generateContent
Google Gemini-compatible endpoint. Works with the official @google/genai SDK and the Gemini CLI by pointing the base URL at https://api.airforce/v1beta. Any routed model works — requests are translated to and from the native Gemini shape, and the model is taken from the URL path (not the body).
https://api.airforce/v1beta/models/{model}:generateContentAuthentication
Pass your Airforce API key any of the three ways Google clients use:
# 1) query parameter (Google default)
?key=sk-air-YOUR_API_KEY
# 2) header
x-goog-api-key: sk-air-YOUR_API_KEY
# 3) bearer token
Authorization: Bearer sk-air-YOUR_API_KEYRequest body
| Parameter | Type | Required | Description |
|---|---|---|---|
| contents | array | Required | Conversation turns. Each: { role: "user" | "model", parts: [...] }. A part is { text }, { functionCall: { name, args } }, or { functionResponse: { name, response } }. "model" is Gemini's term for the assistant role. |
| systemInstruction | object | Optional | System prompt: { parts: [{ text }] }. |
| generationConfig | object | Optional | { temperature, maxOutputTokens, topP, stopSequences } — mapped to the canonical sampling parameters. |
| tools | array | Optional | Tool definitions: [{ functionDeclarations: [{ name, description, parameters }] }]. functionDeclarations are flattened across entries. |
| toolConfig | object | Optional | Tool-choice control: { functionCallingConfig: { mode: "AUTO" | "ANY" | "NONE" } }. ANY forces a call, NONE disables tools. |
Example
curl "https://api.airforce/v1beta/models/gemini-3.1-pro:generateContent" \
-H "x-goog-api-key: sk-air-YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"contents": [
{"role": "user", "parts": [{"text": "What is the capital of France?"}]}
],
"systemInstruction": {"parts": [{"text": "You are a helpful assistant."}]},
"generationConfig": {"temperature": 0.7, "maxOutputTokens": 256}
}'Response shape
| Parameter | Type | Required | Description |
|---|---|---|---|
| candidates | array | Optional | Generated turns: [{ content: { role: "model", parts }, finishReason, index }]. Only the first candidate is populated. |
| candidates[].finishReason | string | Optional | "STOP" | "MAX_TOKENS" | "SAFETY" | "OTHER". |
| usageMetadata | object | Optional | { promptTokenCount, candidatesTokenCount, totalTokenCount, cachedContentTokenCount? }. cachedContentTokenCount appears when the upstream reported a cache read. |
| modelVersion | string | Optional | Echo of the requested model. |
{
"candidates": [{
"content": {
"role": "model",
"parts": [{"text": "The capital of France is Paris."}]
},
"finishReason": "STOP",
"index": 0
}],
"usageMetadata": {
"promptTokenCount": 16,
"candidatesTokenCount": 8,
"totalTokenCount": 24
},
"modelVersion": "gemini-3.1-pro"
}POST /v1beta/models/{model}:streamGenerateContent
Streaming uses the :streamGenerateContent action and returns Server-Sent Events. Each data: line is a full Gemini-shaped chunk (not a delta object); the final chunk carries usageMetadata.
data: {"candidates":[{"content":{"role":"model","parts":[{"text":"The capital"}]},"index":0}],"modelVersion":"gemini-3.1-pro"}
data: {"candidates":[{"content":{"role":"model","parts":[{"text":" is Paris."}]},"index":0}],"modelVersion":"gemini-3.1-pro"}
data: {"candidates":[{"content":{"role":"model","parts":[]},"finishReason":"STOP","index":0}],"usageMetadata":{"promptTokenCount":16,"candidatesTokenCount":8,"totalTokenCount":24}}List models
The catalog is also exposed in Gemini Model-resource shape so Google clients can enumerate models.
curl https://api.airforce/v1beta/modelsNotes: the base URL is https://api.airforce/v1beta (or /v1), not Google's host. The model name comes from the URL path, not the request body. Only the first candidate is returned, and a subset of Gemini fields is translated — safetySettings and cachedContent are currently ignored. Billing, rate limits and smart routing apply exactly as on /v1/chat/completions.
Erreurs
Airforce renvoie des codes d'état HTTP standard et une enveloppe d'erreur uniforme pour les deux points de terminaison.
| Parameter | Type | Required | Description |
|---|---|---|---|
| 400 | invalid_request_error | Optional | JSON mal formé, champ obligatoire manquant, modèle inconnu. |
| 401 | invalid_request_error / auth_required | Optional | Clé API manquante ou invalide. |
| 402 | insufficient_quota | Optional | Le modèle nécessite un abonnement actif ou un solde Pay-as-you-Go positif. |
| 403 | model_access_denied / insufficient_scope | Optional | Les autorisations de plan ou par clé refusent cette demande. |
| 404 | model_not_found | Optional | Le modèle demandé n’existe pas ou vous n’y avez pas accès. |
| 429 | rate_limit_error | Optional | Taux de demande ou plafond de jetons quotidien dépassé. |
| 503 | api_error / moderation_unavailable | Optional | Toutes les clés en amont du fournisseur demandé ont échoué. |
{
"error": {
"message": "The requested model does not exist or you do not have access to it.",
"type": "model_not_found",
"param": null,
"code": "404"
}
}Le slug descriptif se trouve dans type. code est le statut HTTP sous forme de chaîne (p. ex. "404"), et param est null sauf en cas d'erreurs de validation de plage de paramètres, où il nomme le paramètre fautif.
Découvrez les modèles
Consultez la liste complète des ID de modèle et leurs indicateurs de capacité (vision, outils, raisonnement, mise en cache, longueur du contexte, …) sur /docs/api/models.
curl https://api.airforce/v1/models \
-H "Authorization: Bearer sk-air-YOUR_API_KEY"