Base URL
Point any OpenAI SDK's baseURL at one of these. The path prefix (/openai/v1) is part of the base URL.
| Environment | Base URL |
|---|---|
| Hosted (free, zero install) | https://api.llm-mock.dev/openai/v1 |
| Local (source or Docker) | http://localhost:3000/openai/v1 |
import OpenAI from "openai";
const client = new OpenAI({
baseURL: "https://api.llm-mock.dev/openai/v1", // or http://localhost:3000/openai/v1
apiKey: "sk-mock-key-01",
});
Authentication
Requests authenticate with a bearer token in the Authorization header, exactly like the real API. Keys are validated against a closed set, so you can test both the happy path and the failure path.
| Key | Result |
|---|---|
sk-mock-key-01 … sk-mock-key-10 | ✅ Valid — ship in the repo (and on the hosted instance) |
any other key (e.g. sk-mock-invalid) | 401 — real OpenAI invalid_api_key envelope |
Run your own instance to use a custom key set — point LLM_MOCK_API_KEYS_FILE at your own JSON array of keys.
Endpoints
OpenAI is the provider implemented today; these are its endpoints under the /openai prefix.
| Method | Path | Notes |
|---|---|---|
| POST | /openai/v1/chat/completions | Full chat.completion object, n choices, SSE streaming, stream_options.include_usage |
| POST | /openai/v1/responses | Full response object, typed SSE event stream |
| GET | /openai/v1/responses/{id} | Stateless: synthesizes a deterministic response for any id |
| DELETE | /openai/v1/responses/{id} | Idempotent; returns the OpenAI deletion object |
| GET | /openai/v1/models | Simulated catalog (gpt-4.1, gpt-4o, gpt-4o-mini, text-embedding-3-*, …) |
| GET | /openai/v1/models/{model} | 404 in OpenAI error format for unknown models |
| POST | /openai/v1/embeddings | Deterministic unit vectors, correct dimension per model, dimensions param, float and base64 encoding |
| GET | /health | Healthcheck (mock-only, outside any provider contract) |
temperature, top_p, tools, response_format, …)
are accepted without error, because real SDK clients send them.
Chat completions
POST /openai/v1/chat/completions — returns a full chat.completion object. By default the assistant echoes the last user message ("Echo: <your prompt>").
curl https://api.llm-mock.dev/openai/v1/chat/completions \
-H "Authorization: Bearer sk-mock-key-01" \
-H "Content-Type: application/json" \
-d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "ping"}]}'
Supports multiple choices via n, streaming via stream: true, and usage on the final stream chunk via stream_options.include_usage.
Responses API
The newer /responses surface is fully modelled, including the typed SSE event stream.
| Method | Path | Behaviour |
|---|---|---|
| POST | /openai/v1/responses | Returns a full response object; with stream: true emits typed events (response.created, response.output_text.delta, response.completed, …) |
| GET | /openai/v1/responses/{id} | Stateless — synthesizes a deterministic response for any id, no store required |
| DELETE | /openai/v1/responses/{id} | Idempotent; returns the standard OpenAI deletion object |
Models
A simulated catalog so model-discovery code paths work end to end.
| Method | Path | Behaviour |
|---|---|---|
| GET | /openai/v1/models | Lists gpt-4.1, gpt-4o, gpt-4o-mini, text-embedding-3-small, text-embedding-3-large, … |
| GET | /openai/v1/models/{model} | Returns the model object, or 404 in OpenAI error format for unknown models |
Embeddings
POST /openai/v1/embeddings — returns deterministic, hash-seeded unit vectors with the correct dimension per model. Honours the dimensions param and both float and base64 encodings.
curl https://api.llm-mock.dev/openai/v1/embeddings \
-H "Authorization: Bearer sk-mock-key-01" \
-H "Content-Type: application/json" \
-d '{"model": "text-embedding-3-small", "input": "hello"}'
Controlling replies
By default every completion echoes the last user message. When a test needs a specific reply, the request itself carries it in a header — nothing to register beforehand, nothing to clean up.
| Header | Purpose |
|---|---|
x-llm-mock-response | ASCII reply to return verbatim as the assistant message |
x-llm-mock-response-base64 | Base64-encoded reply for content beyond ASCII (UTF-8). Wins when both headers are present. |
const completion = await client.chat.completions.create(
{ model: "gpt-4o", messages: [{ role: "user", content: "What's the weather like?" }] },
{ headers: { "x-llm-mock-response": "It is sunny in Valencia." } },
);
// completion.choices[0].message.content === "It is sunny in Valencia."
Streaming (SSE)
stream: true works on both chat completions and the Responses API, emitting the real Server-Sent Events stream your SDK already knows how to consume.
- Chat completions — delta chunks terminated by
data: [DONE]. Addstream_options.include_usagefor a final usage chunk. - Responses API — typed events:
response.created,response.output_text.delta,response.completed, …
const stream = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: "Hello!" }],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content ?? "");
}
Errors
Failures return the exact OpenAI error envelope, so you can test your error handling too. Invalid keys, unknown models and validation errors all follow this shape:
{
"error": {
"message": "Incorrect API key provided: sk-moc****alid. ...",
"type": "invalid_request_error",
"param": null,
"code": "invalid_api_key"
}
}
Provider support
Multi-provider by design: each provider mounts under its own URL prefix and implements its own API contract. Here's where each one stands today.
| Provider | Prefix | Status |
|---|---|---|
| OpenAI | /openai/v1 | ✅ Supported |
| Anthropic | /anthropic | 🔜 Planned |
| Gemini (AI Studio) | /gemini | 🔜 Planned |
| Gemini Enterprise (Vertex AI) | — | 🔜 Planned |
| Azure OpenAI | — | 🔜 Planned |
Want a provider prioritized? Open an issue — or a PR.