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

# OpenAI-compatible API examples

> Make OpenAI-compatible API requests to Poolside models.

This page shows workflows that combine several requests or depend on model behavior, such as thinking control, streaming, tool calling, and structured output. For the parameters and response schema of a single endpoint, see the endpoint reference in this section.

The examples use Poolside-hosted inference. Other access methods use their own base URL and credentials:

| Access method                   | Base URL                           | What you need                                                   |
| ------------------------------- | ---------------------------------- | --------------------------------------------------------------- |
| Poolside-hosted inference       | `https://inference.poolside.ai/v1` | API key from [Poolside Platform](https://platform.poolside.ai/) |
| Self-managed Poolside inference | `https://<model-hostname>/v1`      | The key configured for your endpoint, if it requires one        |
| OpenRouter                      | `https://openrouter.ai/api/v1`     | OpenRouter API key and OpenRouter model ID                      |

Request parameters and model IDs differ by access method, so an example does not always port. For OpenRouter, use the sections that name OpenRouter explicitly, and check the `supported_parameters` field in [OpenRouter's model metadata](https://openrouter.ai/api/v1/models) before adapting any other example.

## Prerequisites

Before you get started, you need:

1. Authentication for your access method: a [Poolside Platform](https://platform.poolside.ai/) or OpenRouter API key, or the authentication configured for your self-managed model endpoint. To configure authentication, see [Authenticate API requests](/api/overview#authenticate-api-requests).
2. `curl` or another tool that can make API requests.

<Note>
  If API key authentication is off for your model endpoint, omit the `Authorization` header.
</Note>

## List available models

Most API requests require a model `id`. For the full request and response reference, see the List models endpoint in this section.

```bash title="List models" theme={null}
curl --request GET \
  --url https://inference.poolside.ai/v1/models \
  --header 'Authorization: Bearer <api-key>'
```

OpenRouter serves models from many providers, so filter its catalog for Poolside model IDs. This request does not require an API key:

```bash title="List Poolside models on OpenRouter" theme={null}
curl --silent --request GET \
  --url https://openrouter.ai/api/v1/models |
  jq '[.data[] | select(.id | startswith("poolside/")) | {id, name}]'
```

```json title="Response example" theme={null}
[
  {
    "id": "poolside/laguna-s-2.1",
    "name": "Poolside: Laguna S 2.1"
  },
  {
    "id": "poolside/laguna-s-2.1:free",
    "name": "Poolside: Laguna S 2.1 (free)"
  }
]
```

OpenRouter may offer free and paid Poolside models with different context lengths. To see current availability in the browser, see [Poolside models on OpenRouter](https://openrouter.ai/poolside).

## Send a chat prompt

To generate a completion, send the model `id` and your prompt as `content` inside `messages` with the `user` role:

```bash title="Send chat prompt" theme={null}
curl --request POST \
  --url https://inference.poolside.ai/v1/chat/completions \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api-key>' \
  --data '{
  "messages": [
    {
      "content": "Explain cURL",
      "role": "user"
    }
  ],
  "model": "poolside/laguna-s-2.1"
}'
```

```json title="Response example" theme={null}
{
  "id": "chatcmpl-abc123",
  "model": "poolside/laguna-s-2.1",
  "created": 1751993576,
  "object": "chat.completion",
  "choices": [
    {
      "index": 0,
      "message": {
        "reasoning_content": "The user is asking what cURL is. I should explain that it is a command-line tool, name the protocols it supports, and give the common uses...",
        "content": "cURL is a powerful command-line tool used for transferring data to or from a server, supporting various protocols such as HTTP, HTTPS, FTP, and more. It's widely used for testing APIs, downloading files, and automating HTTP requests. cURL allows you to specify headers, methods (GET, POST, PUT, DELETE, etc.), and data payloads, making it versatile for a range of web-related tasks.\n",
        "role": "assistant"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "completion_tokens": 88,
    "prompt_tokens": 447,
    "total_tokens": 535
  }
}
```

Responses from models that think can include `reasoning_content` in addition to `content`. The `usage` object reports the tokens used by the request and response.

To give the model information it does not have, put that context in the message `content` as ordinary text. For guidance on what to include, see the [Prompting guide](/resources/prompting-best-practices).

### Turn off thinking

Some models support per-request thinking control through chat template settings. To turn thinking off, set `chat_template_kwargs.enable_thinking` to `false`. The response then returns `content` without `reasoning_content`:

```bash title="Turn off thinking" theme={null}
curl --request POST \
  --url https://inference.poolside.ai/v1/chat/completions \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api-key>' \
  --data '{
    "model": "poolside/laguna-s-2.1",
    "messages": [
      {
        "role": "user",
        "content": "What are channels in Go?"
      }
    ],
    "chat_template_kwargs": {
      "enable_thinking": false
    }
  }'
```

Support depends on the model and serving configuration.

## Preserve reasoning in agentic workflows

<Note>
  For agentic workflows with Poolside models, preserve `reasoning_content` from assistant responses when you include those responses in follow-up requests. Dropping previous reasoning content can prevent the model from reasoning in later steps. For examples, see the [Control reasoning](https://huggingface.co/poolside/Laguna-S-2.1#controlling-reasoning) sections on the Laguna model pages.
</Note>

## Stream responses

To receive the response as a series of chunks returned as server-sent events, set `stream` to `true`. This is useful for real-time applications. Set `stream_options.include_usage` to `true` to receive token counts in a final chunk:

```bash title="Stream chat prompt" theme={null}
curl --request POST \
  --url https://inference.poolside.ai/v1/chat/completions \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api-key>' \
  --data '{
  "messages": [
    {
      "content": "Explain cURL",
      "role": "user"
    }
  ],
  "model": "poolside/laguna-s-2.1",
  "stream": true,
  "stream_options": {
    "include_usage": true
  }
}'
```

Generation chunks carry a `delta` object, and models that think stream `reasoning_content` before `content`. When `include_usage` is `true`, a final chunk carries `usage` with an empty `choices` array:

```text title="Response example" theme={null}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":"The user"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","reasoning_content":" wants"},"finish_reason":null}]}

data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":"cURL"},"finish_reason":null}]}

...

data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":447,"completion_tokens":88,"total_tokens":535}}

data: [DONE]
```

## Extend models with tools

You can extend a model's capabilities by providing tools that it can call during a conversation. This lets the model retrieve real-time data, run calculations, or interact with external systems.

<Note>
  OpenAI-compatible tool calling behavior can vary by model and serving configuration. If you need to force a specific function call with `tool_choice`, such as `"required"` or a named function, try turning off thinking first by setting `chat_template_kwargs.enable_thinking` to `false`. For request and response formats, see the [OpenAI function calling guide](https://platform.openai.com/docs/guides/function-calling).
</Note>

Tool calling takes two requests. First, include a `tools` array and define the functions the model can call:

```bash title="Define a tool" theme={null}
curl --request POST \
  --url https://inference.poolside.ai/v1/chat/completions \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api-key>' \
  --data '{
  "model": "poolside/laguna-s-2.1",
  "messages": [
    {
      "role": "user",
      "content": "what is the weather forecast for San Francisco"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_forecast",
        "description": "Get weather forecast for a city",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "City name"
            }
          },
          "required": [
            "city"
          ],
          "additionalProperties": false
        }
      }
    }
  ]
}'
```

When the model needs a tool, it returns a `tool_calls` array and a `finish_reason` of `tool_calls`. The `arguments` value is a JSON string:

```json title="Response example" theme={null}
{
  "id": "chatcmpl-abc123",
  "model": "poolside/laguna-s-2.1",
  "created": 1753997542,
  "object": "chat.completion",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "tool_calls": [
          {
            "id": "call_abc123",
            "type": "function",
            "function": {
              "name": "get_forecast",
              "arguments": "{\"city\": \"San Francisco\"}"
            }
          }
        ]
      },
      "finish_reason": "tool_calls"
    }
  ],
  "usage": {
    "completion_tokens": 95,
    "prompt_tokens": 338,
    "total_tokens": 433
  }
}
```

Second, run the tool yourself and send the result back. Include the original user message, the assistant message containing the `tool_calls`, and a `tool` message whose `tool_call_id` matches.

Copy the assistant message from the previous response rather than rebuilding it, so that `reasoning_content` travels with it when the model returned it. The assistant message below is abbreviated for readability:

```bash title="Send a tool result" theme={null}
curl --request POST \
  --url https://inference.poolside.ai/v1/chat/completions \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api-key>' \
  --data '{
  "model": "poolside/laguna-s-2.1",
  "messages": [
    {
      "role": "user",
      "content": "what is the weather forecast for San Francisco"
    },
    {
      "role": "assistant",
      "content": "",
      "tool_calls": [
        {
          "id": "call_abc123",
          "type": "function",
          "function": {
            "name": "get_forecast",
            "arguments": "{\"city\": \"San Francisco\"}"
          }
        }
      ]
    },
    {
      "role": "tool",
      "tool_call_id": "call_abc123",
      "content": "{\"temperature\": 25}"
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_forecast",
        "description": "Get weather forecast for a city",
        "parameters": {
          "type": "object",
          "properties": {
            "city": {
              "type": "string",
              "description": "City name"
            }
          },
          "required": [
            "city"
          ],
          "additionalProperties": false
        }
      }
    }
  ]
}'
```

The model then answers using the tool result.

## Constrain the response format

To constrain a response to a specific JSON structure, include a `response_format` object with `type` set to `json_schema` and a JSON Schema definition. The model returns a JSON string in `message.content` that follows the schema.

Support depends on the model and serving configuration.

```bash title="Request structured output" theme={null}
curl --request POST \
  --url https://inference.poolside.ai/v1/chat/completions \
  --header 'Content-Type: application/json' \
  --header 'Authorization: Bearer <api-key>' \
  --data '{
  "model": "poolside/laguna-s-2.1",
  "messages": [
    {
      "content": "Extract the city and country from: I live in San Francisco, United States.",
      "role": "user"
    }
  ],
  "response_format": {
    "type": "json_schema",
    "json_schema": {
      "name": "location",
      "strict": true,
      "schema": {
        "type": "object",
        "properties": {
          "city": {
            "type": "string"
          },
          "country": {
            "type": "string"
          }
        },
        "required": [
          "city",
          "country"
        ],
        "additionalProperties": false
      }
    }
  }
}'
```

Parse `message.content` as JSON to get the structured object:

```json theme={null}
{
  "city": "San Francisco",
  "country": "United States"
}
```

To request a valid JSON object without enforcing a schema, set `response_format` to `{"type": "json_object"}` instead.

## Control reasoning through OpenRouter

OpenRouter uses its own `reasoning` object rather than `chat_template_kwargs`. To control reasoning effort for OpenRouter-compatible models, include a `reasoning` object:

```json theme={null}
{
  "model": "<model-id>",
  "messages": [
    {
      "content": "Explain cURL",
      "role": "user"
    }
  ],
  "reasoning": {
    "effort": "max"
  }
}
```

OpenRouter's generic effort values are `max`, `xhigh`, `high`, `medium`, `low`, `minimal`, and `none`, but provider and model support varies. Replace `<model-id>` and `max` with values that your selected model supports. To check which parameters a model accepts, see the `supported_parameters` field in [OpenRouter's model metadata](https://openrouter.ai/api/v1/models).

<Note>
  The `reasoning` field is OpenRouter-style: a top-level object, which differs from the OpenAI Chat Completions `reasoning_effort` parameter. Use it only with OpenRouter or another provider that accepts this field. A direct connection to the OpenAI Chat Completions API at `https://api.openai.com/v1/chat/completions` rejects the `reasoning` field.

  Effort-level support varies by provider and model. Some providers ignore effort settings or apply thinking automatically. Check your provider's documentation for how it handles the `reasoning` field.
</Note>
