Sign inContact usStart free
Base URL and SDK MigrationJuly 24, 2026Flatkey Team

Chat Completions with cURL Across Multiple AI Models

Use one OpenAI-compatible cURL request to test GPT, Claude, Gemini, and DeepSeek model families, compare outputs, and add safe failure handling.

Chat Completions with cURL Across Multiple AI Models

You can learn more about an AI gateway from one terminal command than from a long feature list. If the gateway is genuinely OpenAI-compatible, the same curl request should work across supported model families while the base URL, authorization header, message format, and response parsing stay stable.

This tutorial shows the practical pattern with Flatkey: start with one chat-completions request, move the model name into a variable, and test several current model families without rewriting the integration. It is designed for developers who want to validate an API from a terminal before they add an SDK or commit application code.

Model-selection note: Model catalogs change. The model IDs below reflect Flatkey's public documentation checked on July 24, 2026. Confirm the current model row and availability before using an ID in production.

The shortest working chat-completions cURL request

Create a Flatkey API key, export it in your shell, and send a request to the OpenAI-compatible chat-completions endpoint:

export FLATKEY_API_KEY="your-flatkey-api-key"

curl https://router.flatkey.ai/v1/chat/completions \
  -H "Authorization: Bearer $FLATKEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [
      {
        "role": "user",
        "content": "Write a one-sentence product description for a waterproof daypack."
      }
    ]
  }'

Four parts matter:

Request part What stays stable
Base URL https://router.flatkey.ai/v1
Endpoint /chat/completions
Authentication Authorization: Bearer $FLATKEY_API_KEY
Message shape An array of role-and-content objects

For compatible chat models, the main field you switch is model.

Use the same cURL shape across model families

Put the model ID in a shell variable so the request body does not need to change:

export FLATKEY_API_KEY="your-flatkey-api-key"
export MODEL="gpt-4o-mini"

curl -sS https://router.flatkey.ai/v1/chat/completions \
  -H "Authorization: Bearer $FLATKEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"model\": \"$MODEL\",
    \"messages\": [
      {
        \"role\": \"system\",
        \"content\": \"Return concise ecommerce copy.\"
      },
      {
        \"role\": \"user\",
        \"content\": \"Write a product title for a lightweight waterproof daypack.\"
      }
    ],
    \"temperature\": 0.2
  }" | jq -r '.choices[0].message.content'

Now rerun the command with another documented model ID:

export MODEL="claude-sonnet-4-6"
export MODEL="gemini-2.5-flash"
export MODEL="deepseek-v3.1"

The request still uses the same endpoint, headers, messages, and jq parser. That stable call shape is the operational advantage: you can compare supported model families without maintaining a separate terminal script for every provider.

Model-selection note: A shared request shape does not mean every model behaves identically. Supported parameters, context limits, tool behavior, safety behavior, latency, and output style can differ. Treat compatibility as a simpler integration surface, not as proof that models are interchangeable.

Run a small multi-model test loop

For a quick terminal comparison, define a short list and send the same prompt to each model:

#!/usr/bin/env bash
set -euo pipefail

: "${FLATKEY_API_KEY:?Set FLATKEY_API_KEY first}"

MODELS=(
  "gpt-4o-mini"
  "claude-sonnet-4-6"
  "gemini-2.5-flash"
  "deepseek-v3.1"
)

PROMPT="Write three benefit-led bullet points for a waterproof commuter backpack."

for MODEL in "${MODELS[@]}"; do
  echo
  echo "=== $MODEL ==="

  jq -n \
    --arg model "$MODEL" \
    --arg prompt "$PROMPT" \
    '{
      model: $model,
      messages: [
        {role: "system", content: "You write concise ecommerce copy."},
        {role: "user", content: $prompt}
      ],
      temperature: 0.2
    }' |
  curl -sS https://router.flatkey.ai/v1/chat/completions \
    -H "Authorization: Bearer $FLATKEY_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @- |
  jq -r '.choices[0].message.content // .error.message'
done

Using jq -n to construct JSON is safer than manually escaping a long shell string. It also makes the script easier to extend with variables, additional messages, or optional parameters.

Save the script as compare-models.sh, make it executable, and run it:

chmod +x compare-models.sh
./compare-models.sh

What to compare in the output

A multi-model test is useful only when the prompt and scoring method are consistent. For an ecommerce copy task, compare:

Dimension Terminal-friendly check
Instruction following Did the output return exactly three bullets?
Format stability Can the response be parsed without special cases?
Brand fit Is the tone specific, credible, and free of unsupported claims?
Latency How long did the request take?
Token usage What did the response report in its usage object?
Error behavior Does a failed request return a useful error message?

Add cURL timing fields when latency matters:

curl -sS -o response.json \
  -w 'status=%{http_code} total=%{time_total}s\n' \
  https://router.flatkey.ai/v1/chat/completions \
  -H "Authorization: Bearer $FLATKEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gemini-2.5-flash",
    "messages": [
      {"role": "user", "content": "Write a five-word product tagline."}
    ]
  }'

jq . response.json

This separates transport measurements from model output. The terminal prints the HTTP status and total request time, while the JSON response remains available for inspection.

Model-selection note: Do not choose a production model from one response. Run a representative prompt set, repeat requests, and score the outputs against the requirements that matter to your application.

Keep the request comparable

Small prompt or parameter changes can make a model test misleading. Use these controls:

  1. Keep the messages identical. Do not improve the prompt for one model but not the others.
  2. Use the same temperature. Lower values usually make comparison runs easier to review.
  3. Capture raw JSON. Store the complete response, not only the rendered text.
  4. Record the model ID. A display name is not precise enough for reproducible tests.
  5. Separate errors from bad answers. A transport or availability error is not an output-quality score.
  6. Check current availability. A documented model can still have changing operational status.

Add basic failure handling

Use --fail-with-body so cURL exits on HTTP errors while preserving the response body:

HTTP_BODY=$(mktemp)

if ! curl --fail-with-body -sS \
  -o "$HTTP_BODY" \
  https://router.flatkey.ai/v1/chat/completions \
  -H "Authorization: Bearer $FLATKEY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-4o-mini",
    "messages": [
      {"role": "user", "content": "Return the word ready."}
    ]
  }'; then
  jq -r '.error.message // "Request failed"' "$HTTP_BODY" >&2
  rm -f "$HTTP_BODY"
  exit 1
fi

jq -r '.choices[0].message.content' "$HTTP_BODY"
rm -f "$HTTP_BODY"

In application code, also add explicit timeouts, bounded retries for retryable failures, and logging that does not expose secret keys or sensitive prompt content.

A practical model-selection policy

The simplest policy is to select by workload rather than provider name:

Workload First test What to verify before rollout
High-volume simple copy A fast, cost-efficient model Format compliance and acceptable error rate
Nuanced brand writing A stronger general model Tone, factual restraint, and revision rate
Long-context synthesis A model with suitable context support Retrieval quality and truncation behavior
Latency-sensitive UI A low-latency model Tail latency, not only one fast request
Fallback route A model from another family Parameter compatibility and output contract

Start with the smallest model that reliably clears your quality threshold. Escalate to a stronger model when the task needs it. If you add fallback routing, test the fallback with the same response contract rather than assuming it can replace the primary model without application changes.

You can review current model access and pricing on Flatkey's pricing page before selecting the IDs for a production test.

When to move from cURL to an SDK

cURL is ideal for confirming four things quickly:

  • the API key works
  • the base URL is correct
  • the selected model accepts the request
  • the response shape matches your parser

Move to an SDK when you need streaming helpers, structured retry logic, typed responses, reusable clients, or application-level observability. Keep the successful cURL request in your runbook: it remains the fastest way to separate gateway access problems from SDK configuration problems.

Final implementation checklist

  • Export the API key instead of placing it directly in scripts.
  • Use https://router.flatkey.ai/v1 as the base URL.
  • Send compatible chat requests to /chat/completions.
  • Move the model ID into configuration.
  • Build JSON with jq when shell escaping becomes complex.
  • Capture HTTP status, latency, response content, and usage data.
  • Compare models with identical prompts and parameters.
  • Verify current catalog availability before production rollout.
  • Add timeouts, bounded retries, and secret-safe logging in application code.

One stable cURL request gives you a clean starting point. Once it works, changing the model field turns that request into a practical test harness for multiple AI model families—without changing the authentication, base URL, or response parser each time.

Frequently asked questions

Can I use the same chat-completions cURL request for every AI model?

Use it for models that Flatkey exposes through the compatible chat-completions route. Other modalities or protocol-specific features can require different endpoints or request fields.

What is the minimum field set for a chat-completions request?

For a basic request, provide a supported model and a messages array. You also need the bearer authorization header and JSON content type.

Why put the model name in an environment variable?

It keeps the request shape stable, reduces editing mistakes, and makes scripts easier to run across staging, evaluation, and production configurations.

Should I use cURL in production?

cURL is excellent for verification, scripts, and runbooks. Most production applications benefit from an SDK or HTTP client with explicit timeout, retry, telemetry, and type-handling support.

How do I choose among GPT, Claude, Gemini, and DeepSeek models?

Choose with a representative evaluation set. Compare instruction following, output quality, latency, token usage, error behavior, and the specific features your workload requires. Confirm current availability before rollout.