Legacy Completions
Endpoint
Section titled “Endpoint”POST https://api.bve.me/v1/completionsRequires Authorization: Bearer sk-bve-YOUR_KEY.
Limitations
Section titled “Limitations”best_ofandlogprobsare not supported by the emulation layer and return400 unsupported_parameter.- Array prompts are joined with
\n\nbefore being sent as a single user message. - The emulated
idis derived from the chat completion id (chatcmpl-→cmpl-).
Request body
Section titled “Request body”{ "model": "gpt-4o", "prompt": "The capital of France is", "max_tokens": 10}| Field | Type | Required | Description |
|---|---|---|---|
model | string | Yes | Model ID |
prompt | string | string[] | No | Prompt text. Arrays are joined with \n\n. |
max_tokens | integer | No | Max tokens to generate |
temperature | number | No | Sampling temperature (0–2) |
top_p | number | No | Nucleus sampling (0–1) |
n | integer | No | Number of completion choices to generate; must be ≥ 1 |
best_of | integer | No | Must be ≥ 1; always rejected — not supported by the emulation layer (returns 400 unsupported_parameter) |
echo | boolean | No | Prepend the normalized prompt text to each returned choice |
logprobs | integer | No | Top-N log probabilities; must be ≥ 0. Always rejected — not supported by the emulation layer (returns 400 unsupported_parameter) |
stop | string | string[] | No | Up to 4 stop sequences |
presence_penalty | number | No | Presence penalty (−2 to 2) |
frequency_penalty | number | No | Frequency penalty (−2 to 2) |
user | string | No | End-user identifier (max 256 chars) |
seed | integer | No | Deterministic seed |
stream | boolean | No | Enable streaming SSE mode. Chunks use object: "text_completion" format (same as non-streaming but delivered as SSE). |
Response
Section titled “Response”{ "id": "cmpl-abc123", "object": "text_completion", "created": 1716288000, "model": "gpt-4o", "choices": [ { "text": " Paris", "index": 0, "logprobs": null, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 7, "completion_tokens": 2, "total_tokens": 9 }}Response headers
Section titled “Response headers”BVE Gateway adds the following headers to every /v1/completions response:
| Header | Example | Description |
|---|---|---|
X-Request-Id | 550e8400-… | Server-generated UUID for this request |
X-BVE-Client-Id | my-trace-123 | Echo of the client-supplied X-Request-Id (when present and valid: alphanumeric + -_., ≤ 128 chars). Absent when the client did not send X-Request-Id or the value failed validation. |
X-BVE-Latency | 87 | Total gateway latency in milliseconds |
X-BVE-Model | gpt-4o | Model ID for this request |
X-BVE-Key-Name | prod-key | Display name of the authenticated key |
The standard X-RateLimit-* per-key headers (requests per minute, per day, monthly caps, token limits) are also present. See Rate Limits & Quotas for the full table and example output.
cURL example
Section titled “cURL example”curl https://api.bve.me/v1/completions \ -H "Authorization: Bearer sk-bve-YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "gpt-4o", "prompt": "The capital of France is", "max_tokens": 5 }'SDK example
Section titled “SDK example”import OpenAI from 'openai';
const client = new OpenAI({ apiKey: 'sk-bve-YOUR_KEY', baseURL: 'https://api.bve.me/v1',});
const completion = await client.completions.create({ model: 'gpt-4o', prompt: 'The capital of France is', max_tokens: 5,});
console.log(completion.choices[0].text);from openai import OpenAI
client = OpenAI( api_key="sk-bve-YOUR_KEY", base_url="https://api.bve.me/v1",)
completion = client.completions.create( model="gpt-4o", prompt="The capital of France is", max_tokens=5,)
print(completion.choices[0].text)Array prompts
Section titled “Array prompts”Array prompts are joined with \n\n before being forwarded as a single user message.
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: 'sk-bve-YOUR_KEY', baseURL: 'https://api.bve.me/v1',});
const completion = await client.completions.create({ model: 'gpt-4o', prompt: ['Translate to French:', 'Hello, world!'], max_tokens: 20,});
console.log(completion.choices[0].text);from openai import OpenAI
client = OpenAI( api_key="sk-bve-YOUR_KEY", base_url="https://api.bve.me/v1",)
completion = client.completions.create( model="gpt-4o", prompt=["Translate to French:", "Hello, world!"], max_tokens=20,)
print(completion.choices[0].text)Echo mode
Section titled “Echo mode”When echo: true is set, the normalized prompt text is prepended to each choice’s text field.
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: 'sk-bve-YOUR_KEY', baseURL: 'https://api.bve.me/v1',});
const completion = await client.completions.create({ model: 'gpt-4o', prompt: 'The capital of France is', max_tokens: 5, echo: true,});
// text includes the prompt: "The capital of France is Paris"console.log(completion.choices[0].text);from openai import OpenAI
client = OpenAI( api_key="sk-bve-YOUR_KEY", base_url="https://api.bve.me/v1",)
completion = client.completions.create( model="gpt-4o", prompt="The capital of France is", max_tokens=5, echo=True,)
# text includes the prompt: "The capital of France is Paris"print(completion.choices[0].text)Streaming
Section titled “Streaming”Streaming is supported. Set "stream": true and BVE Gateway will transform the upstream chat.completion.chunk SSE events into text_completion SSE events in real time.
curl https://api.bve.me/v1/completions \ -H "Authorization: Bearer sk-bve-YOUR_KEY" \ -H "Content-Type: application/json" \ -d '{"model": "gpt-4o", "prompt": "The capital of France is", "max_tokens": 10, "stream": true}'Each SSE chunk uses object: "text_completion" with a choices[].text field instead of choices[].delta.content:
data: {"id":"cmpl-abc","object":"text_completion","created":1716288000,"model":"gpt-4o","choices":[{"text":" Paris","index":0,"logprobs":null,"finish_reason":null}]}
data: {"id":"cmpl-abc","object":"text_completion","created":1716288000,"model":"gpt-4o","choices":[{"text":"","index":0,"logprobs":null,"finish_reason":"stop"}]}
data: [DONE]import OpenAI from 'openai';
const client = new OpenAI({ apiKey: 'sk-bve-YOUR_KEY', baseURL: 'https://api.bve.me/v1',});
const stream = await client.completions.create({ model: 'gpt-4o', prompt: 'The capital of France is', max_tokens: 10, stream: true,});
for await (const chunk of stream) { process.stdout.write(chunk.choices[0]?.text ?? '');}from openai import OpenAI
client = OpenAI( api_key="sk-bve-YOUR_KEY", base_url="https://api.bve.me/v1",)
stream = client.completions.create( model="gpt-4o", prompt="The capital of France is", max_tokens=10, stream=True,)
for chunk in stream: print(chunk.choices[0].text or "", end="", flush=True)Gateway validation
Section titled “Gateway validation”The gateway validates required fields before forwarding to the emulation path.
Required fields
Section titled “Required fields”| Missing / invalid | Code | param |
|---|---|---|
model absent | missing_required_parameter | "model" |
model not a string | invalid_type | "model" |
prompt not a string or array | invalid_type | "prompt" |
stream not a boolean | invalid_type | "stream" |
Unsupported parameters
Section titled “Unsupported parameters”| Condition | Code | param |
|---|---|---|
best_of not a positive integer | invalid_value | "best_of" |
best_of set (any valid integer) | unsupported_parameter | "best_of" |
logprobs not a non-negative integer | invalid_value | "logprobs" |
logprobs set (any valid integer) | unsupported_parameter | "logprobs" |
Optional field constraints
Section titled “Optional field constraints”| Parameter | Invalid condition | Code | param |
|---|---|---|---|
temperature | Not a number | invalid_type | "temperature" |
temperature | Outside [0, 2] | invalid_value | "temperature" |
top_p | Not a number | invalid_type | "top_p" |
top_p | Outside [0, 1] | invalid_value | "top_p" |
max_tokens | Not a number (e.g. a string) | invalid_type | "max_tokens" |
max_tokens | Not a positive integer (float or ≤ 0) | invalid_value | "max_tokens" |
n | Not a number (e.g. a string) | invalid_type | "n" |
n | Not a positive integer (float or ≤ 0) | invalid_value | "n" |
echo | Not a boolean | invalid_type | "echo" |
presence_penalty | Not a number | invalid_type | "presence_penalty" |
presence_penalty | Outside [-2, 2] | invalid_value | "presence_penalty" |
frequency_penalty | Not a number | invalid_type | "frequency_penalty" |
frequency_penalty | Outside [-2, 2] | invalid_value | "frequency_penalty" |
stop | Not a string or array | invalid_type | "stop" |
stop | Array with more than 4 elements | invalid_value | "stop" |
stop[N] | Not a string | invalid_type | "stop[N]" |
user | Not a string | invalid_type | "user" |
user | Longer than 256 characters | invalid_value | "user" |
seed | Not a number (e.g. a string) | invalid_type | "seed" |
seed | A float (e.g. 1.5) | invalid_value | "seed" |
All 400 responses use the standard error envelope:
{ "error": { "message": "temperature must be between 0 and 2", "type": "invalid_request_error", "param": "temperature", "code": "invalid_value" }}