Documentation

One endpoint. You send messages, you get a reply. Every number on this page is enforced by the running server.

POST /api/v1/chat no key

Quickstart

A streamed reply, in one command:

shell
curl -N https://www.ai.latdlabs.in/api/v1/chat \
  -H "content-type: application/json" \
  -d '{
    "messages": [
      { "role": "user", "content": "What is a haiku? One example, please." }
    ],
    "stream": true
  }'

What comes back, abbreviated:

stream
data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"A hai"}}]}

data: {"object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}

data: [DONE]

Messages

messages is the only required field. Each entry has a role and content.

rolewhat it does
systemApplies to the whole request. If you send several, the first is used.
userYour turn. Can carry an image.
assistantAn earlier reply. Send these back to keep a conversation going.
Nothing is stored. No session, no history, no database. The conversation lives in your client and you send it back with every request — so a fresh call knows only what you give it.

Request fields

fieldtypenotes
messagesrequiredTurns, oldest first. Must not be empty.
streambooleanDefault true. false returns one JSON object.
modelstringAccepted and echoed, but it does not select anything.
temperaturenumberAccepted for compatibility. Does not change the answer.
max_tokensintegerAccepted for compatibility. Reply length is set by the server.
systemstringShorthand for a leading system message.

Unknown fields are ignored, not rejected, so a request built for another provider still answers.

Response headers
headermeaning
x-kova-image-turns1 if the request carried an image.
x-ratelimit-limitRequests allowed per minute.
x-ratelimit-remainingLeft in the current window.
retry-afterSeconds to wait. Only on 429.

Images

An image is a part inside a user message — not a separate upload, and not a property of the conversation.

json
{
  "role": "user",
  "content": [
    { "type": "text", "text": "What is in this picture?" },
    { "type": "image_url",
      "image_url": { "url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ…" } }
  ]
}

url also takes a plain link (https://example.com/chart.png). The server fetches it before sending, so the address must be reachable without a login.

How images behave
  • Read on the turn that carries it. Later turns are text — ask about the picture in the message that includes it.
  • A picture alone still needs words. If the text part is empty, the server asks a plain "what do you see" for you.
  • Formats: JPEG, PNG, WebP and GIF all work.
  • One image per turn. If a turn has more than one, the first is used.

Streaming

With "stream": true the response is text/event-stream: one line per event beginning data:, then a blank line, ending in data: [DONE].

eventcarries
deltachoices[0].delta.content — append it.
usageEmpty choices, a usage object.
finishfinish_reason: "stop" on the last one.
[DONE]A bare string, not JSON. Stop reading.
A minimal browser reader
javascript
const r = await fetch('/api/v1/chat', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify({
    messages: [{ role: 'user', content: 'Tell me a short joke.' }],
  }),
});

const reader = r.body.getReader();
const dec = new TextDecoder();
let buf = '', out = '';

while (true) {
  const { value, done } = await reader.read();
  if (done) break;
  buf += dec.decode(value, { stream: true });

  let i;
  while ((i = buf.indexOf('\n')) >= 0) {
    const line = buf.slice(0, i).trim();
    buf = buf.slice(i + 1);
    if (!line.startsWith('data:')) continue;
    const payload = line.slice(5).trim();
    if (payload === '[DONE]') break;
    const piece = JSON.parse(payload).choices?.[0]?.delta?.content;
    if (piece) { out += piece; console.log(out); }
  }
}

Plain JSON

Set "stream": false for scripts and cron jobs.

response
{
  "object": "chat.completion",
  "model": "glitchyapi",
  "choices": [{
    "index": 0,
    "message": { "role": "assistant", "content": "Tokyo." },
    "finish_reason": "stop"
  }],
  "usage": { "prompt_tokens": 8, "completion_tokens": 2, "total_tokens": 10 }
}

Errors

One JSON object, a status that means something, never a stack trace.

error
{
  "error": {
    "message": "`messages` must be a non-empty array.",
    "type": "invalid_request_error",
    "code": "missing_messages"
  }
}
statuswhencode
400Unreadable request: no messages, bad JSON, wrong types, over a size limit.invalid_request_error
413Image larger than the published ceiling.image_too_large
429Over 60 a minute. See retry-after.too_many_requests
502The service could not complete the answer. Retrying usually works.service_error

Limits

Published from the file that enforces them, so the two cannot drift.

whatlimit
requests / minute60 per client address
messages / request200
characters / message100,000
text / request400,000 characters
image, base643.4M chars (~3M bytes)
request body24 MB
remote image6 MB

Under heavy free use a request may wait a few seconds before it starts. It still streams; it just starts late. That wait is not counted against the per-minute limit.

Service

Two endpoints that describe the service rather than answering a question.

GET /api/health
json
{
  "ok": true,
  "service": "glitchyapi",
  "api": "/api/v1/chat",
  "streaming": true,
  "json": true,
  "images": true,
  "limits": "/docs#limits",
  "docs": "/docs"
}
GET /api/v1/models

One entry, so tools that insist on listing models get an answer. There is nothing to choose.

json
{ "object": "list", "data": [
  { "id": "glitchyapi", "object": "model", "selectable": false }
] }

Questions

Do I need an API key?

No. Requests are sent as they are, with nothing in the headers.

Does it remember my last message?

No. Every request is independent. Send the earlier turns back inside messages and the reply is written in context.

Why is the model field ignored?

There is one model behind the endpoint and the server chooses it. Accepting the field keeps clients built for other providers working.

Is there a JavaScript or Python library?

No, and none is needed — JSON in, JSON out. fetch and requests are enough.

Is my conversation private?

Nothing is written to disk by this service, so there is no chat log to look at afterwards. It travels over HTTPS like any other request. Do not send secrets you would not send anywhere else.

Can I stream and send an image together?

Yes. The two are independent.

Try it in the chat