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

# Python SDK - Send Method

> Complete guide to the send() method in the Python SDK.

The `send()` method is used to make non-streaming chat completion requests to the Edgee AI Gateway. It returns a `SendResponse` object with the model's response.

## Arguments

| Parameter | Type | Description |
| - | - | - |
| `model` (The field is required.) | `str` | The model identifier to use (e.g., `"gpt-5.2"`) |
| `input` | `str \| InputObject \| dict` | The input for the completion. Can be a simple string or a structured `InputObject` or dictionary |
| `stream` | `bool` | If `True`, returns a generator yielding `StreamChunk` objects. If `False` (default), returns a `SendResponse` object |

### Input Types

#### String Input

When `input` is a string, it's automatically converted to a user message:

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input="What is the capital of France?"
)

# Equivalent to: input={"messages": [{"role": "user", "content": "What is the capital of France?"}]}
print(response.text)
# "The capital of France is Paris."
```

#### InputObject or Dictionary

When `input` is an `InputObject` or dictionary, you have full control over the conversation:

| Property | Type | Description |
| - | - | - |
| `messages` (The field is required.) | `list[dict]` | Array of conversation messages |
| `tools` | `list[dict] \| None` | Array of function tools available to the model |
| `tool_choice` | `str \| dict \| None` | Controls which tool (if any) the model should call. See [Tools documentation](https://www.edgee.ai/docs/llm-router/sdk/python/tools.md) for details |
| `tags` | `list[str] \| None` | Optional tags to categorize and label the request for analytics and filtering. Can also be sent via the `x-edgee-tags` header (comma-separated) |
| `compression_model` | `str` | Compression model for this request: `"claude"`, `"codex"`, `"opencode"`, `"cursor"`. Each model is a bundle of compression strategies. Overrides API key settings when present. |

**Example with Dictionary Input:**

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input={
        "messages": [
            {"role": "user", "content": "What is 2+2?"}
        ]
    }
)

print(response.text)
# "2+2 equals 4."
```

**Example with Tags:**

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input={
        "messages": [
            {"role": "user", "content": "Summarize this article"}
        ],
        "tags": ["summarization", "production", "user-123"]
    }
)
```

### Message Object

Each message in the `messages` array has the following structure:

| Property | Type | Description |
| - | - | - |
| `role` (The field is required.) | `str` | The role of the message sender: `"system"`, `"developer"`, `"user"`, `"assistant"`, or `"tool"` |
| `content` | `str \| None` | The message content. Required for `system`, `user`, `tool` and `developer` roles. Optional for `assistant` when `tool_calls` is present |
| `name` | `str \| None` | Optional name for the message sender |
| `tool_calls` | `list[dict] \| None` | Array of tool calls made by the assistant. Only present in `assistant` messages |
| `tool_call_id` | `str \| None` | ID of the tool call this message is responding to. Required for `tool` role messages |

### Message Roles

* **`system`**: System instructions that set the behavior of the assistant
* **`developer`**: Instructions provided by the application developer, prioritized ahead of user messages.
* **`user`**: Instructions provided by an end user.
* **`assistant`**: Assistant responses (can include `tool_calls`)
* **`tool`**: Results from tool/function calls (requires `tool_call_id`)

**Example - System and User Messages:**

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input={
        "messages": [
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": "What is 2+2?"},
            {"role": "assistant", "content": "2+2 equals 4."},
            {"role": "user", "content": "What about 3+3?"}
        ]
    }
)

print(response.text)
# "3+3 equals 6."
```

For complete tool calling examples and best practices, see [Tools documentation](https://www.edgee.ai/docs/llm-router/sdk/python/tools.md).

## Return Value

The `send()` method returns a `SendResponse` object when `stream=False` (default):

### SendResponse Object

| Property | Type | Description |
| - | - | - |
| `choices` | `list[Choice]` | Array of completion choices (typically one) |
| `usage` | `Usage \| None` | Token usage information (if provided by the API) |
| `compression` | `Compression \| None` | Token compression metrics (if compression was applied) |

### Choice Object

Each choice in the `choices` array contains:

| Property | Type | Description |
| - | - | - |
| `index` | `int` | The index of this choice in the array |
| `message` | `dict` | The assistant's message response |
| `finish_reason` | `str \| None` | Reason why the generation stopped. Possible values: `"stop"`, `"length"`, `"tool_calls"`, `"content_filter"`, or `None` |

**Example - Handling Multiple Choices:**

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input="Give me a creative idea."
)

# Process all choices
for choice in response.choices:
    print(f"Choice {choice.index}: {choice.message.get('content')}")
    print(f"Finish reason: {choice.finish_reason}")
```

### Message Object (in Response)

The `message` in each choice has:

| Property | Type | Description |
| - | - | - |
| `role` | `str` | The role of the message (typically `"assistant"`) |
| `content` | `str \| None` | The text content of the response. `None` when `tool_calls` is present |
| `tool_calls` | `list[dict] \| None` | Array of tool calls requested by the model (if any). See [Tools documentation](https://www.edgee.ai/docs/llm-router/sdk/python/tools.md) for details |

### Usage Object

Token usage information (when available):

| Property | Type | Description |
| - | - | - |
| `prompt_tokens` | `int` | Number of tokens in the prompt (after compression if applied) |
| `completion_tokens` | `int` | Number of tokens in the completion |
| `total_tokens` | `int` | Total tokens used (prompt + completion) |

**Example - Accessing Token Usage:**

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input="Explain quantum computing briefly."
)

if response.usage:
    print(f"Prompt tokens: {response.usage.prompt_tokens}")
    print(f"Completion tokens: {response.usage.completion_tokens}")
    print(f"Total tokens: {response.usage.total_tokens}")
```

### Compression Object

Token compression metrics (when compression is applied):

| Property | Type | Description |
| - | - | - |
| `saved_tokens` | `int` | Number of tokens saved by compression |
| `cost_savings` | `int` | Estimated cost savings in micro-units (e.g. 27000 = \$0.027) |
| `reduction` | `int` | Percentage reduction (e.g. 18 = 18%) |
| `time_ms` | `int` | Time taken for compression in milliseconds |

**Example - Accessing Compression Metrics:**

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input={
        "messages": [
            {"role": "user", "content": "Analyze this long document with lots of context..."}
        ],
        "compression_model": "claude"
    }
)

if response.compression:
    print(f"Saved tokens: {response.compression.saved_tokens}")
    print(f"Reduction: {response.compression.reduction}%")
    print(f"Cost savings: ${response.compression.cost_savings / 1_000_000:.3f}")
    print(f"Time: {response.compression.time_ms} ms")
```

> [!NOTE]
>
> The `compression` object is only present when token compression is applied to the request. Simple queries may not trigger compression.

## Convenience Properties

The `SendResponse` class provides convenience properties for easier access:

| Property | Type | Description |
| - | - | - |
| `text` | `str \| None` | Shortcut to `choices[0].message["content"]` |
| `message` | `dict \| None` | Shortcut to `choices[0].message` |
| `finish_reason` | `str \| None` | Shortcut to `choices[0].finish_reason` |
| `tool_calls` | `list \| None` | Shortcut to `choices[0].message.get("tool_calls")` |

**Example - Using Convenience Properties:**

```python theme={"dark"}
response = edgee.send(
    model="gpt-5.2",
    input="Hello!"
)

# Instead of: response.choices[0].message["content"]
print(response.text)

# Instead of: response.choices[0].message
print(response.message)

# Instead of: response.choices[0].finish_reason
print(response.finish_reason)

# Instead of: response.choices[0].message.get("tool_calls")
if response.tool_calls:
    print("Tool calls:", response.tool_calls)
```

## Streaming with send()

You can use `send()` with `stream=True` to get streaming responses. This returns a generator yielding `StreamChunk` objects:

```python theme={"dark"}
for chunk in edgee.send("gpt-5.2", "Tell me a story", stream=True):
    if chunk.text:
        print(chunk.text, end="", flush=True)
```

For more details about streaming, see the [Stream Method documentation](https://www.edgee.ai/docs/llm-router/sdk/python/stream.md).

## Error Handling

The `send()` method can raise exceptions in several scenarios:

```python theme={"dark"}
try:
    response = edgee.send(
        model="gpt-5.2",
        input="Hello!"
    )
except RuntimeError as error:
    # API errors: "API error {status}: {message}"
    # Network errors: Standard HTTP errors
    print(f"Request failed: {error}")
```

### Common Errors

* **API errors**: `RuntimeError: API error {status}: {message}` - The API returned an error status
* **Network errors**: Standard HTTP errors from `urllib`
* **Invalid input**: Errors from invalid request structure


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.