API documentation
The LessStrict API is OpenAI-compatible. If your code already talks to OpenAI, change the base URL and the API key and use our model slugs. Everything else keeps working.
Quickstart
- Create an account and open Dashboard, API keys. Create a key and copy it; it is shown once.
- Send your first request:
curl https://lessstrict.com/v1/chat/completions \
-H "Authorization: Bearer lsk_your_key" \
-H "Content-Type: application/json" \
-d '{
"model": "ls-lite",
"messages": [
{"role": "system", "content": "You are a terse assistant."},
{"role": "user", "content": "Give me a regex for ISO 8601 dates."}
]
}'
The response has the standard OpenAI shape:
{
"id": "chatcmpl-9f2b...",
"object": "chat.completion",
"created": 1762500000,
"model": "ls-lite",
"choices": [{ "index": 0, "message": { "role": "assistant", "content": "^\\d{4}-\\d{2}-\\d{2}$" }, "finish_reason": "stop" }],
"usage": { "prompt_tokens": 31, "completion_tokens": 12, "total_tokens": 43 }
}
Authentication
Every request needs an API key in the Authorization header as a bearer token. Keys start with lsk_. Treat them like passwords: keep them on the server, never in client-side code.
Authorization: Bearer lsk_live_...
Each key has a billing mode that you choose when you create it and can change later:
- AUTO (default): use the plan allowance for the model first; when it is used up or the model is not in the plan, charge the wallet.
- SUBSCRIPTION: plan allowance only. Requests fail with
402 plan_limitwhen it is exhausted. - WALLET: always pay per token from the wallet, never touching plan allowances.
Keys can also be restricted to specific models and given a monthly spend cap.
Base URL
https://lessstrict.com/v1
All endpoints below are relative to this URL. Requests must use HTTPS in production. CORS is enabled for GET and POST, but calling the API from a browser exposes your key, so proxy through your own backend.
Chat completions
POST /v1/chat/completions
Generates a reply for a conversation. Parameters:
| Field | Type | Notes |
|---|---|---|
| model | string | A LessStrict model slug, e.g. "ls-lite". See GET /v1/models. |
| messages | array | Messages with role system, user or assistant. Content is a string or an array of text and image_url parts (images only on vision models). |
| stream | boolean | true to receive server-sent events. Default false. |
| temperature | number | 0 to 2. Defaults to the model's default. |
| max_tokens | integer | Cap on output tokens. Clamped to the model's max output. max_completion_tokens is accepted as an alias. |
| top_p | number | 0 to 1. |
| stop | string | string[] | Stop sequences. |
| response_format | object | {"type": "json_object"} for JSON mode on models that support it. |
| web_search | boolean | LessStrict extension. Runs a web search for the last user message and gives the results to the model, which cites them as [n]. Uses your plan's search allowance, then the wallet. |
Example with web search and JSON mode:
{
"model": "ls-lite",
"web_search": true,
"response_format": { "type": "json_object" },
"messages": [
{ "role": "system", "content": "Reply with JSON: {\"summary\": string, \"sources\": string[]}" },
{ "role": "user", "content": "What changed in the latest Node.js LTS release?" }
]
}
A system message you send is appended to LessStrict's own system prompt for the model (identity, date, capabilities). Your instructions take precedence for style and format.
Streaming
With "stream": true the response is text/event-stream. Each event is a chat.completion.chunk in OpenAI format. The last content chunk is followed by a usage chunk and then data: [DONE].
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1762500000,"model":"ls-lite","choices":[{"index":0,"delta":{"role":"assistant","content":"Hel"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1762500000,"model":"ls-lite","choices":[{"index":0,"delta":{"content":"lo"},"finish_reason":null}]}
data: {"id":"chatcmpl-...","object":"chat.completion.chunk","created":1762500000,"model":"ls-lite","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":12,"completion_tokens":2,"total_tokens":14}}
data: [DONE]
If the upstream provider fails mid-stream you receive a chunk with an error object and the stream ends. Tokens are only billed for successful or client-cancelled responses, never for upstream errors.
Models
GET /v1/models
Lists the models your key can use. The shape matches OpenAI's list with extra fields:
{
"object": "list",
"data": [
{
"id": "ls-chat",
"object": "model",
"created": 1762500000,
"owned_by": "lessstrict",
"name": "LS Coder",
"description": "Built for software engineering",
"context_length": 262144,
"max_output_tokens": 32768,
"pricing": { "input_per_1m": 0.5, "output_per_1m": 2.0, "currency": "USD" },
"capabilities": { "vision": false, "tools": true, "json": true, "reasoning": false, "streaming": true },
"categories": ["coding", "agents"]
}
]
}
GET /v1/models/{id} returns a single model.
Account
GET /v1/me
Returns the state of the account behind the key: wallet balance, plan, and remaining allowance per model for the current period. Use it to decide which model to call or to show your own users what is left.
{
"balance_usd": 12.4,
"plan": { "slug": "pro", "name": "Pro", "requests_per_minute": 60 },
"key": { "name": "backend", "billing_mode": "AUTO" },
"allowances": [
{ "model": "ls-chat", "monthly_tokens": 6000000, "used_month_tokens": 418233, "daily_tokens": null, "used_day_tokens": 12000 }
]
}
Response headers
| Header | Meaning |
|---|---|
| x-request-id | Unique ID for the request. Quote it in support requests. |
| x-ls-billed-via | plan or wallet: which source paid for the request. |
| x-ls-charge-usd | Amount deducted from the wallet, 0 when covered by the plan. Sent as a trailer-free header on non-streaming responses. |
| x-ratelimit-limit | Requests allowed per minute on your plan. |
| x-ratelimit-remaining | Requests left in the current minute. |
Errors
Errors use the OpenAI shape so SDK error handling works unchanged:
{ "error": { "message": "You have used this month's Pro allowance for LS Ultra.", "type": "billing_error", "code": "plan_limit" } }
| Status | Code | When |
|---|---|---|
| 400 | invalid_request | Malformed body or unsupported parameter. |
| 401 | invalid_api_key | Missing, revoked, expired or unknown key. |
| 402 | plan_limit | Plan allowance for the model is used up and no wallet fallback applies. |
| 402 | insufficient_funds | Wallet is empty on a wallet-billed request. |
| 402 | no_payg | Model is only available through plans and your plan does not include it. |
| 403 | model_not_allowed_for_key | Key is restricted to other models, or the account lacks API access. |
| 404 | model_not_found | Unknown model slug. |
| 429 | rate_limited | Requests per minute exceeded. Retry after the delay in the message. |
| 502 | upstream_error | The provider behind the model failed. Nothing was billed. |
| 503 | model_unavailable | The model is disabled or its provider is not configured. |
Official SDKs
Point the OpenAI SDK at LessStrict. No other change is needed.
Python
from openai import OpenAI
client = OpenAI(base_url="https://lessstrict.com/v1", api_key="lsk_...")
resp = client.chat.completions.create(
model="ls-chat",
messages=[{"role": "user", "content": "Write a Python function that parses a cron expression."}],
)
print(resp.choices[0].message.content)
Node.js
import OpenAI from "openai";
const client = new OpenAI({ baseURL: "https://lessstrict.com/v1", apiKey: process.env.LESSSTRICT_API_KEY });
const stream = await client.chat.completions.create({
model: "ls-chat",
messages: [{ role: "user", content: "Write a TypeScript function that parses a cron expression." }],
stream: true,
});
for await (const chunk of stream) process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
Tools that accept a custom OpenAI base URL (editors, agents, chat clients) work the same way: base URL https://lessstrict.com/v1, API key lsk_…, model set to a LessStrict slug.
Limits and billing
- Rate limits are per account, in requests per minute, set by your plan. The chat and the API share the same limit.
- Plan allowances are per model, in tokens per month, optionally with a daily token or request cap. They reset at 00:00 UTC on the first of the month (monthly) and every day (daily).
- On AUTO, a request runs on the plan if the estimated size fits the remaining allowance, otherwise on the wallet at the model's per-token rate.
- Wallet charges use the token counts reported by the provider. If a provider does not report usage we estimate conservatively and show the estimate in your usage history.
- Requests that fail before generation starts are free. Requests you cancel mid-stream are billed for the tokens generated.
- Every request appears in Dashboard, Usage with tokens, cost, billing source and request ID.
Something missing? Tell us what you are building and we will document it.