> ## Documentation Index
> Fetch the complete documentation index at: https://docs.asksurf.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# POST /v1/chat/completions

> OpenAI-compatible Chat Completions API (with field-level annotations and streaming SSE details)

## Overview

| Item                         | Value                             |
| ---------------------------- | --------------------------------- |
| **Method**                   | `POST`                            |
| **Path**                     | `/v1/chat/completions`            |
| **Authentication**           | `Authorization: Bearer <API_KEY>` |
| **Content-Type**             | `application/json`                |
| **Response (non-streaming)** | `application/json`                |
| **Response (streaming)**     | `text/event-stream` (SSE)         |

## Quick Start

Send a request with `model` and `messages`. Use `stream=true` to receive SSE chunks.

> **Supported models**: `surf-ask`, `surf-research`, `surf-1.5`, `surf-1.5-instant`, `surf-1.5-thinking`.

> `surf-ask` and `surf-research` are **legacy** models. `surf-1.5`, `surf-1.5-instant`, and `surf-1.5-thinking` are the **new** models. Legacy models remain available and the request format is unchanged.

> **Note**: When using `surf-research` and `surf-1.5`, it is recommended to set the timeout to 10 minutes.

### What’s new in `surf-1.5`

`surf-1.5` is the recommended, next-generation model. Compared to legacy models, it’s designed for more advanced workflows:

* **Enhanced performance**: Delivers superior response quality with significantly reduced latency in `surf-1.5-instant`, providing faster and more accurate results compared to legacy models.
* **Custom tool calls (function calling)**: Better support for defining tools via `tools` and orchestrating multi-step tool-augmented tasks.
* **Configurable reasoning depth**: Use `reasoning_effort` (`low` / `medium` / `high`) to trade off speed vs. deeper analysis.
* **Built for agent-style workflows**: Works well with Surf extensions like `ability` (capability constraints) and `citation` (citation formats) in the same request shape.

### Model variants: `surf-1.5`, `surf-1.5-instant`, `surf-1.5-thinking`

The `surf-1.5` family includes three model variants with different reasoning capabilities:

* **`surf-1.5-instant`**: A lightweight model optimized for fast responses to simple queries.

* **`surf-1.5-thinking`**: A more powerful model with deeper reasoning capabilities, designed to handle complex problems that require thorough analysis and multi-step reasoning.

* **`surf-1.5`**: An adaptive model that automatically selects between `surf-1.5-instant` and `surf-1.5-thinking` based on the request parameters and problem complexity. This provides an optimal balance between speed and depth without requiring manual model selection.

### Summary (What you can do)

* **OpenAI-compatible**: Use the OpenAI Chat Completions shape (`model`, `messages`, optional `stream`) and standard `Authorization: Bearer <API_KEY>`.
* **Streaming (SSE)**: Set `stream=true` to receive incremental chunks (`text/event-stream`) and terminate on `data: [DONE]`.
* **Custom tool calls**: Provide `tools` (OpenAI function calling). The model may request tool executions during generation.
* **Reasoning depth**: Control analysis strength with `reasoning_effort`: `low` / `medium` / `high`.
* **Surf extensions**: Use `ability` to constrain available capability domains and `citation` to request output citation formats.
* **Errors**: This endpoint may return `400`, `401`, or `502` (both streaming and non-streaming).

### Examples

<CodeGroup>
  ```jsonc theme={null}
  {
    "model": "surf-ask", // Required: model identifier. Options: surf-ask / surf-research / surf-1.5 / surf-1.5-instant / surf-1.5-thinking
    "messages": [ // Required: list of chat messages (recommended: at least one role=user message)
      {
        "role": "system", // Required: system / user / assistant
        "content": "You are Surf, an analysis assistant focused on crypto markets and on-chain data." // Required: message content
      },
      {
        "role": "user", // Required: user input
        "content": "Summarize today's BTC market conditions and list the key drivers." // Required: your question/task
      }
    ],
    "stream": false, // Optional: enable streaming output. true -> SSE(text/event-stream), false -> JSON
    "reasoning_effort": "medium", // Optional: reasoning strength low / medium / high
    "ability": [ // Optional: Surf extension: capability-domain constraints/hints
      "evm_onchain", // search / evm_onchain / solana_onchain / market_analysis / calculate
      "market_analysis"
    ],
    "citation": [ // Optional: Surf extension: citation formats to include
      "source", // source / chart
      "chart"
    ],
    "tools": [ // Optional: tool definitions (OpenAI tools/function calling)
      {
        "type": "function", // Required: currently fixed to function
        "function": { // Required: function tool definition
          "name": "calculate_portfolio_value", // Required: tool name (function name)
          "description": "Calculate total portfolio value", // Optional: tool purpose
          "parameters": { // Optional: parameters JSON Schema
            "type": "object",
            "properties": {
              "symbols": {
                "type": "array",
                "items": { "type": "string" }
              }
            },
            "required": ["symbols"]
          }
        }
      }
    ]
  }
  ```

  ```bash theme={null}
  curl -X POST "https://api.asksurf.ai/gateway/v1/chat/completions" \
    -H "Authorization: Bearer $SURF_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "model": "surf-ask",
      "messages": [
        { "role": "system", "content": "You are Surf, an analysis assistant focused on crypto markets and on-chain data." },
        { "role": "user", "content": "Summarize today's BTC market conditions and list the key drivers." }
      ],
      "stream": false
    }'
  ```

  ```python theme={null}
  import requests

  url = "https://api.asksurf.ai/gateway/v1/chat/completions"
  headers = {
      "Authorization": f"Bearer {SURF_API_KEY}",
      "Content-Type": "application/json",
  }
  payload = {
      "model": "surf-ask",
      "messages": [
          {"role": "user", "content": "Summarize BTC price action over the past 24 hours in three bullet points."},
      ],
      "stream": False,
  }

  resp = requests.post(url, headers=headers, json=payload, timeout=60)  # For surf-research and surf-1.5, use timeout=600 (10 minutes)
  resp.raise_for_status()
  print(resp.json())
  ```

  ```javascript theme={null}
  const url = "https://api.asksurf.ai/gateway/v1/chat/completions";
  const resp = await fetch(url, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.SURF_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "surf-ask",
      messages: [{ role: "user", content: "Explain what it usually means when BTC open interest rises while price trades sideways." }],
      stream: false,
    }),
  });

  console.log(await resp.json());
  ```
</CodeGroup>

## Non-streaming response example (with field annotations)

```jsonc theme={null}
{
  "id": "chatcmpl-abc123", // Unique identifier for this completion
  "object": "chat.completion", // Object type (non-streaming responses are typically chat.completion)
  "created": 1699890366, // Creation timestamp (seconds)
  "model": "surf-ask", // Model identifier actually used
  "choices": [ // List of generated results (usually only 1)
    {
      "index": 0, // Choice index
      "finish_reason": "stop", // Finish reason: stop/length/tool_calls/content_filter/error (may also be null)
      "message": { // Final message (assistant)
        "role": "assistant", // Role (typically assistant)
        "content": "BTC is trading flat in the last 24h with rising open interest.", // Model output text
        "reasoning": "Internal chain-of-thought or brief rationale" // (If returned) reasoning/rationale field
      }
    }
  ],
  "usage": { // Token usage statistics
    "prompt_tokens": 23, // Input tokens
    "completion_tokens": 12, // Output tokens
    "total_tokens": 35 // Total tokens (input + output)
  }
}
```

## Request fields (`model.CompletionsRequest`)

> Note: This endpoint follows the overall structure of OpenAI `chat.completions`, and additionally provides Surf extension fields such as `ability` and `citation`.

| Field              | Type                  | Required | Description                                                                                                         | Example                                                                                                                                                                                  |
| ------------------ | --------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model`            | string (enum)         | Yes      | Model identifier to use.                                                                                            | `"surf-ask"` / `"surf-research"` / `"surf-1.5"` / `"surf-1.5-instant"` / `"surf-1.5-thinking"`                                                                                           |
| `messages`         | array\<object>        | Yes      | List of chat messages.                                                                                              | Minimal: `[{"role":"user","content":"Hello!"}]` · With system: `[{"role":"system","content":"You are a helpful assistant."},{"role":"user","content":"Hello!"}]`                         |
| `stream`           | boolean               | No       | Enable streaming output.                                                                                            | `true` → SSE (`text/event-stream`) · `false` (default) → JSON                                                                                                                            |
| `reasoning_effort` | string (enum)         | No       | Reasoning strength.                                                                                                 | `"low"` / `"medium"` / `"high"`                                                                                                                                                          |
| `ability`          | array\<string> (enum) | No       | Surf extension: hints/constraints for which capability domains are available for this request.                      | `["search"]` / `["evm_onchain"]` / `["solana_onchain"]` / `["market_analysis"]` / `["calculate"]`                                                                                        |
| `citation`         | array\<string> (enum) | No       | Surf extension: citation formats to include in the output.                                                          | `["source"]` / `["chart"]`                                                                                                                                                               |
| `tools`            | array\<object>        | No       | Tool definitions compatible with OpenAI tools/function calling. The model may request tool calls during generation. | `[{"type":"function","function":{"name":"get_weather","description":"Get current weather","parameters":{"type":"object","properties":{"city":{"type":"string"}},"required":["city"]}}}]` |

### Single message in `messages[]` (`model.Message`)

| Field     | Type          | Required | Description (Notes)                                                                                                                                                        |
| --------- | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `role`    | string (enum) | Yes      | Message role: `system` (system instructions) / `user` (user input) / `assistant` (model output).                                                                           |
| `content` | string        | Yes      | Message text content. `system` is used for rules/boundaries; `user` is used for questions/tasks; you typically do not need to include `assistant` messages in the request. |

### Tool definitions in `tools[]` (`model.Tool` / `model.ToolFunction`)

| Field      | Type          | Required | Description (Notes)              |
| ---------- | ------------- | -------- | -------------------------------- |
| `type`     | string (enum) | Yes      | Tool type. Currently `function`. |
| `function` | object        | Yes      | Function tool definition.        |

Fields of the `function` object:

| Field         | Type                 | Required | Description (Notes)                                                       |
| ------------- | -------------------- | -------- | ------------------------------------------------------------------------- |
| `name`        | string               | Yes      | Tool name (function name). Prefer `snake_case`.                           |
| `description` | string               | No       | Tool purpose description, to help the model decide whether to call it.    |
| `parameters`  | object (JSON Schema) | No       | JSON Schema describing tool parameters (structure/types/required fields). |

## Response fields (non-streaming JSON: `model.CompletionsProxyResponse`)

| Field     | Type           | Description (Notes)                                           |
| --------- | -------------- | ------------------------------------------------------------- |
| `id`      | string         | Unique identifier for this completion (e.g., `chatcmpl-...`). |
| `object`  | string         | Object type, typically `chat.completion`.                     |
| `created` | integer        | Creation timestamp (seconds).                                 |
| `model`   | string         | Model identifier actually used.                               |
| `choices` | array\<object> | List of generated results (usually only 1).                   |
| `usage`   | object         | Token usage statistics.                                       |

### `choices[]` (`model.CompletionsChoice`)

| Field           | Type           | Description (Notes)                                                                                                                                                                                         |
| --------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `index`         | integer        | Choice index (starting from 0).                                                                                                                                                                             |
| `finish_reason` | string \| null | Finish reason. Common values: `stop` (normal completion), `length` (reached token limit), `tool_calls` (triggered tool calls), `content_filter` (blocked by safety policy), `error` (aborted due to error). |
| `message`       | object         | Final message (`assistant`).                                                                                                                                                                                |

### `message` (`model.CompletionsMessage`)

| Field       | Type   | Description (Notes)                                                                                                                 |
| ----------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------- |
| `role`      | string | Role, typically `assistant`.                                                                                                        |
| `content`   | string | Final model output text.                                                                                                            |
| `reasoning` | string | (If returned) field used to expose model reasoning/rationale. Note: depending on product policy, this may be omitted or simplified. |

### `usage` (`model.CompletionsUsage`)

| Field               | Type    | Description (Notes)            |
| ------------------- | ------- | ------------------------------ |
| `prompt_tokens`     | integer | Input tokens.                  |
| `completion_tokens` | integer | Output tokens.                 |
| `total_tokens`      | integer | Total tokens (input + output). |

## Streaming response (SSE: `text/event-stream`)

When `stream=true`, the server continuously streams SSE events. Each event block typically looks like:

```text theme={null}
data: { ...json... }

```

The termination event is:

```text theme={null}
data: [DONE]

```

> Note: The current OpenAPI spec does not define a dedicated schema for streaming chunks. In the examples, each chunk's `object` is typically `chat.completion.chunk`, and incremental output is delivered via `choices[].delta` (e.g., `role` / `content`). `finish_reason` is usually `null` until the stream ends.

## Error response (`model.BaseResponse`)

This endpoint may return: `400`, `401`, `502` (both streaming and non-streaming).

| Field        | Type    | Description (Notes)             |
| ------------ | ------- | ------------------------------- |
| `success`    | boolean | Whether the request succeeded.  |
| `message`    | string  | Error message / hint.           |
| `error_code` | string  | Error code (e.g., `FORBIDDEN`). |


## OpenAPI

````yaml openapi.json POST /v1/chat/completions
openapi: 3.0.0
info:
  description: >-
    Official API documentation for Surf - Your gateway to Web3 exploration and
    discovery.
  title: Surf API Documentation
  termsOfService: http://swagger.io/terms/
  contact:
    name: Surf Team
    email: backend@cybertinolab.com
  license:
    name: GPL-3.0-or-later
    url: https://spdx.org/licenses/GPL-3.0-or-later.html
  version: 1.0.0
servers:
  - url: https://api.asksurf.ai/gateway
security: []
paths:
  /v1/chat/completions:
    post:
      tags:
        - completions
      summary: Chat Completions
      description: >-
        OpenAI-compatible Chat Completions. Use Bearer authorization. Proxied to
        hermod for authentication, billing, and LLM execution.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/model.CompletionsRequest'
        description: Request body (OpenAI-compatible)
        required: true
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/model.CompletionsProxyResponse'
            text/event-stream:
              schema:
                $ref: '#/components/schemas/model.CompletionsProxyResponse'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
            text/event-stream:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
            text/event-stream:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
        '402':
          description: Payment Required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
            text/event-stream:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
        '502':
          description: Bad Gateway
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
            text/event-stream:
              schema:
                $ref: '#/components/schemas/model.BaseResponse'
      security:
        - AccessToken: []
components:
  schemas:
    model.CompletionsRequest:
      type: object
      properties:
        ability:
          type: array
          items:
            $ref: '#/components/schemas/model.Ability'
          example:
            - '["evm_onchain"'
            - '"market_analysis"'
            - '"calculate"]'
        citation:
          type: array
          items:
            $ref: '#/components/schemas/model.Citation'
          example:
            - '["source"'
            - '"chart"]'
        messages:
          type: array
          items:
            $ref: '#/components/schemas/model.Message'
        model:
          allOf:
            - $ref: '#/components/schemas/model.LLMModel'
          example: surf-ask
        reasoning_effort:
          enum:
            - low
            - medium
            - high
          allOf:
            - $ref: '#/components/schemas/model.ReasoningEffort'
          example: medium
        stream:
          type: boolean
          example: false
        tools:
          type: array
          items:
            $ref: '#/components/schemas/model.Tool'
    model.CompletionsProxyResponse:
      type: object
      properties:
        choices:
          type: array
          items:
            $ref: '#/components/schemas/model.CompletionsChoice'
        created:
          description: Timestamp indicating when the message was generated.
          type: integer
          example: 1764312947
        id:
          description: >-
            A unique identifier for the generated message. Used for internal
            tracking.
          type: string
          example: chatcmpl-1764312947577
        model:
          type: string
          example: surf-ask
        object:
          type: string
          example: chat.completion
        usage:
          $ref: '#/components/schemas/model.CompletionsUsage'
    model.BaseResponse:
      type: object
      properties:
        error_code:
          type: string
          example: FORBIDDEN
        message:
          type: string
          example: success
        success:
          type: boolean
          example: true
    model.Ability:
      type: string
      enum:
        - search
        - evm_onchain
        - solana_onchain
        - market_analysis
        - calculate
      x-enum-varnames:
        - AbilitySearch
        - AbilityEVMOnchain
        - AbilitySolanaOnchain
        - AbilityMarketAnalysis
        - AbilityCalculate
    model.Citation:
      type: string
      enum:
        - source
        - chart
      x-enum-varnames:
        - CitationSource
        - CitationChart
    model.Message:
      type: object
      properties:
        content:
          description: The final user-visible text generated by the assistant.
          type: string
          example: Summarize today's Bitcoin market.
        role:
          description: |-
            The role of the message. The source of the message. Include:
            - system — system instructions
            - user — user input
            - assistant — model output
            - tool — tool responses
          allOf:
            - $ref: '#/components/schemas/model.Role'
          example: user
    model.LLMModel:
      type: string
      enum:
        - surf-ask
        - surf-research
        - surf-1.5
        - surf-1.5-thinking
        - surf-1.5-instant
      x-enum-varnames:
        - LLMModelSURFASK
        - LLMModelSURFRESEARCH
        - LLMModelSURF15
        - LLMModelSURF15_THINKING
        - LLMModelSURF15_INSTANT
    model.ReasoningEffort:
      type: string
      enum:
        - low
        - medium
        - high
      x-enum-varnames:
        - ReasoningEffortLow
        - ReasoningEffortMedium
        - ReasoningEffortHigh
    model.Tool:
      type: object
      properties:
        function:
          $ref: '#/components/schemas/model.ToolFunction'
        type:
          enum:
            - function
          allOf:
            - $ref: '#/components/schemas/model.ToolType'
          example: function
    model.CompletionsChoice:
      type: object
      properties:
        finish_reason:
          description: |-
            Indicates why the model stopped generating. Common values:
            - stop — normal completion
            - length — hit max token limit
            - tool_calls — model invoked a tool
            - content_filter — blocked by safety filters
            - error — unexpected interruption
          type: string
          example: stop
        index:
          description: The index of the choice
          type: integer
          example: 0
        message:
          $ref: '#/components/schemas/model.CompletionsMessage'
    model.CompletionsUsage:
      type: object
      properties:
        completion_tokens:
          type: integer
          example: 113
        prompt_tokens:
          type: integer
          example: 13026
        total_tokens:
          type: integer
          example: 13760
    model.Role:
      type: string
      enum:
        - user
        - assistant
        - system
      x-enum-varnames:
        - RoleUSER
        - RoleASSISTANT
        - RoleSYSTEM
    model.ToolFunction:
      type: object
      properties:
        description:
          type: string
          example: Calculate the total value of a crypto portfolio
        name:
          type: string
          example: calculate_portfolio_value
        parameters:
          description: Parameters is a JSON Schema object.
          type: object
    model.ToolType:
      type: string
      enum:
        - function
      x-enum-varnames:
        - ToolTypeFunction
    model.CompletionsMessage:
      type: object
      properties:
        content:
          type: string
          example: Here is a summary of today's Bitcoin market...
        reasoning:
          description: A field to showcase model's thinking process.
          type: string
          example: Internal chain-of-thought or brief rationale
        role:
          allOf:
            - $ref: '#/components/schemas/model.Role'
          example: assistant
  securitySchemes:
    AccessToken:
      type: apiKey
      name: Authorization
      in: header

````