For developers
Your ideas. Your application.
Bring Winfield Research into your own workflow with a familiar chat API. The same assistant, the same prepaid balance, without a separate subscription.
Get connected
- Create an account, confirm your email, and sign in.
- In the workspace, open Settings → Account → API keys and create a key. Keep it private.
- Add credits to the account you will use for API requests.
The API is available over HTTPS at https://chat.winfieldresearch.com/api/v1. Authenticate with Authorization: Bearer YOUR_API_KEY. A website password or sign-in cookie is not an API key.
Store your key in a server-side environment variable or secret manager. Never put it in browser JavaScript, a mobile app bundle, a shared notebook, or a public repository. Creating a replacement key invalidates the previous key. Password resets also revoke API keys.
Your first request
Set WR_API_KEY in your environment, then send a chat completion. This example uses cURL; it works without a browser login or a browser challenge.
curl https://chat.winfieldresearch.com/api/v1/chat/completions \
-H "Authorization: Bearer $WR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "winfield-research-standard",
"messages": [{"role": "user", "content": "Explain how a hash table works."}],
"max_tokens": 512
}'Read the answer from choices[0].message.content. The response includes usage counts for input and output tokens.
Using a compatible Python client
Install the openai Python package. It is a client library for this API format, not a second service subscription.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://chat.winfieldresearch.com/api/v1",
api_key=os.environ["WR_API_KEY"],
timeout=120,
max_retries=0,
)
response = client.chat.completions.create(
model="winfield-research-standard",
messages=[{"role": "user", "content": "Explain how a hash table works."}],
max_tokens=512,
)
print(response.choices[0].message.content)Retries are disabled in the example because submitting a second generation can incur another usage charge. Requests are not idempotent generation jobs.
Choose a mode
GET /models lists three presets of the same assistant, ordered by thinking intensity. Focus is the default, including when you omit the model field:
| Model | Behaviour |
|---|---|
winfield-research-standard | Standard: quick rewrites, summaries and transformations without thinking, at temperature 0.7. |
winfield-research-creative | Creative: distinct practical ideas and expressive writing, with medium thinking, temperature 1.0 and top_k 20. |
winfield-research | Focus: the default for evidence-led research, coding and multi-step work, with temperature 0.7 and maximum thinking. |
All three retain the same tools and supported inputs. For consequential calculations or complex constraints, prefer Focus with tool-based verification. Creative encourages exploration, not greater factual accuracy; maximum thinking can take longer and still make mistakes.
Choose reasoning_effort: "low" for brief thinking, "medium" for a balanced setting, or "high" for the maximum supported effort. "xhigh" is also accepted as the maximum. Use "none" to turn thinking off. These settings guide the assistant; they do not guarantee a particular thinking length or answer quality.
The old winfield-research-focus and winfield-research-thinking identifiers remain aliases of Focus and Creative. Responses may identify the shared winfield-research model regardless of the preset. Thinking uses output tokens and is billed at the output rate. When retaining a reasoning trace between requests, send it in the assistant message's reasoning_content field. To disable retention of older reasoning, set chat_template_kwargs: {"preserve_thinking": false}.
Sampling controls include temperature, top_p, top_k, stop, and seed. All modes default to top_p: 0.95 with no fixed seed; Standard and Focus use top_k: 0. Explicit request settings override the defaults. The current serving backend does not support min_p or logit_bias. A seed is not a guarantee of reproducibility.
Stream longer answers
Set stream: true to receive Server-Sent Events. Streaming is recommended for long responses and thinking: it avoids waiting for the entire answer before the connection becomes active.
stream = client.chat.completions.create(
model="winfield-research",
messages=[{"role": "user", "content": "Compare three approaches to testing a web API."}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)The stream sends comment keepalives while the model is quiet. Custom clients should ignore SSE comment lines beginning with :, handle JSON error events, and stop at [DONE]. After streaming headers are sent, a later failure can arrive inside the stream rather than as a new HTTP status.
Closing the connection cancels the request where possible. It does not guarantee that no tokens were processed or charged. If final usage is unavailable after an interruption, reserved credit can remain held for review; contact support rather than repeatedly submitting the same request.
Include images
Use a content array containing text and an image_url item. The URL must be reachable by the service, or use a base64 data URL. Do not use an image URL that contains private credentials.
{
"model": "winfield-research-standard",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "Describe the layout of this diagram."},
{"type": "image_url", "image_url": {"url": "https://example.com/diagram.png"}}
]
}],
"max_tokens": 512
}Use images you are authorized to share. This chat API does not accept native audio or video uploads. For document extraction or video-frame inspection, use the workspace; video inspection does not include audio.
Function calling
Send function definitions in tools and select tool_choice: "auto", "none", "required", or a supported named function. When the model returns tool_calls, your application validates the arguments and executes the function. Add these fields to a complete chat request:
"tools": [{
"type": "function",
"function": {
"name": "get_project_status",
"description": "Read the status of a project the user can access.",
"parameters": {
"type": "object",
"properties": {"project_id": {"type": "string"}},
"required": ["project_id"]
}
}
}],
"tool_choice": "auto"Append the returned assistant message and a role: "tool" message with its matching tool_call_id, then send the next completion. Keep any system or developer instructions at the beginning of the message history, before user, assistant, and tool messages. Instructions inserted later are rejected; do not add a new system message after a tool result. Treat function arguments as untrusted input and enforce your own permissions before execution.
Function calling does not automatically run our workspace search, terminal, or coding tools. Those are interactive workspace features. This API does not provide arbitrary server-side tool execution, account administration, billing mutations, or an embeddings endpoint.
One balance. Clear usage.
API calls and workspace conversations draw from the same prepaid balance: $1.00 per million input tokens and $6.00 per million output tokens, in USD. There is no separate cached-input discount. Thinking output is included in output usage.
New customers receive a one-time $0.50 bonus after their first qualifying top-up. The minimum is $5 by card or $1 with crypto. Manage credits to top up or review account activity.
Input and output share a 262,144-token context window. max_tokens is an upper bound, not a promise that all requested tokens fit. The remaining context, supported model behaviour, and available credit determine the usable output budget. Keep requests below 95 MiB, including base64 expansion; uploads through the workspace must also fit the network's 100 MB request-body ceiling.
For long generations, use streaming and configure your client timeout accordingly. An uninterrupted request can run for up to 24 hours at the application layer; non-streaming requests can encounter shorter network timeouts. You may need to increase your client read timeout beyond the example's 120 seconds.
When a request fails
| Status | What to check |
|---|---|
| 400 / 422 | Request JSON, model ID, parameter values, and available context. |
| 401 | A missing, replaced, or revoked API key. Create a new key after a password reset. |
| 402 / 403 | Available credit or account access. Read the error message and visit Manage Credits. |
| 413 | Request size, including encoded images. |
| 429 | Too many requests. Follow any Retry-After header and back off. |
| 500 / 502 / 503 / 504 | A temporary service or network problem. Do not assume that a disconnected generation used no tokens. |
Save the X-Request-ID response header and, when present, the network's CF-Ray header. Send those identifiers and the time of the request to [email protected]. Do not send your API key or private prompt content.
API requests and tool integrations are subject to our Terms and Data Handling guide. This is not a zero-retention service.