Overview
Base URL: https://iamgravity.eu/api/gravity/v1
All requests and responses are JSON, sent over HTTPS. Usage is charged to your Gravity token balance. Buy tokens before you start. Prompts and answers are not stored.
API keys
Create a key in your account under API keys (profile menu). A key looks like grav_ followed by random characters. It is shown once, so copy it right away. Only a hash is kept on our side. You can have up to 10 active keys and revoke any of them at any time; a revoked key stops working immediately.
Send the key in the Authorization header of every request:
Authorization: Bearer grav_xxxxxxxxxxxxxxxxxxxxxxxx
The Gravity desktop app signs in on its own and uses its session token in the same header; you do not need a key for the app.
Keep keys secret. Do not put them in browser code, public repositories or screenshots.
List models
GET /models
GET /api/gravity/v1/models
Authorization: Bearer grav_...
Response:
{
"object": "list",
"data": [
{
"id": "gravity-coder",
"object": "model",
"owned_by": "gravity",
"name": "Gravity Coder",
"description": "Tuned for writing and fixing code across several files.",
"context_window": 32768,
"max_output_tokens": 4096,
"pricing": { "input_per_1k": 70, "output_per_1k": 105 }
}
]
}
Use the id as the model value in chat requests. pricing is the number of Gravity tokens charged for every 1,000 tokens the model reads (input) or writes (output). The list depends on what is currently available.
Chat completions
POST /chat/completions
POST /api/gravity/v1/chat/completions
Authorization: Bearer grav_...
Content-Type: application/json
{
"model": "gravity-coder",
"messages": [
{ "role": "system", "content": "You are a careful C# reviewer." },
{ "role": "user", "content": "Explain what this method does." }
],
"max_tokens": 1024,
"temperature": 0.2,
"stream": false
}
Response:
{
"id": "chatcmpl-...",
"object": "chat.completion",
"model": "...",
"choices": [
{ "index": 0, "message": { "role": "assistant", "content": "..." }, "finish_reason": "stop" }
],
"usage": { "prompt_tokens": 42, "completion_tokens": 180, "total_tokens": 222 }
}
| Field | Notes |
|---|---|
model | Required. An id from GET /models. |
messages | Required. Non-empty list of system, user, assistant and tool messages. Maximum request size 2 MB. |
max_tokens | Optional. Capped to the model limit and to what your balance can pay for. |
temperature, top_p, stop, tools | Optional, passed to the model when it supports them. |
stream | Optional. true returns server-sent events. |
n, user, stream_options | Ignored. |
Streaming
With "stream": true the response is text/event-stream. Each event is a line starting with data: that holds a JSON chunk, and the stream ends with data: [DONE].
data: {"choices":[{"index":0,"delta":{"content":"Hello"}}]}
data: {"choices":[{"index":0,"delta":{"content":" world"}}]}
data: [DONE]
If you close the connection early, you are charged for what was generated so far.
Balance
GET /api/gravity/balance (note: no /v1)
{ "balance": 482310, "buy_url": "https://iamgravity.eu/GravityAi" }
Billing
Cost of a request in Gravity tokens, rounded up, minimum 1:
cost = ceil( prompt_tokens / 1000 x input_per_1k + completion_tokens / 1000 x output_per_1k )
Your balance never goes below zero. Before a request runs we check the balance can cover the prompt, and the answer length is limited to what the balance can still pay for. When the model does not report usage while streaming, the tokens are estimated.
Errors and limits
Errors use this shape:
{ "error": { "message": "Not enough Gravity tokens. Buy more at https://iamgravity.eu/GravityAi", "type": "gravity_error", "code": "insufficient_tokens" } }
| HTTP | code | Meaning |
|---|---|---|
| 400 | invalid_request | Body is not valid JSON, messages is missing, or the model rejected the request. |
| 401 | invalid_api_key | Missing, wrong or revoked key. |
| 402 | insufficient_tokens | Balance too low. Buy tokens. |
| 404 | model_not_found | Unknown or unavailable model id. |
| 413 | request_too_large | Request larger than 2 MB. |
| 429 | rate_limited | More than 60 requests per minute per account, or the model is busy. Retry after a moment. |
| 502 / 503 | provider_error, service_unavailable | Temporary problem on our side. Retry with a short delay. Failed requests are not charged. |
Examples
curl
curl https://iamgravity.eu/api/gravity/v1/chat/completions \
-H "Authorization: Bearer $GRAVITY_API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"gravity-coder","messages":[{"role":"user","content":"Write a C# extension method to chunk a list."}]}'
C#
using var http = new HttpClient { BaseAddress = new Uri("https://iamgravity.eu/api/gravity/v1/") };
http.DefaultRequestHeaders.Authorization = new("Bearer", Environment.GetEnvironmentVariable("GRAVITY_API_KEY"));
var response = await http.PostAsJsonAsync("chat/completions", new
{
model = "gravity-coder",
messages = new[] { new { role = "user", content = "Explain dependency injection in two sentences." } }
});
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
Python (OpenAI SDK)
from openai import OpenAI
client = OpenAI(base_url="https://iamgravity.eu/api/gravity/v1", api_key="grav_...")
reply = client.chat.completions.create(
model="gravity-coder",
messages=[{"role": "user", "content": "Hello"}],
)
print(reply.choices[0].message.content)