# Gemini native (https://hypit.ai/api-reference/gemini/)

> Google's own wire format, passed through unchanged — for code already written against the GenAI SDK.

Some projects are written against Google's native API rather than the OpenAI shape: `contents`
instead of `messages`, `parts` instead of `content`, `usageMetadata` instead of `usage`. Rewriting
that to fit an OpenAI client is work you should not have to do, so hypit.ai serves the native format
directly.

```text
POST https://hypit.ai/v1beta/models/{model}:{action}
```

Note this is `/v1beta`, **not** `/v1`. The body is forwarded to Google byte for byte and the
response comes back untouched.

## Actions [#actions]

Exactly three are supported:

| Action                  | Behaviour                                                         |
| ----------------------- | ----------------------------------------------------------------- |
| `generateContent`       | buffered JSON response                                            |
| `streamGenerateContent` | SSE by default; `?alt=json` returns Google's JSON array of frames |
| `countTokens`           | buffered, and **not billed**                                      |

Anything else — `embedContent`, `batchEmbedContents`, `predict`, file uploads — is
`404 unsupported_action`. Only `POST` is routed; a `GET` to this path is a plain `404`.

```bash
curl "https://hypit.ai/v1beta/models/gemini-3.1-pro:generateContent" \
  -H "x-goog-api-key: $HYPIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contents": [{"parts": [{"text": "What is Hypit? One sentence."}]}]
  }'
```

Streaming:

```bash
curl -N "https://hypit.ai/v1beta/models/gemini-3.1-pro:streamGenerateContent?alt=sse" \
  -H "x-goog-api-key: $HYPIT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"contents": [{"parts": [{"text": "Write a haiku about a paper boat."}]}]}'
```

`streamGenerateContent` with no `alt` parameter defaults to `alt=sse`, matching Google. There is
**no `[DONE]` sentinel** on this surface — Google does not send one, and adding it would make the
official SDK try to decode it as a `GenerateContentResponse`.

## Authentication [#authentication]

The same hypit.ai `sk-hh-` key as everywhere else, in any of the places a Google client puts it:

```bash
-H "x-goog-api-key: $HYPIT_API_KEY"      # what the GenAI SDKs send
-H "Authorization: Bearer $HYPIT_API_KEY"
"…:generateContent?key=$HYPIT_API_KEY"   # the REST spelling
```

Whichever you use, the credential is stripped from the query string before we forward the request —
`key`, `api_key`, `apikey` and `access_token` are removed. Every other query parameter is passed
through.

## Pointing the Google SDKs here [#pointing-the-google-sdks-here]

```python
import os
from google import genai

client = genai.Client(
    api_key=os.environ["HYPIT_API_KEY"],
    http_options={"base_url": "https://hypit.ai"},
)

response = client.models.generate_content(
    model="gemini-3.1-pro",
    contents="What is Hypit? One sentence.",
)
print(response.text)
```

```js
import { GoogleGenAI } from "@google/genai";

const ai = new GoogleGenAI({
  apiKey: process.env.HYPIT_API_KEY,
  httpOptions: { baseUrl: "https://hypit.ai" },
});

const response = await ai.models.generateContent({
  model: "gemini-3.1-pro",
  contents: "What is Hypit? One sentence.",
});
console.log(response.text);
```

The base URL here is the **origin**, without `/v1beta` — the SDK appends the version segment itself.

## Model names with a colon [#model-names-with-a-colon]

Google's URLs put the action after a literal colon inside the last path segment. We split on the
**last** colon, so a model name that itself contains one still resolves:

```text
/v1beta/models/tunedModels/my-tune:generateContent   →  model "tunedModels/my-tune"
```

A percent-encoded `%3A` is decoded when no literal colon is present.

## Which models [#which-models]

A model is reachable here when its card lists `gemini` in `endpoints`:

```bash
curl -s https://hypit.ai/v1/models \
  -H "Authorization: Bearer $HYPIT_API_KEY" |
  jq -r '.data[] | select(.endpoints[] == "gemini") | .id'
```

Many of these models are reachable on `/v1/chat/completions` too — the same model card usually lists
both `chat` and `gemini`. Pick whichever wire format your code already speaks; billing is identical.

## Errors [#errors]

<Callout type="warn" title="Errors come back in the OpenAI envelope, not Google's">
  Google returns `{"error": {"code": 400, "message": "…", "status": "INVALID_ARGUMENT"}}`. hypit.ai
  returns its own shape on every surface, including this one:

  ```json
  {
    "error": {
      "message": "action \"embedContent\" is not supported; use generateContent, streamGenerateContent or countTokens",
      "type": "not_found_error",
      "code": "unsupported_action",
      "param": null
    }
  }
  ```

  A Google SDK will see an unfamiliar error body. Handle non-2xx responses yourself rather than relying
  on the SDK's error parsing.
</Callout>

| Status | `code`               | Meaning                                           |
| ------ | -------------------- | ------------------------------------------------- |
| `404`  | `unknown_route`      | the path is not `/v1beta/models/{model}:{action}` |
| `404`  | `unsupported_action` | not one of the three actions above                |
| `400`  | `bad_request_body`   | a body was sent that is not a JSON object         |
| `413`  | `request_too_large`  | over 32 MiB                                       |
| `502`  | `empty_stream`       | the upstream produced no frames                   |

Everything in [Errors and limits](/api-reference/errors) applies here too — this route sits behind
the same authentication, rate limiting and credit gate as `/v1`.