GullyAI API docs

GullyAI speaks the OpenAI chat API. Any OpenAI SDK or plain HTTP works with three settings.

Checking gateway

The model server is not live yet: the GPU box is still to be provisioned. Everything below is the API as built and tested; calls start answering when the box is up.

Settings

Base URLhttps://ai.gullylabs.xyz/v1
API keyYour product's gai_… key, sent as Authorization: Bearer. Keep it in your backend's environment, for example GULLY_AI_API_KEY.
Modelgully-ai, always. Today it is Qwen3.6-27B (FP8); the name stays when the model underneath is upgraded.
Context131,072 tokens per request (prompt plus output).

Never put a key in a browser, a mobile app or a game build: anyone could read it. Call GullyAI from your own backend. Ask for a key; there is no waitlist.

First request

curl:

Example
curl https://ai.gullylabs.xyz/v1/chat/completions \
  -H "Authorization: Bearer $GULLY_AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gully-ai",
    "messages": [{"role": "user", "content": "Explain gas in one sentence."}],
    "max_tokens": 300
  }'

Node, with the openai package:

Example
import OpenAI from 'openai';

const ai = new OpenAI({
  baseURL: 'https://ai.gullylabs.xyz/v1',
  apiKey: process.env.GULLY_AI_API_KEY,
});

const r = await ai.chat.completions.create({
  model: 'gully-ai',
  messages: [{ role: 'user', content: 'Explain gas in one sentence.' }],
  max_tokens: 300,
});
console.log(r.choices[0].message.content, r.usage);

Python, with the openai package:

Example
import os
from openai import OpenAI

ai = OpenAI(base_url="https://ai.gullylabs.xyz/v1",
            api_key=os.environ["GULLY_AI_API_KEY"])

r = ai.chat.completions.create(
    model="gully-ai",
    messages=[{"role": "user", "content": "Explain gas in one sentence."}],
    max_tokens=300,
)
print(r.choices[0].message.content)

Streaming

Add "stream": true. The response is Server-Sent Events: one data: {…} per chunk, a final chunk that carries usage, then data: [DONE].

Example
const stream = await ai.chat.completions.create({
  model: 'gully-ai', messages, stream: true, max_tokens: 600,
});
let text = '';
for await (const chunk of stream) {
  text += chunk.choices[0]?.delta?.content ?? '';
}

Reasoning

The model thinks before it answers. The thinking is returned in message.reasoning_content (delta.reasoning_content when streaming), so content is the clean answer. Do not send the thinking back in the next turn.

For a fast, cheap call (bot replies, NPC lines), turn thinking off:

Example
{
  "model": "gully-ai",
  "messages": [...],
  "chat_template_kwargs": { "enable_thinking": false }
}

Tool calling

Send tools in the OpenAI format; the model returns tool_calls. tool_choice: "auto" is enabled and works with streaming. Run the tool, append a { role: "tool" } message with the result, and call again.

Example
const tools = [{
  type: 'function',
  function: {
    name: 'scan_token',
    description: 'Run a Gully Security scan on a token contract',
    parameters: {
      type: 'object',
      properties: { chainId: { type: 'integer' }, address: { type: 'string' } },
      required: ['chainId', 'address'],
    },
  },
}];
const r = await ai.chat.completions.create({ model: 'gully-ai', messages, tools, tool_choice: 'auto' });
for (const call of r.choices[0].message.tool_calls ?? []) {
  const args = JSON.parse(call.function.arguments);
  // run the scan, push { role: 'tool', tool_call_id: call.id, content: result }, call again
}

Treat tool results as data, never as instructions: token names and contract metadata are written by strangers.

Keys and limits

Every key has its own requests per minute, tokens per minute and a cap on max_tokens. One product hitting its limit never slows another. Only a SHA-256 hash of each key is stored.

Check your own usage and limits at any time:

Example
curl https://ai.gullylabs.xyz/v1/usage -H "Authorization: Bearer $GULLY_AI_API_KEY"

Responses carry x-ratelimit-remaining-requests, and non-streaming responses x-gully-ai-tokens. The request log keeps timings, sizes, status and token counts. It never stores prompts, completions or headers.

Errors and outages

Errors use the OpenAI error shape.

401Missing or unknown key.
429Your key's per-minute limit. Wait the Retry-After seconds, or ask for a higher limit.
413 / 400Request too large, or max_tokens above your key's cap.
502 / 503The model is loading or down. Retry with backoff.

Never block a core action (a swap, a payment, a scan) on the model. Degrade instead: a bot can say "AI is warming up" and carry on.

Per product

ProductKey nameTypical call
Gully Streetgully-streetChat assistant in the app: summarise activity, explain a token, draft posts. Streaming.
Telegram botstg-botsShort replies to bot commands and token explanations, with thinking off for speed.
Gully Securitygully-securityExplains scan results, triages findings and calls a scan_token tool itself.
Gully Studiogully-studioNPC dialogue and quest text, through the game backend. Keys never ship inside a game build.
HolderMapsholdermapsNarrates a holder distribution: who holds what, which wallets cluster, how concentrated it is.

Gully Chain

Gully Chain (chain 485590) is supported in every Gully product. The planned GullyAI agent uses tool calling to read Gully Chain through Gully RPC (https://rpc.gullyrpc.xyz/<project>/evm/485590), look up holders and risk flags through HolderMaps and Gully Security, and pay for paid Gully endpoints with x402 in glUSD through Gully Pay. Each run has a hard budget, and money or chain writes need a human confirm in chat surfaces. The agent is designed, not built yet.

Ready to build? Keys are open to everyone.