Getting started
Quickstart
Send the first request in a few minutes. Nothing is reinstalled: the client library you already use, pointed at a different base URL.
GTokens is a gateway to frontier models, billed per token at published rates. It speaks two API shapes: the OpenAI chat shape, which serves text and image models alike, and the native Messages API shape, which serves text models for the client libraries and agent runtimes built for it. One key and one prepaid balance serve both.
There are three ways in, depending on how much of your own code you want to keep.
| Approach | Best for |
|---|---|
| Plain HTTP | Any language, no dependency to add. One POST and a bearer header. |
| OpenAI library | Text and image models, and any code already written against the OpenAI client. Change the base URL and the key; the rest of the call stays. |
| Messages API client | Text models through a Messages API client or an agent runtime. Two environment variables are usually the whole change. |
Scroll the table horizontally for the second column.
You need a key and a balance. Keys are created in the account area — as many as you need — and look like sk-sl-…: the prefix, then 43 random characters, shown once when created. The balance is prepaid; a request that the balance cannot cover comes back 402 payment_required and is not charged.
Using the API directly
Every route is a JSON POST with one header for the key. Nothing else is required and no package has to be installed, which makes this the shortest way to check that the key and the base URL are right.
curl https://api.gtokens.io/api/v1/chat/completions \
-H "Authorization: Bearer sk-sl-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-opus-5",
"messages": [{"role": "user", "content": "What is the meaning of life?"}]
}'import requests
r = requests.post(
"https://api.gtokens.io/api/v1/chat/completions",
headers={"Authorization": "Bearer sk-sl-your-key"},
json={
"model": "anthropic/claude-opus-5",
"messages": [{"role": "user", "content": "What is the meaning of life?"}],
},
timeout=300,
)
body = r.json()
print(body["choices"][0]["message"]["content"])
print(body["usage"]["cost"], "USD")const r = await fetch('https://api.gtokens.io/api/v1/chat/completions', {
method: 'POST',
headers: {
Authorization: 'Bearer sk-sl-your-key',
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: 'anthropic/claude-opus-5',
messages: [{ role: 'user', content: 'What is the meaning of life?' }],
}),
})
const body = await r.json()
console.log(body.choices[0].message.content)
console.log(body.usage.cost, 'USD')The answer comes back in choices[0].message.content, and usage carries the token counters and cost — the exact amount this request was charged. A ceiling on the reply, max_tokens, is 4096 when you do not send one.
The model is named by its catalogue identifier, in the form vendor/model. The catalogue is fixed: there are no aliases, no automatic upgrades and no substitution — the model you name is the model that answers, or the request is refused. Read the catalogue from the API
Using the OpenAI library
The gateway speaks the OpenAI shapes for text and image models alike, so existing code keeps working: the base URL becomes https://api.gtokens.io/api/v1 and the key becomes the API key. The library sends a bearer token by default, which is exactly what the gateway reads. Tools, streaming and prompt caching go through in the shape the library already speaks.
from openai import OpenAI
client = OpenAI(
base_url="https://api.gtokens.io/api/v1",
api_key="sk-sl-your-key",
)
completion = client.chat.completions.create(
model="anthropic/claude-opus-5",
messages=[{"role": "user", "content": "What is the meaning of life?"}],
)
print(completion.choices[0].message.content)
# The same call as a stream: chunks arrive while the model is still writing.
stream = client.chat.completions.create(
model="anthropic/claude-opus-5",
messages=[{"role": "user", "content": "What is the meaning of life?"}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="", flush=True)import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.gtokens.io/api/v1',
apiKey: 'sk-sl-your-key',
})
const completion = await client.chat.completions.create({
model: 'anthropic/claude-opus-5',
messages: [{ role: 'user', content: 'What is the meaning of life?' }],
})
console.log(completion.choices[0].message.content)
// The same call as a stream: chunks arrive while the model is still writing.
const stream = await client.chat.completions.create({
model: 'anthropic/claude-opus-5',
messages: [{ role: 'user', content: 'What is the meaning of life?' }],
stream: true,
})
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta.content ?? '')
}# The library only sets a base URL and a bearer token, so the raw call
# is the same request with the same two headers. -N lets the chunks print
# as they arrive.
curl -N https://api.gtokens.io/api/v1/chat/completions \
-H "Authorization: Bearer sk-sl-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-opus-5",
"messages": [{"role": "user", "content": "What is the meaning of life?"}],
"stream": true
}'Two routes are served in this shape: chat/completions for text and for image generation, and images for edits, where the frame to work from is passed in input_references and the reply carries data[].b64_json. Every field of a chat completion
Using the Messages API
Text models also answer on the Messages API, in the request and response shapes of the matching client library — the way to go when that library or an agent runtime is what you hold. The base URL is the gateway root — the library appends the path itself — and the key goes in as the auth token, not the API key: the x-api-key header is not read on any route.
from anthropic import Anthropic
client = Anthropic(
base_url="https://api.gtokens.io",
auth_token="sk-sl-your-key", # auth_token, not api_key
)
message = client.messages.create(
model="anthropic/claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Say hello"}],
)
print(message.content[0].text)import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic({
baseURL: 'https://api.gtokens.io',
authToken: 'sk-sl-your-key', // authToken, not apiKey
})
const message = await client.messages.create({
model: 'anthropic/claude-sonnet-5',
max_tokens: 1024,
messages: [{ role: 'user', content: 'Say hello' }],
})
console.log(message.content[0])curl https://api.gtokens.io/api/v1/messages \
-H "Authorization: Bearer sk-sl-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "anthropic/claude-sonnet-5",
"max_tokens": 1024,
"messages": [{"role": "user", "content": "Say hello"}]
}'Add stream: true to receive server-sent events. Clients that take their settings from the environment need the same two values and no code change at all:
export ANTHROPIC_BASE_URL=https://api.gtokens.io
export ANTHROPIC_AUTH_TOKEN=sk-sl-your-keyGenerating a picture
An image model is called on the same chat/completions route with the same body; what changes is the model and the reply, which carries the frame as a data URL in choices[0].message.images[0].image_url.url instead of text.
curl https://api.gtokens.io/api/v1/chat/completions \
-H "Authorization: Bearer sk-sl-your-key" \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-5.4-image-2",
"messages": [{"role": "user", "content": "A lighthouse at dawn"}],
"image_config": {"aspect_ratio": "9:16"}
}'from openai import OpenAI
client = OpenAI(
base_url="https://api.gtokens.io/api/v1",
api_key="sk-sl-your-key",
)
completion = client.chat.completions.create(
model="openai/gpt-5.4-image-2",
messages=[{"role": "user", "content": "A lighthouse at dawn"}],
extra_body={"image_config": {"aspect_ratio": "9:16"}},
)
frame = completion.choices[0].message.images[0]["image_url"]["url"]import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://api.gtokens.io/api/v1',
apiKey: 'sk-sl-your-key',
})
const completion = await client.chat.completions.create({
model: 'openai/gpt-5.4-image-2',
messages: [{ role: 'user', content: 'A lighthouse at dawn' }],
// @ts-expect-error image_config is a gateway field
image_config: { aspect_ratio: '9:16' },
})
const [image] = (completion.choices[0].message as any).imagesGeometry is set either by an aspect ratio, or by exact pixels in size — for example "1080x1920", which is honoured to the pixel. A tier such as "2K" is a normalised size, not an exact one: with an aspect ratio it decides the delivered frame. Up to ten frames per request with n.
What's next
- API reference — the request and response shape shared by every route, and what
usagemeans. - Parameters — every field a request body accepts, and what happens to the ones we do not read.
- Errors — status codes, typed codes and the body they arrive in.
- Limits — balance, ceilings, the reserve held during a request and what bounds throughput.
- Model catalogue — what is served, what is listed but not served yet, and at what rate.
