Skip to main content

Getting started

MindsHub Inference gives you every major model behind one API key, reachable through the three APIs developers already use:

  • OpenAI Chat Completions: POST /v1/chat/completions
  • OpenAI Responses: POST /v1/responses
  • Anthropic Messages: POST /v1/messages

These are three request formats over the same engine, not three products. Streaming, tool calling, vision, and web search work through all of them, and any model works behind any of the three: Claude Fable from the OpenAI SDK, GPT 5.6 Sol from the Anthropic SDK.

You keep the SDK and the code you already have, and change a base URL.

1. Get a key

  1. Sign up at console.mindshub.ai.
  2. Create an API key.
  3. Copy it when it appears: the full key is shown once, at creation.
export MINDSHUB_API_KEY="mdb_..."

New organizations get a monthly allowance of included tokens on mindshub_air, so your first calls cost nothing. Everything else draws a prepaid wallet. See Billing.

2. The same request, three ways

Here is one prompt, sent through all three APIs. Note the model in each: a Claude model through the OpenAI SDK, a GPT model through the Anthropic SDK. The Chat Completions and Messages tabs work today as shown. The Responses tab shows where that API is heading; it fails today because the endpoint still returns Chat Completions shapes (status).

from openai import OpenAI
import os

client = OpenAI(
base_url="https://api.mindshub.ai/v1",
api_key=os.environ["MINDSHUB_API_KEY"],
)

response = client.chat.completions.create(
model="sonnet", # a Claude model, via the OpenAI SDK
messages=[{"role": "user", "content": "Name three uses for a paperclip."}],
)
print(response.choices[0].message.content)

Two details worth remembering:

  • The OpenAI SDKs take the base URL with /v1 and authenticate with api_key.
  • The Anthropic SDKs take the base URL without /v1 (the client appends the path itself) and authenticate with auth_token, not api_key. The api_key field sends an x-api-key header, which MindsHub rejects.

Not sure which to use? Choosing an API has a one-screen decision table.

3. Switch models by editing one string

Every model is addressed by a short, stable alias. The alias resolves server-side to a concrete model at the provider, so upgrades don't break your code.

for model in ["fable", "gpt", "kimi", "deepseek", "gemini-flash"]:
response = client.chat.completions.create(
model=model,
messages=[{"role": "user", "content": "Explain a hash map in one sentence."}],
)
print(f"{model:14} {response.choices[0].message.content}")

That loop runs five models from five vendors on one key and one bill. A sample of the catalog:

AliasModel
fableClaude Fable 5
opusClaude Opus 5
sonnetClaude Sonnet 5
gptGPT 5.6 Sol
gpt-miniGPT 5.4 Mini
geminiGemini 3.1 Pro Preview
kimiKimi K3
deepseekDeepSeek V4 Pro
qwenQwen3.7 Plus
grokGrok 4.5

The full list, with reasoning-effort support and live availability, is in Models and from GET /v1/models.

4. Stream the output

Set stream=True. Each API streams in its own native format, so your existing streaming code keeps working.

stream = client.chat.completions.create(
model="sonnet",
messages=[{"role": "user", "content": "Count to five."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)

Guard on chunk.choices being non-empty in the Chat Completions loop: some models end the stream with a usage-bearing chunk that carries no choices.

5. Call your tools

Tool calling works on every API and on the models that support it. Declare a function, and when the model asks for it, run it and hand the result back.

tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a city",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
},
}]

messages = [{"role": "user", "content": "What's the weather in Lisbon?"}]

response = client.chat.completions.create(model="sonnet", messages=messages, tools=tools)
call = response.choices[0].message.tool_calls[0]

# Run the real function here; hard-coded for the example.
result = "19°C, light rain"

messages.append(response.choices[0].message)
messages.append({"role": "tool", "tool_call_id": call.id, "content": result})

final = client.chat.completions.create(model="sonnet", messages=messages, tools=tools)
print(final.choices[0].message.content)

Send the same tools array on the follow-up call, and echo tool_call_id back exactly as you received it. Full reference in Chat completions → Tool calling.

6. Send an image

Attach an image as a data: URL or a public URL:

response = client.chat.completions.create(
model="sonnet",
messages=[{
"role": "user",
"content": [
{"type": "text", "text": "What's in this image?"},
{"type": "image_url", "image_url": {"url": "https://example.com/chart.png"}},
],
}],
)

7. Give the model the web

Add the built-in web_search and fetch tools and the platform handles retrieval; there's no search API to sign up for:

response = client.chat.completions.create(
model="sonnet",
messages=[{"role": "user", "content": "What shipped in Python 3.14?"}],
tools=[{"type": "web_search"}, {"type": "fetch"}],
)

Searches carry a small per-search charge on top of tokens; see Billing.

8. See what it cost

Every non-streaming response carries usage; streams end with a usage chunk when you ask via stream_options: {"include_usage": true}:

print(response.usage.prompt_tokens, response.usage.completion_tokens)

And your account-wide totals are one call away:

curl "https://auth.mindshub.ai/v1/usage/summary/?range=period&group_by=model" \
-H "Authorization: Bearer $MINDSHUB_API_KEY"

That returns per-model token counts and cost for the current billing period, covering every SDK and coding agent you've pointed at MindsHub. One place to check instead of four vendor dashboards.

What we adapt for you

Models disagree about parameters: some take no top_k, some take no reasoning_effort, and output ceilings differ. A parameter the target model can't take is dropped, a value above its range (max_tokens over the model's ceiling, a reasoning_effort above its ladder) is clamped down, and the request is served either way, with every change named in the X-MindsHub-Dropped-Params and X-MindsHub-Clamped-Params response headers. A model can still restrict the values of a parameter it supports; that error passes through as a 400 (Kimi K3 takes temperature only at 1). The full contract is in Core concepts.

Use it in your coding agent

The same key runs your terminal and editor agents. Claude Code points at MindsHub with two environment variables and can run on Kimi K3 or any other catalog model, billed to the same balance as your application traffic. Codex support is coming.

Where to next