aip / your accounts, one API

BRING YOUR OWN ACCOUNTS

Your providers.
One endpoint.

Connect your AI accounts, choose easy, medium or hard, and keep building. Streaming, automatic failover, and a clear view of your usage.

Create an account

Your quota stays yours

OpenRouter, Pollinations, Cloudflare, Groq, Mistral, or your own endpoint. Every request uses your connected account.

Choose the right level

easy for speed, medium for everyday work, hard for coding and reasoning. auto selects medium.

Content stays out of storage

We record usage metadata. We never store or log prompts or completions. Provider policies still apply.

Get started

Create an account, connect a provider in your dashboard, and create an API key. Or install the CLI:

curl -fsSL https://ai.safetyvibe.eu/install.sh | sh
aip login
aip connect openrouter
aip chat "Write a Python merge sort"
eval "$(aip env --create-key --shell bash)"

Browser login uses OAuth with PKCE. On a headless machine, run aip login --device. aip env --create-key creates a stable API key for other tools; login tokens refresh automatically inside aip.

Set your environment

export OPENAI_BASE_URL="https://ai.safetyvibe.eu/v1"
export OPENAI_API_KEY="aip_your_key"

Use an explicit model such as openrouter/meta-llama/llama-3.3-70b-instruct:free to select a fixed provider. List currently available models at GET /v1/models.

curl · streaming

curl -N "$OPENAI_BASE_URL/chat/completions" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"model":"medium","messages":[{"role":"user","content":"Hello"}],"stream":true}'

OpenAI Python SDK

import os
from openai import OpenAI

client = OpenAI(base_url=os.environ["OPENAI_BASE_URL"],
                api_key=os.environ["OPENAI_API_KEY"])
for chunk in client.chat.completions.create(
    model="hard", messages=[{"role": "user", "content": "Explain binary search"}],
    stream=True,
):
    if chunk.choices:
        print(chunk.choices[0].delta.content or "", end="", flush=True)

OpenAI JavaScript SDK

import OpenAI from "openai";
const client = new OpenAI({
  baseURL: process.env.OPENAI_BASE_URL,
  apiKey: process.env.OPENAI_API_KEY,
});
const stream = await client.chat.completions.create({
  model: "hard", messages: [{role: "user", content: "Explain binary search"}],
  stream: true,
});
for await (const chunk of stream) {
  process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}

aider

export OPENAI_API_BASE="$OPENAI_BASE_URL"
aider --model openai/hard

aider uses OPENAI_API_BASE for its endpoint. aider configuration.

Continue

In your local Continue configuration, use the OpenAI provider with the same URL and key. Store the key in Continue secrets rather than in a shared repository.

name: aip
version: 1.0.0
schema: v1
models:
  - name: aip hard
    provider: openai
    model: hard
    apiBase: https://ai.safetyvibe.eu/v1
    apiKey: ${{ secrets.OPENAI_API_KEY }}

Continue configuration.

Codex CLI

Export OPENAI_API_KEY using aip env --create-key, then put this in ~/.codex/config.toml:

model = "gpt-5.5"
model_provider = "aip"
model_context_window = 32768

[model_providers.aip]
name = "aip"
base_url = "https://ai.safetyvibe.eu/v1"
env_key = "OPENAI_API_KEY"
wire_api = "responses"
requires_openai_auth = false
supports_websockets = false

The service translates Responses text, function tools and freeform custom tools to your provider's Chat Completions API. Send conversation history on each request; response storage, previous_response_id, background mode, and WebSockets are not supported. Hosted and unsupported tools (including web_search, file_search and multi_agent namespaces) are stripped; no Codex feature overrides are needed. Level names easy, medium, hard and auto are routing aliases without built-in Codex metadata. Use gpt-5.5 as the Codex metadata-compatible alias for hard; it selects your own connected upstreams, not an OpenAI subscription. The explicit context limit above is a conservative example; set it to the smallest context supported by every upstream model serving hard. Tool capability depends on your chosen upstream model. Custom-tool grammar constraints are sent as instructions, not enforced by the adapter. Official Codex configuration reference.

Claude Code

export ANTHROPIC_BASE_URL="https://ai.safetyvibe.eu"
export ANTHROPIC_AUTH_TOKEN="$OPENAI_API_KEY"
export ANTHROPIC_DEFAULT_HAIKU_MODEL="easy"
export ANTHROPIC_DEFAULT_SONNET_MODEL="medium"
export ANTHROPIC_DEFAULT_OPUS_MODEL="hard"
claude --model medium

The base URL has no /v1 suffix. Messages text, images, tool_use and tool_result are translated to Chat Completions. Claude model names containing haiku, sonnet or opus map to easy, medium or hard respectively. You can also use an explicit provider/model. Anthropic prompt caching and extended thinking are not forwarded. Claude Code gateway documentation.

Responses with the OpenAI SDK

with client.responses.stream(model="hard", input="Explain binary search") as stream:
    for event in stream:
        if event.type == "response.output_text.delta":
            print(event.delta, end="", flush=True)

Sign in with aip

Discover endpoints at /.well-known/oauth-authorization-server (also available at /.well-known/openid-configuration). This is OAuth authorization with api and account scopes, not an OpenID issuer: no openid scope or ID tokens. Register a public client:

curl https://ai.safetyvibe.eu/oauth/register \
  -H 'Content-Type: application/json' \
  -d '{"client_name":"My tool","redirect_uris":["http://127.0.0.1/callback"],"grant_types":["authorization_code","refresh_token"],"token_endpoint_auth_method":"none"}'

Generate a random 43–128 character PKCE verifier and use base64url(SHA256(verifier)) without padding as the challenge. Open /oauth/authorize?response_type=code&client_id=YOUR_ID&redirect_uri=YOUR_URI&scope=api%20account&state=RANDOM_STATE&code_challenge=CHALLENGE&code_challenge_method=S256. After login and consent, check state in the callback and exchange the code:

curl https://ai.safetyvibe.eu/oauth/token \
  -d grant_type=authorization_code -d client_id=YOUR_ID \
  --data-urlencode redirect_uri=YOUR_URI -d code=CODE -d code_verifier=VERIFIER

Use the bearer access token on /v1/* (api scope) or /api/* and /oauth/userinfo (account scope). Access tokens last one hour; refresh tokens last at most 30 days. Refresh with grant_type=refresh_token, client_id, and refresh_token; atomically save both new tokens. Reusing a refresh token revokes its entire family. Revoke with form fields client_id and token at /oauth/revoke. HTTPS redirects match exactly. Loopback HTTP redirects allow any port but must retain the registered host, path, and query. Device clients register the urn:ietf:params:oauth:grant-type:device_code grant, then use the metadata's device endpoint with client_id and scope. Poll /oauth/token at the returned interval, increasing it by five seconds after every slow_down response.

Limits and retries

Default limits are 32 concurrent requests and 600 requests per minute per user. HTTP 429 includes Retry-After. Failover happens only before the first response byte; after a stream begins, an interrupted stream must be retried by your client. Provider model access, prices and free quotas can change. Model discovery is cached for one hour.