Developers
Free OpenAI-Compatible API
Chat completions with no API key, no account, and native tool calling — point any OpenAI SDK at ROK and it just works.
ROK is a free agentic AI — it browses the web, works with files, generates images, and reviews code in a single chat, no account required. This is the API behind it: free, keyless, and with native tool calling.
1. Overview — a free OpenAI-compatible API with no key
ROK exposes a public, OpenAI-compatible chat completions API at https://rokremasteredapi.rokteam.org/v1. Point any OpenAI SDK client at it by changing base_url — requests and responses use the same schema as the OpenAI API.
- No authentication required. This endpoint is public by design — no API key or account required to use it.
- Native tool calling.
toolsandtool_choiceare forwarded to the engine andtool_callsare surfaced in OpenAI format (streaming and non-streaming). It is a passthrough — your client runs the tools locally and sends results back asrole: "tool"messages. - Rate limited. 5 requests per minute per IP — excess requests return 429.
2. Authentication
No authentication is required. The official OpenAI SDKs insist on a non-empty api_key, so pass any placeholder string — it is accepted and ignored server-side:
from openai import OpenAI
client = OpenAI(
base_url="https://rokremasteredapi.rokteam.org/v1",
api_key="unused", # any non-empty string; not checked server-side
)3. Available models
GET https://rokremasteredapi.rokteam.org/v1/models returns the list in the standard OpenAI shape ( {"object": "list", "data": [{id, object, created, owned_by}]} ):
| Parameter | Type | Description |
|---|---|---|
rok-hermes | default | ROK's standard model. Used when the model field is omitted. |
rok-hermes-spicy | adult-content | ROK's adult-content variant with a fixed server-side personality prompt. Explicit and edgy creative content is allowed, but it still refuses real-world harm — minors, non-consensual content of real people, targeted harassment, and real-world weapons/explosives instructions. |
rok-hermes-spicy without an age gate. It still refuses real-world harm (minors, non-consensual content of real people, targeted harassment, and real-world weapons/explosives instructions). Use it accordingly.GLM 5.3 Flash and the GPT-5.6 Lunapair — selected from the chat's model picker. These are app-only and metered against ROK's own budget, so they are deliberately NOT exposed on this public API. The models above are the complete public set.An unknown model name is rejected with a 400 model_not_found error. Omitting model (or sending an empty string) silently defaults to rok-hermes.
4. Quickstart — curl
Non-streaming request:
curl https://rokremasteredapi.rokteam.org/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "rok-hermes",
"messages": [
{"role": "user", "content": "Write a haiku about the ocean."}
]
}'Streaming request — the -Nflag disables curl's output buffering so chunks arrive as they are generated:
curl -N https://rokremasteredapi.rokteam.org/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "rok-hermes",
"stream": true,
"messages": [
{"role": "user", "content": "Tell me a short joke."}
]
}'List available models:
curl https://rokremasteredapi.rokteam.org/v1/models
5. Quickstart — OpenAI SDK
The official openai SDKs work identically to using real OpenAI — just point base_url at ROK.
Python
from openai import OpenAI
client = OpenAI(
base_url="https://rokremasteredapi.rokteam.org/v1",
api_key="unused",
)
completion = client.chat.completions.create(
model="rok-hermes",
messages=[
{"role": "user", "content": "Write a haiku about the ocean."}
],
)
print(completion.choices[0].message.content)
# Streaming works too:
stream = client.chat.completions.create(
model="rok-hermes",
messages=[{"role": "user", "content": "Tell me a joke."}],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")JavaScript / TypeScript
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://rokremasteredapi.rokteam.org/v1",
apiKey: "unused",
});
const completion = await client.chat.completions.create({
model: "rok-hermes",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(completion.choices[0].message.content);6. Request parameters
POST https://rokremasteredapi.rokteam.org/v1/chat/completions — all fields except messages are optional.
| Parameter | Type | Description |
|---|---|---|
messages | array (required) | Chat messages as [{role, content}]. Roles: system, user, assistant, tool. Tool results use {role: "tool", tool_call_id, content}; assistant messages may carry tool_calls. Empty content is allowed on tool results and on assistant messages with tool_calls. |
tools | array | Function definitions forwarded to the engine. Returned tool_calls describe CLIENT-side tools — the server never executes them. See the tool-calling callout below. |
tool_choice | string | object | "auto" (default), "none", or {"type": "function", "function": {"name": "..."}} to force a specific tool. |
model | string | Model name. Optional — defaults to "rok-hermes". See Available models. |
stream | boolean | When true, return a server-sent-event (SSE) stream instead of a single JSON body. |
temperature | number | Sampling temperature. Defaults to 0.7. |
top_p | number | Nucleus sampling probability mass. |
max_tokens | integer | Maximum tokens to generate. Values above the cap are clamped, not rejected. |
max_completion_tokens | integer | Alias for max_tokens. Cannot be combined with max_tokens in the same request. |
frequency_penalty | number | Penalizes tokens based on how often they appear so far. |
presence_penalty | number | Penalizes tokens that have already appeared in the conversation. |
stop | string | string[] | Up to a few strings where the model should stop generating. |
seed | integer | Reproducibility hint for the sampler. |
stream_options | object | Set {"include_usage": true} to append a final usage chunk to a stream. |
tools and tool_choice are forwarded to the engine and tool_calls are returned in OpenAI format — but the server never executes tools. Run them on the client (files, commands, etc.) and send each result back as a role: "tool" message with the matching tool_call_id, then continue the conversation.max_tokens is clamped to a sane ceiling (4,096 by default) rather than forwarded as-is — requesting more never errors, it just generates up to the cap.7. Streaming format
With stream: true the response is a server-sent-event (SSE) stream of chat.completion.chunk objects. The first chunk carries the role; subsequent chunks carrydelta.content; a final chunk reports finish_reason; and the stream always terminates with data: [DONE].
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1755461120,"model":"rok-hermes","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1755461120,"model":"rok-hermes","choices":[{"index":0,"delta":{"content":" world"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1755461120,"model":"rok-hermes","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]Pass stream_options: {"include_usage": true} to receive one final usage chunk ( "choices": [] with a usage field) just before [DONE].
8. Errors
Errors follow the OpenAI shape: {"error": {"message", "type", "param", "code"}}.
{
"error": {
"message": "The model 'gpt-4o' does not exist or is not accessible via this API. Available models: rok-hermes, rok-hermes-spicy.",
"type": "invalid_request_error",
"param": "model",
"code": "model_not_found"
}
}| Status | error.type | When |
|---|---|---|
| 400 | invalid_request_error | Malformed body, empty messages, unsupported role or empty content, invalid model name, or misuse of max_tokens/max_completion_tokens. |
| 429 | rate_limit_error | Rate limit exceeded (5 requests/minute per IP). Includes Retry-After and X-RateLimit-* headers. |
| 502 | server_error | The upstream model service returned an error. |
| 503 | server_error | The model service is not configured on this server, unreachable, or temporarily rate limited. |
429 responses include Retry-After (seconds until the window resets) plus X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers:
{
"error": {
"message": "Rate limit exceeded: 5 requests per minute per IP. Please slow down and retry.",
"type": "rate_limit_error",
"param": null,
"code": "rate_limit_exceeded"
}
}9. Rate limits
The public API allows 5 requests per minute per IP. The client IP is taken from the CF-Connecting-IP header when present (ROK sits behind Cloudflare), falling back to the direct connection address — so the limit applies to the real caller, not a shared proxy.
- Requests beyond the limit are rejected with
429 rate_limit_error— the request is not queued, and the limit resets on a rolling per-minute window. - Streaming counts as a single request regardless of how many chunks it produces.
GET /v1/modelsis deliberately not rate limited — it returns a static list, so SDKs can probe it freely.