SporeLabs API Reference
SporeLabs is an OpenAI-compatible inference endpoint. Change your base_url and api_key, keep your existing client.
Base URL: https://sporelabs.dev/v1
Auth: Authorization: Bearer <api-key>
If you already have code that calls OpenAI, swap two lines and everything keeps working:
# Before
client = OpenAI(api_key="sk-...")
# After
client = OpenAI(
base_url="https://sporelabs.dev/v1",
api_key="sl-xxx...",
)
Request/response shapes, auth headers, SSE streaming format, tool calls, JSON mode, and error envelopes all follow the OpenAI API contract. What is different:
- Model IDs use OpenRouter's
provider/modelformat (or omitmodelentirely — see Model IDs). - Base URL is
https://sporelabs.dev/v1(nothttps://api.openai.com/v1). - Payment is a prepaid dollar wallet debited at real upstream spend (pass-through, no markup), not postpaid billing.
- Extra endpoints like
/v1/improve/settingsare SporeLabs-only additions beyond the OpenAI spec.
Quick Start (copy-paste)
Python (openai SDK)
from openai import OpenAI
client = OpenAI(
base_url="https://sporelabs.dev/v1",
api_key="sl-xxx...",
)
response = client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
Omit model (or send "auto") to let SporeLabs pick: traffic starts on Kimi K3 (moonshotai/kimi-k3) and only moves when a cheaper model is proven equal on your traffic.
curl
curl https://sporelabs.dev/v1/chat/completions \
-H "Authorization: Bearer sl-xxx..." \
-H "Content-Type: application/json" \
-d '{
"model": "openai/gpt-4o",
"messages": [{"role": "user", "content": "Hello!"}]
}'
JavaScript (openai npm)
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://sporelabs.dev/v1",
apiKey: "sl-xxx...",
});
const response = await client.chat.completions.create({
model: "openai/gpt-4o",
messages: [{ role: "user", content: "Hello!" }],
});
console.log(response.choices[0].message.content);
Anthropic SDK
The Anthropic surface (/v1/messages) is served from the same base URL — use x-api-key or Bearer with your SporeLabs key:
import anthropic
client = anthropic.Anthropic(
base_url="https://sporelabs.dev/v1",
api_key="sl-xxx...",
)
message = client.messages.create(
model="anthropic/claude-sonnet-4-20250514",
max_tokens=1024,
messages=[{"role": "user", "content": "Hello!"}],
)
print(message.content[0].text)
Streaming (SSE)
stream = client.chat.completions.create(
model="openai/gpt-4o",
messages=[{"role": "user", "content": "Count to 5"}],
stream=True,
)
for chunk in stream:
print(chunk.choices[0].delta.content or "", end="")
OpenAI-Compatible Endpoints
These endpoints match the OpenAI API shape so any OpenAI SDK works with a base URL swap.
POST /v1/chat/completions
Create a chat completion.
Request body (OpenAI ChatCompletionRequest):
| Field | Type | Required | Notes |
|---|---|---|---|
model |
string | no | OpenRouter slug like openai/gpt-4o, or omit / "auto" to let SporeLabs route (starts on Kimi K3). See Model IDs. |
messages |
array | yes | Standard OpenAI message array. Roles: system, user, assistant, tool. |
stream |
boolean | no | Enable SSE streaming (default false). |
temperature |
number | no | Sampling temperature (0-2, default varies by model). |
max_tokens |
integer | no | Maximum tokens in response. |
tools |
array | no | Function/tool definitions. Supported. |
tool_choice |
string/object | no | "auto", "any", "none", or {"type":"function","function":{"name":"..."}}. |
response_format |
object | no | {"type": "json_object"} for JSON mode. |
stop |
string/array | no | Stop sequences. |
seed |
integer | no | Deterministic sampling (best-effort). |
frequency_penalty |
number | no | -2 to 2. |
presence_penalty |
number | no | -2 to 2. |
top_p |
number | no | Nucleus sampling (0-1). |
user |
string | no | End-user identifier for usage tracking. |
Response (OpenAI ChatCompletion):
{
"id": "chatcmpl-xxx",
"object": "chat.completion",
"created": 1698850000,
"model": "openai/gpt-4o",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "Hello! How can I help you?"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 7,
"total_tokens": 19
}
}
When stream=true, each chunk follows the OpenAI SSE format:
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1698850000,"model":"openai/gpt-4o","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"choices":[{"index":0,"delta":{"content":"Hello"},"finish_reason":null}]}
data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
GET /v1/models
List available models: a minimal set — auto, the Kimi incumbent, and owned adapters — not the full OpenRouter index.
Response:
{
"object": "list",
"data": [
{
"id": "openai/gpt-4o",
"object": "model",
"created": 1698850000,
"owned_by": "openai"
}
]
}
GET /v1/models/{model_id}
Retrieve a specific model by its OpenRouter slug.
SporeLabs-Specific Endpoints
These are not part of the OpenAI standard but use the same base URL and bearer token. Full details in the Jobs API doc.
| Method | Path | Description |
|---|---|---|
| POST | /v1/improve/settings |
Save improvement routing/specialization settings. |
| POST | /v1/specializations/{spec_id}/apply |
Apply a specialization pattern. |
| GET | /v1/agent-proposals |
List hosted agent proposals. |
| POST | /v1/agent/codes |
Generate an invite code for an agent session. |
| POST | /v1/agent/token |
Exchange invite code for a short-lived agent token. |
| GET | /v1/jobs |
List training/inference jobs (read-only support endpoint). |
| GET | /v1/jobs/{job_id} |
Get details for one job (read-only support endpoint). |
| Various | /v1/datasets |
Import, export, delete datasets. |
Authentication
All endpoints use standard bearer authentication:
Authorization: Bearer <api-key>
Get your API key from the SporeLabs portal dashboard.
Errors
Errors follow the OpenAI error envelope:
{
"error": {
"message": "Insufficient funds",
"type": "insufficient_quota",
"param": null,
"code": "insufficient_quota"
}
}
Common error types:
| HTTP Status | Type | Meaning |
|---|---|---|
| 400 | invalid_request_error |
Malformed request body. On the Anthropic surface (/v1/messages), an empty wallet also returns 400. |
| 401 | authentication_error |
Missing or invalid API key. |
| 404 | invalid_request_error |
Unknown model or endpoint. |
| 429 | insufficient_quota |
Wallet is empty — top up in the portal. Also used for rate limiting. 402 is never returned on SDK paths. |
| 503 | upstream_unavailable / api_error |
Upstream provider blip; retry with backoff. |
| 500 | api_error |
Server error; retry with backoff. |
Model IDs
Omit model (or send auto) and SporeLabs routes for you: Auto starts on Kimi K3 (moonshotai/kimi-k3) and moves a job to a cheaper model only after proving equality on your traffic. Name a model and that exact model serves the call.
Named models use OpenRouter slugs. Common ones:
moonshotai/kimi-k3
openai/gpt-4o
openai/gpt-4o-mini
openai/o3-mini
anthropic/claude-sonnet-4-20250514
anthropic/claude-3.5-haiku-20241022
google/gemini-2.5-pro-preview-03-25
meta-llama/llama-3.3-70b-instruct
Get the current list from GET /v1/models.
Limits
- Context window: Depends on model (8K-200K tokens).
- Max response tokens: Depends on model (4K-128K).
- Streaming: Supported for all chat models.
- Rate limits: Contact support if you need higher limits for your workload.