# GPT-6 Astra — /chat/completions

GPT-6 Astra chat API (OpenAI), the GPT-6 flagship: a 1,050,000-token context window, up to 128,000 output tokens per request, five thinking levels from low to max, image input, tool calls and JSON output. OpenAI SDK compatible, and also served on /v1/responses.

**Endpoint:** `POST https://nezhagate.com/v1/chat/completions`

## Quick start

Two steps: send a request, then stream the answer. Replace YOUR_API_KEY with your key and the commands below run as they are. The endpoint matches OpenAI Chat Completions, so existing OpenAI code only needs a new `base_url` and `model`.

**Step 1 · Send a request**

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-6-astra", "messages": [{"role": "user", "content": "Explain photosynthesis in one sentence"}]}'
```

Response (HTTP 200)

```
{
  "id": "chatcmpl-7f3c9a1e2b4d6f8a",
  "object": "chat.completion",
  "created": 1791281400,
  "model": "gpt-6-astra",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Photosynthesis is how plants use sunlight to turn carbon dioxide and water into sugar, releasing oxygen.",
        "reasoning_content": "..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 18,
    "completion_tokens": 186,
    "total_tokens": 204,
    "prompt_tokens_details": {"cached_tokens": 0},
    "completion_tokens_details": {"reasoning_tokens": 144}
  }
}
```

`message.content` is the answer and `message.reasoning_content` the thinking. `usage` holds the tokens this request is billed for: thinking tokens are part of `completion_tokens` and bill at the output price.

**Step 2 · Stream the answer (recommended)**

```
curl -N https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-6-astra", "stream": true, "messages": [{"role": "user", "content": "Explain photosynthesis in one sentence"}]}'
```

The stream is standard SSE: each chunk carries text in `choices[0].delta.content`, the last chunk carries `usage`, and it ends with `data: [DONE]`. Stream long answers and thinking models: a non-streaming request is cut off after about 10 minutes.

**Python (OpenAI SDK)**

```
from openai import OpenAI

client = OpenAI(base_url="https://nezhagate.com/v1", api_key="YOUR_API_KEY")

# 1. One request, one answer
resp = client.chat.completions.create(
    model="gpt-6-astra",
    messages=[{"role": "user", "content": "Explain photosynthesis in one sentence"}],
)
print(resp.choices[0].message.content)

# 2. Streaming
stream = client.chat.completions.create(
    model="gpt-6-astra",
    messages=[{"role": "user", "content": "Explain photosynthesis in one sentence"}],
    stream=True,
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)
```

## Authentication
```
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

## At a glance

| Context window | 1,050,000 tokens |
| --- | --- |
| Max output per request | 128,000 tokens |
| Input | Text, images |
| Thinking | Per `reasoning_effort` (medium when unset); the summary comes back in `reasoning_content` |
| Tool calling | Yes (OpenAI `tools` format) |
| JSON output | Yes (`response_format`) |
| Web search | Yes (`web_search: true`; the search itself is free) |
| Endpoints | /v1/chat/completions · /v1/responses |
| Streaming | Yes (`stream: true`, SSE) |
| Billing | Per token: input, output (thinking included) and cache hits each have a price; failures are free |

## Request parameters

The JSON body of `POST /v1/chat/completions`, the same as OpenAI Chat Completions. The common parameters, and what each one does on this model:

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| model | string | Yes | The model id: `gpt-6-astra`. |
| messages | array | Yes | The conversation: each message has a `role` (`system`, `user`, `assistant` or `tool`) and `content`. `content` is a string, or an array of `text` and `image_url` parts (image input, see below). For multi-turn chat, send the whole history in order. |
| stream | boolean | No | `true` streams the answer as SSE, with `usage` in the last chunk. Defaults to `false`. |
| max_tokens | integer | No | This model takes no output cap: `max_tokens` / `max_completion_tokens` are ignored (no error). Ask for a short answer in the prompt instead. |
| temperature / top_p | number | No | This model takes no sampling parameters; they are ignored (no error). |
| reasoning_effort | string | No | Thinking depth: low / medium / high / xhigh / max; medium by default. Higher levels think longer and more carefully, and take longer and spend more output tokens. The level can also go into the model id, such as `gpt-6-astra-high`. |
| tools / tool_choice | array / string | No | Function calling in the standard OpenAI format. When the model calls a function it returns `finish_reason: "tool_calls"` with `tool_calls`; see below. |
| response_format | object | No | `{"type": "json_object"}` makes the model return valid JSON only; also say in the prompt that you want JSON and which fields. |
| web_search | boolean | No | `true` runs a web search first and hands the results to the model as reference; see below. |

`frequency_penalty` and `presence_penalty` are ignored as well. Other OpenAI parameters are passed to the model as sent.

## Price

Billed per token at the live rates below (per million tokens). Only successful requests are billed, on actual usage: failures, timeouts and refusals are free. 1 credit = $0.005.

| Type | USD / 1M tokens | Credits / 1M tokens |
| --- | --- | --- |
| Input | $3.00 | 600 |
| Output (thinking included) | $15.00 | 3000 |
| Cache hit | $0.30 | 60 |

For example, a request with 2,000 input tokens and 1,000 output tokens (thinking included) costs about $0.021.

## Thinking

`reasoning_effort` takes low / medium / high / xhigh / max; unset, the gateway asks for medium. Higher levels think longer and answer more reliably, and take longer and spend more output tokens; low is enough for simple questions.

The thinking summary comes back in `message.reasoning_content` (`delta.reasoning_content` when streaming). The level can also go into the model id, `gpt-6-astra-low`, `gpt-6-astra-high` or `gpt-6-astra-xhigh`, with the same effect, billed as `gpt-6-astra`.

On /v1/responses the depth goes in `reasoning.effort`, and a summary comes back only when you set `reasoning.summary` yourself (the gateway does not add it).

Thinking tokens are part of `usage.completion_tokens` and bill at the output price (some models also break them out in `completion_tokens_details.reasoning_tokens`).

## Prompt caching

No parameter needed: a repeated long prefix (the same system prompt, the earlier turns of a conversation) may be cached by the model provider automatically, and the cached part bills at the lower cache price. Whether and how much is cached is up to the provider; keep the parts that never change at the start of the messages for the best hit rate.

The cached token count comes back in `usage.prompt_tokens_details.cached_tokens`; that part bills at the cache-hit price and the rest of the input at the input price.

## Image input

Put an `image_url` part in the `content` array. A base64 data URI (`data:image/png;base64,...`) is the recommended form and can always be read; a public image link has to be downloaded first, and occasionally it cannot be fetched or times out. Images count as input tokens by the model vendor rules.

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-6-astra", "messages": [{"role": "user", "content": [{"type": "text", "text": "What is in this image"}, {"type": "image_url", "image_url": {"url": "data:image/png;base64,iVBORw0KGgo..."}}]}]}'
```

## Tool calling (function calling)

Describe your functions in `tools` with JSON Schema. When the model decides to call one, it returns `finish_reason: "tool_calls"` with the function name and arguments (a JSON string) in `message.tool_calls`.

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-6-astra", "messages": [{"role": "user", "content": "What is the weather in Shanghai right now"}], "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Current weather of a city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}]}'
```

Run the function, append the result as a `role: "tool"` message with the matching `tool_call_id`, and send the conversation again; the model then gives its final answer. `tool_choice` takes `auto`, `none`, `required` or a named function.

## The Responses API (/v1/responses)

This model is also served on the OpenAI Responses API, the one Codex and similar tools use. Requests and responses follow the OpenAI Responses format: the input goes in `input` and the thinking depth in `reasoning.effort`.

```
curl https://nezhagate.com/v1/responses \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-6-astra", "input": "Explain photosynthesis in one sentence", "reasoning": {"effort": "medium", "summary": "auto"}}'
```

This endpoint adds no default thinking level and returns no summary on its own; send `"summary": "auto"` as above when you want one. Billing is the same as on the chat endpoint; web search is offered on /v1/chat/completions only.

## Web search

Add `"web_search": true` to the request (or a `{"type": "web_search"}` entry in `tools`): the gateway first searches the live web based on the conversation, hands up to 5 results with their links to the model as reference, and the model answers and cites its sources. The search itself is free; the reference text bills as input tokens.

```
curl https://nezhagate.com/v1/chat/completions \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "gpt-6-astra", "web_search": true, "messages": [{"role": "user", "content": "What are the main tech news stories today"}]}'
```

## Reliability and automatic retries

- When a request errors, times out or hits a capacity limit, the gateway retries it automatically before anything is returned, so you need not retry and are never billed twice.

- Once a stream starts sending, it is no longer retried automatically: if it breaks partway, you are billed for the usage produced up to that point (see [Billing and refunds](https://nezhagate.com/en/docs/guide/billing)).

- When the model is at capacity the gateway returns 429 with `Retry-After`; wait that many seconds and retry. Failed requests are not billed. Each model status is on [the status page](https://nezhagate.com/en/status).

## Errors and refunds

Errors come back as an `error` object (`message`, `type`, `code`, `param`); decide by the HTTP status and `code`, never by matching the text of `message`. Failed requests are not billed; the full list is in [Error codes](https://nezhagate.com/en/docs/guide/errors).

| HTTP | code | Cause | What to do |
| --- | --- | --- | --- |
| 400 | invalid_messages / invalid_role / invalid_max_tokens | A chat request's `messages` is missing, empty or malformed, a `role` is not one of the allowed values, or `max_tokens` is not a positive integer. `param` names the field. | Fix the field named in `param`. |
| 400 | model_not_found | The model id is misspelled, `model` is missing, or the model cannot be used on this endpoint (a chat model on the image endpoint, for example). | Use a model id from `GET /v1/models` or the [API reference](https://nezhagate.com/en/docs/api). |
| 400 | max_tokens_too_small | A reasoning model spent all of `max_tokens` thinking and wrote no answer (non-streaming chat). | Raise `max_tokens` and retry. |
| 4xx | invalid_request | The model provider rejected the request (an over-long context or an unsupported parameter, for example); the status and `message` are passed on as the provider sent them, and `code` is the provider's value. | Change the request as `message` says. |
| 401 | invalid_api_key | The key does not exist, was deleted or is disabled, or the account is disabled. | Check the key on the API Keys page of the console. |
| 402 | insufficient_quota | Not enough balance to hold this request. The gateway holds the highest possible cost of the request first, so a burst of concurrent calls can trigger it too. | After a burst, wait a few seconds and retry; otherwise top up. |
| 403 | permission_denied | The model is not on the key's model allowlist. | Use a model on the allowlist, or edit the allowlist in the console. |
| 429 | rate_limit_exceeded | Over the requests-per-minute limit set on this key (counted over the last 60 seconds). No `Retry-After` header. | Wait a few seconds and retry, or raise the key's limit in the console. |
| 429 | rate_limit_exceeded | The model is at capacity and the request was not sent. Chat endpoints only: image and video jobs are never refused but queue instead. The OpenAI-compatible endpoints add `Retry-After`. | Wait the seconds in `Retry-After`, then retry. |
| 502 | upstream_timeout | The upstream timed out, including taking too long to send the first token. | Retry; use `stream: true` for long answers. |
| 502 | upstream_unavailable | The upstream failed or is unavailable for the moment. | Retry shortly. |
| 502 | upstream_busy | The model provider is rate-limiting. A provider's 429 is never passed on as a 429; you get this 502 instead. | Retry shortly. |
| 502 | empty_completion | The model provider returned empty content. This is not a judgement of your content. | Retry. |
| 503 | model_maintenance | The model is under maintenance and takes no requests; no other model answers in its place meanwhile. | Retry when maintenance ends; see [Status](https://nezhagate.com/en/status). |

If a stream fails after it started sending, the status code stays as it was: the error arrives as a data line carrying `error`, or the stream just ends (without `data: [DONE]`). Treat it as interrupted.
