Docs
Get an API key
Get started›SDKs and compatibility

SDKs and compatibility

Use the OpenAI, Anthropic or OpenRouter SDK you already have; change the base URL and the key.

View .md

There is no SDK of our own to install. Each route speaks a shape an existing SDK already reads, so you point that SDK at the API and keep your code.

SDK Base URL Key
OpenAI (Python, JavaScript and the rest) https://api.vultrinference.com/v1 api_key
Anthropic (Python, JavaScript), Claude Code https://api.vultrinference.com (the SDK adds /v1) api_key, sent as x-api-key
OpenRouter (@openrouter/sdk, openrouter for Python) https://api.vultrinference.com/v1 api_key
OpenAI's SDK
import os

from openai import OpenAI

# The only change from OpenAI's own API: the base URL and the key
client = OpenAI(base_url="https://api.vultrinference.com/v1", api_key=os.environ["VULTR_INFERENCE_API_KEY"])

chat = client.chat.completions.create(
    model="deepseek-v4.1-flash",
    messages=[{"role": "user", "content": "Name three prime numbers."}],
)
print(chat.choices[0].message.content)

embedding = client.embeddings.create(model="qwen3-embedding-4b", input="Vultr Serverless Inference")
print(len(embedding.data[0].embedding), "dimensions")
Anthropic's SDK
import os

from anthropic import Anthropic

# No /v1 here: Anthropic's SDK adds /v1/messages itself
client = Anthropic(base_url="https://api.vultrinference.com", api_key=os.environ["VULTR_INFERENCE_API_KEY"])

message = client.messages.create(
    model="deepseek-v4.1-flash",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Name three prime numbers."}],
)
print(message.content[-1].text)

What each route speaks

Route Shape SDK calls
POST /v1/chat/completions OpenRouter's chat completion, which is OpenAI's with more fields OpenAI chat.completions.create, OpenRouter chat.send
POST /v1/responses OpenAI's Responses API OpenAI responses.create, OpenRouter responses.send
POST /v1/messages Anthropic's Messages API, errors included Anthropic messages.create, Claude Code
GET /v1/models Our model list; OpenRouter's or Anthropic's shape when the client asks for it OpenAI and OpenRouter models.list
POST /v1/images/generations, POST /v1/images/edits OpenAI's images API (a link by default) OpenAI images.generate, images.edit
POST /v1/images OpenRouter's images API (base64 by default) OpenRouter images.generate
POST /v1/videos OpenRouter's video API, with OpenAI's field names OpenAI videos.create_and_poll, OpenRouter videoGeneration
POST /v1/audio/speech OpenAI's speech API, with OpenRouter's fields OpenAI audio.speech.create, OpenRouter tts.createSpeech
POST /v1/audio/transcriptions OpenAI's (a file upload) or OpenRouter's (JSON) OpenAI audio.transcriptions.create, OpenRouter stt.createTranscription
POST /v1/embeddings OpenAI's embeddings, with OpenRouter's input_type OpenAI embeddings.create, OpenRouter embeddings.generate
POST /v1/rerank OpenRouter's rerank, compatible with Cohere's v2 OpenRouter rerank.rerank
POST /v1/systemone TypeSafe's System One API TypeSafe's SDKs
/v1/files OpenAI's Files API, for documents OpenAI files.create, files.retrieve, files.list, files.delete
POST /v1/music, /v1/vector_store Our own Plain HTTP

A field the SDK does not know goes in its extra-body option (extra_body in OpenAI's Python SDK), as Reasoning shows.

Where the API differs

Each route keeps its SDK's shape. These are the places it behaves differently from the original:

Route What differs
Chat completions One answer shape whichever engine serves the model (below). No system prompt is added, and a final assistant message is continued rather than answered.
Responses Nothing is stored, so send the whole conversation in input each time. Function, custom and namespace tools work; hosted tools (web search, file search, code interpreter) do not.
Messages model and max_tokens are required. There is no prompt caching and no token counting route. The models are open models, not Claude (Claude Code).
Files Documents only, read once into text. A file expires after 30 days and cannot be downloaded again.
Videos OpenAI's SDKs see queued for pending, and a cancelled or expired job as failed. Webhooks always carry the default shape.
Every route OpenRouter's routing fields (provider, models, route and the like) are accepted and ignored: there is nothing to route between.

One chat shape

Whichever engine serves the model, a chat completion comes back the same way. A real one, with created, system_fingerprint and logprobs left out:

A chat completion
{
  "id": "chatcmpl-00e333f8-d237-4c25-910f-8171becb0de9",
  "object": "chat.completion",
  "model": "deepseek-v4.1-flash",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "No, 221 is not prime because it equals 13 × 17.",
        "reasoning": "We need answer one sentence. Need determine 221 prime? 221 = 13*17? 13*17=221 yes composite. Answer one sentence."
      },
      "finish_reason": "stop",
      "native_finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 38,
    "completion_tokens": 51,
    "total_tokens": 89,
    "prompt_tokens_details": {
      "cached_tokens": 0
    },
    "completion_tokens_details": {
      "reasoning_tokens": 33
    }
  }
}
  • The thinking is always reasoning, never reasoning_content.
  • finish_reason is one of stop, length, tool_calls, content_filter or error. The engine's own word for it is in native_finish_reason.
  • usage always has both detail objects, so code can read them without checking.
  • A stream ends with a usage chunk whether or not you ask for one (Streaming).