# Perplexity Gateway API: omitting \`max\_tokens\` intermittently 400s on all \`perplexity/\*\` models

**URL:** https://community.perplexity.ai/t/perplexity-gateway-api-omitting-max-tokens-intermittently-400s-on-all-perplexity-models/5754
**Category:** Bug Reports
**Tags:** api
**Created:** [August 8, 2026, 10:50am UTC](https://community.perplexity.ai/t/perplexity-gateway-api-omitting-max-tokens-intermittently-400s-on-all-perplexity-models/5754 "2026-08-08T10:50:10Z")
**Posts on this page:** 2
**Page:** 1

<div class="post-metadata">

### Author: ![kingsleyw](https://avatars.discourse-cdn.com/v4/letter/k/f6c823/32.png) [@kingsleyw](https://community.perplexity.ai/u/kingsleyw)
#### Post date: [August 8, 2026, 10:50am UTC](https://community.perplexity.ai/t/perplexity-gateway-api-omitting-max-tokens-intermittently-400s-on-all-perplexity-models/5754/1 "2026-08-08T10:50:10Z")

</div>

I’ve hit what looks like a default-injection gap in the Gateway’s OpenAI-compatible endpoint, affecting all three `perplexity/*` models. `max_tokens` is optional in the OpenAI Chat Completions spec, and the gateway defaults it correctly everywhere else, so this breaks any standard OpenAI client that doesn’t set it explicitly (I hit it through a GUI client that omits the field by default).

The important detail: **it’s intermittent**. Identical requests to the same model succeed and fail, which points at request routing rather than per-model config.

## Summary

`POST https://api.perplexity.ai/router/v1/chat/completions` without `max_tokens` returns HTTP 400:

```auto
{"error":{"code":null,"param":null,"type":"invalid_request_error",
  "message":"Anthropic Messages API requires `max_tokens`; set `SamplingParams.max_tokens` before routing to Anthropic"}}

```

## Reproduction

```auto
for i in $(seq 1 25); do
  curl -s -X POST 'https://api.perplexity.ai/router/v1/chat/completions' \
    -H "Authorization: Bearer $PERPLEXITY_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{"model":"perplexity/kimi-k3","messages":[{"role":"user","content":"hi"}]}' \
  | grep -q SamplingParams && echo -n F || echo -n .
done

```

A single request is not enough to see it on `kimi-k3` / `glm-5.2` — I initially sampled once per model and wrongly concluded `glm-5.2` was unaffected.

Adding `"max_tokens": 1024` makes the request succeed every time. Sending `"max_tokens": null` fails identically to omitting it.

`"max_completion_tokens": 1024` also succeeds, so both spellings are honoured — only the complete absence of an output limit fails. That narrows this to a missing default rather than a field-name mismatch.

Worth stressing that omitting the field is **valid** OpenAI Chat Completions usage: `max_tokens` is optional in the spec. I hit this with an off-the-shelf GUI client that attaches the field only to chats where the user has explicitly set it, and sends this on any new chat:

```auto
{"model":"perplexity/deepseek-v4-flash-0731",
 "messages":[{"role":"user","content":"hello"}],
 "stream":true,"stream_options":{"include_usage":true}}

```

Nothing about that request is malformed, and it works against the other 12 models.

## Scope

25 identical requests per model, with `GET /router/v1/models` enumerating all 15:

| Model | Failures (no `max_tokens`) |
| --- | --- |
| `perplexity/deepseek-v4-flash-0731` | **25/25** (100%) |
| `perplexity/kimi-k3` | **11/25** (44%) |
| `perplexity/glm-5.2` | **7/25** (28%) |
| `anthropic/claude-opus-5` | 0/25 |
| `openai/gpt-5.6-luna` | 0/25 |
| `anthropic/*`, `openai/*`, `google/*`, `xai/*` (single-shot sweep) | 0 failures |

Two observations that may help localise it:

- Only the `perplexity/*` namespace is affected. All `anthropic/*` models are clean, so this isn’t a general property of the Anthropic-compatible path.
- `kimi-k3`’s failures arrived as a run of **9 consecutive** 400s inside an otherwise healthy sample (`....FFFFFFFFF..F..F......`), rather than scattered independently. That looks like sticky routing to a subset of backends that miss the `max_tokens` default, with `deepseek-v4-flash-0731` apparently pinned to one.

The failure is independent of every other request parameter — it reproduces with and without `temperature`, `stream: true`, a `system` message, `tools`, and `response_format`. Only the presence of `max_tokens` matters.

## Two smaller issues in the same area

### 1. The error message points at the wrong provider

It says “routing to Anthropic” for models under the `perplexity/*` namespace. I assume these are served via an Anthropic-compatible backend internally, but from the outside it reads as though the wrong model was requested — I initially spent a while debugging the `anthropic/*` models, which turned out to be fine. It also leaks an internal symbol (`SamplingParams`). Something like “`max_tokens` is required for model `perplexity/kimi-k3`” would point users straight at the fix.

### 2. Silent empty responses when `max_tokens` is low

The `perplexity/*` models are reasoners, and the reasoning tokens are billed in `usage` but never surfaced — there’s no `reasoning_content` field in the response or in streamed deltas. When the budget is consumed by reasoning, the call returns **200 OK with `content: null`** and `finish_reason: "length"`.

A request lands in one of four states, all observed live, and there is no `max_tokens` value that eliminates the bad ones:

- `finish_reason: stop` with content — the good case
- `finish_reason: stop` with **`content: null`** — clean stop, nothing returned (deepseek at `max_tokens: 1024`, 104 completion tokens billed)
- `finish_reason: length` with `content: null` — budget consumed by reasoning
- `finish_reason: length` with **partial** content — truncated mid-sentence

Rates on `perplexity/deepseek-v4-flash-0731` at `max_tokens: 4096` — a generous budget for a two-sentence answer, where successful runs finish in 129–362 tokens — for the prompt “Explain the CAP theorem in two sentences.”:

| Model (at `max_tokens: 4096`) | Unusable responses |
| --- | --- |
| `perplexity/deepseek-v4-flash-0731` | **4/20** and **2/12** across two samples — full 4096 spent on reasoning, nothing returned |
| `perplexity/kimi-k3` | truncated _at_ 1024 (1024 tokens, 517 chars partial) |
| `perplexity/glm-5.2` | 0/12 |

Two consequences for clients:

- Neither `finish_reason` nor a larger budget is a reliable guard. Raising `max_tokens` past 4096 mainly raises the cost of each failure, since a runaway request is billed for the whole budget and returns nothing.
- The partial-content case is the most dangerous: a 200 OK carrying what looks like a complete answer.

Exposing reasoning token counts in `usage` (as `completion_tokens_details.reasoning_tokens`, per the OpenAI schema) would at least make this diagnosable, and a server-side cap on reasoning length would stop the runaway case outright.

## Environment

- Endpoint: `https://api.perplexity.ai/router/v1` (OpenAI-compatible)
- Observed via raw `curl` and the `openai` Python SDK 2.53.0
- Date: 2026-08-08

Happy to provide full request/response captures if useful.

---

<div class="post-metadata">

### Author: ![viktoriyanavrotskaya](https://avatars.discourse-cdn.com/v4/letter/v/59ef9b/32.png) [@viktoriyanavrotskaya](https://community.perplexity.ai/u/viktoriyanavrotskaya)
#### Post date: [August 10, 2026, 3:29pm UTC](https://community.perplexity.ai/t/perplexity-gateway-api-omitting-max-tokens-intermittently-400s-on-all-perplexity-models/5754/2 "2026-08-10T15:29:22Z")

</div>

Solid writeup. The sticky-routing theory fits the clustered failures well, that run of 9 consecutive 400s on kimi-k3 is pretty telling. The wrong-provider error message wasted a lot of my time too when I hit something [website](https://tropical-casino.com/)similar. Honestly the silent empty response at finish\_reason length is the scarier bug here, billing for reasoning tokens you never see feels rough.
