[comment]: # translation:outdated

[comment]: # aside:5

[comment]: # ({new-ebcec779})
# Send data to Zabbix server or proxy

[zabbix\_utils](https://github.com/zabbix/python-zabbix-utils/blob/main/README.md) lets you send item values to a [trapper item](/manual/config/items/itemtypes/trapper) on Zabbix server or proxy (similarly to [Zabbix sender](/manual/concepts/sender)).

You can send a single value, multiple values, or even target multiple Zabbix clusters.

Data can be sent in synchronous or asynchronous mode:

-   In synchronous mode, your Python script sends values and waits for a response before continuing; this is suitable for simple, sequential, and predictable operations.
-   In asynchronous mode, the script sends values without waiting for each response, allowing other operations to proceed in parallel; this is more efficient for slow requests or large batches of data.

The examples on this page focus on synchronous mode, though [asynchronous mode](#asynchronous-mode) follows similar patterns.
Additional examples are available in the [zabbix_utils](https://github.com/zabbix/python-zabbix-utils/tree/main/examples) GitHub repository.

[comment]: # ({/new-ebcec779})

[comment]: # ({new-2b58225a})
#### Import

To use zabbix\_utils for sending item values, import the `Sender` class in your script:

```python
from zabbix_utils import Sender
```

For sending multiple values, you may also import the `ItemValue` class:

```python
from zabbix_utils import Sender, ItemValue
```

[comment]: # ({/new-2b58225a})

[comment]: # ({new-248fa427})
#### Send single value

To send an item value:

1. Create a `Sender` instance, specifying the IP address and port of your Zabbix server or proxy.
2. Call the `send_value()` method on the `Sender` instance using the following format:

```python
sender_instance.send_value('host', 'item.key', 'value', optional_timestamp, optional_nanoseconds)
```

For example, to send `1` to the `service.status` trapper item on the `Linux server` host:

```python
sender = Sender(server='127.0.0.1', port=10051)
response = sender.send_value('Linux server', 'service.status', 1)
```

[comment]: # ({/new-248fa427})

[comment]: # ({new-8c41d6ba})
##### Using non-default IP

If the server running your script has multiple IP addresses, you can specify a `source_ip` for the `Sender` to use when sending values to Zabbix server or proxy:

```python
sender = Sender(
    server='127.0.0.1',
    port=10051,
    source_ip='10.10.7.1'
)
```

[comment]: # ({/new-8c41d6ba})

[comment]: # ({new-de824c12})
##### Using timeout

You can set a response `timeout` for the `Sender` to control how long your script should wait for a response from Zabbix server or proxy before giving up:

```python
sender = Sender(
    server='127.0.0.1',
    port=10051,
    timeout=30
)
```

[comment]: # ({/new-de824c12})

[comment]: # ({new-62dd1793})
##### Using agent configuration file

You can let zabbix\_utils read the [`Server`](/manual/appendix/config/zabbix_agentd#server) or [`ServerActive`](/manual/appendix/config/zabbix_agentd#serveractive) parameters from a local Zabbix agent or agent 2 configuration file.
In such cases, you don't need to specify connection parameters when creating a `Sender` instance:

```python
sender = Sender(
    use_config=True,
    config_path='/etc/zabbix/zabbix_agent2.conf'
)
```

::: noteimportant
If `ServerActive` contains one or more Zabbix clusters with multiple server instances, `Sender` sends data to the first available server in each cluster.
If `ServerActive` is not set, the address from `Server` with the default port (10051) is used.
:::

[comment]: # ({/new-62dd1793})

[comment]: # ({new-a8b30296})
##### Using encryption

The `Sender` does not include built-in encryption support, but you can provide it by creating a wrapper using third-party libraries:

```python
def psk_wrapper(sock, tls):
    # ...
    # Implementation of TLS PSK wrapper for the socket
    # ...

sender = Sender(
    server='127.0.0.1',
    port=10051,
    socket_wrapper=psk_wrapper
)
```

[comment]: # ({/new-a8b30296})

[comment]: # ({new-b6a3d94d})
##### Response for single value

The response returned by Zabbix server or proxy is processed by the library and returned as a `TrapperResponse` object:

```python
print(response)
# {"processed": 1, "failed": 0, "total": 1, "time": "0.000123", "chunk": 1}

print(response.processed)
# 1

print(response.failed)
# 0

print(response.total)
# 1
```

[comment]: # ({/new-b6a3d94d})

[comment]: # ({new-9eeea215})
##### Asynchronous mode

Asynchronous mode lets your Python script send values without waiting for a response from Zabbix server or proxy.
This can make your script more efficient when it needs to send many values or when some values take a long time send.

When using asynchronous mode, there are a few important differences compared to synchronous mode:

-   Import Python's `asyncio` module (you must first [install](/devel/python/install) the required dependencies).
-   Import `AsyncSender` instead of `Sender`.
-   Write your code inside an `async` function.
-   Use `await` when calling the `send_value()` method.

For example, to send a single value using asynchronous mode:

```python
# 1. Import asyncio for asynchronous mode, and AsyncSender from zabbix_utils:
import asyncio
from zabbix_utils import AsyncSender

# 2. Define the main async function where all data sending operations (must await) will be executed:
async def main():
    sender = AsyncSender(server='127.0.0.1', port=10051)
    response = await sender.send_value('Linux server', 'service.status', 1)

    # 3. Print the response returned by Zabbix server or proxy:
    print(response)

# 4. Run the async main() function using asyncio's event loop:
asyncio.run(main())
```

[comment]: # ({/new-9eeea215})

[comment]: # ({new-34e0ff6d})
#### Send multiple values

To send multiple values:

1. Prepare an array of `ItemValue` objects, each using the same format as the [`send_value()`](#send-single-value) method.
2. Create a `Sender` instance, specifying the IP address and port of your Zabbix server or proxy.
3. Call the `send()` method (instead of `send_value()`) on the `Sender` instance, specifying the array of objects with values to send.

For example, to send five values to different hosts:

```python
items = [
    ItemValue('server-de', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-fr', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-uk', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-nl', 'service.status', 'up', 1770887205, 100),
    ItemValue('server-pl', 'service.status', 'up', 1770887205, 100),
]

sender = Sender(server='127.0.0.1', port=10051)
response = sender.send(items)
```

[comment]: # ({/new-34e0ff6d})

[comment]: # ({new-5848e96f})
##### Using custom chunk size

If you need to send more values than a trapper item can accept in a single request, you can split them into chunks.

By default, the chunk size is 250 values.
You can change it by setting the `chunk_size` parameter when creating a `Sender` instance.

For example, to send five values in three chunks (2-2-1), set the `chunk_size` parameter to `2`:

```python
items = [
    ItemValue('server-de', 'service.status', 'up'),
    ItemValue('server-fr', 'service.status', 'up'),
    ItemValue('server-uk', 'service.status', 'up'),
    ItemValue('server-nl', 'service.status', 'up'),
    ItemValue('server-pl', 'service.status', 'up'),
]

sender = Sender(server='127.0.0.1', port=10051, chunk_size=2)
response = sender.send(items)
```

[comment]: # ({/new-5848e96f})

[comment]: # ({new-5a0b5783})
##### Send values to multiple Zabbix clusters

To send values to multiple Zabbix clusters:

1. Prepare an array of Zabbix clusters. If a cluster has multiple nodes, the value will be sent to the first **available** node of each cluster.
2. Create a `Sender`, specifying your array of Zabbix clusters.
3. Call the `send_value()` method on the `Sender` instance using the same format as the [`send_value()`](#send-single-value) method.

For example, to send send a value to the first available node in each cluster:

```python
zabbix_clusters = [
    ['zabbix.cluster1.node1', 'zabbix.cluster1.node2:10051'],
    ['zabbix.cluster2.node1:10051', 'zabbix.cluster2.node2', 'zabbix.cluster2.node3']
]

sender = Sender(clusters=zabbix_clusters)
response = sender.send_value('Linux server', 'service.status', 1)
```

[comment]: # ({/new-5a0b5783})

[comment]: # ({new-d7964287})
##### Response for multiple values

By default, `Sender` returns an aggregated result of sending values across all hosts or clusters:

```python
print(response)
# {"processed": 2, "failed": 0, "total": 2, "time": "0.000108", "chunk": 2}
```

If you need more detailed information, you can inspect the results for each cluster and each chunk using the `response.details` attribute:

```python
print(response)
# {"processed": 2, "failed": 0, "total": 2, "time": "0.000108", "chunk": 2}

if response.failed == 0:
    print(f"Value sent successfully in {response.time}")
else:
    print(response.details)
    # {
    #     127.0.0.1:10051: [
    #         {
    #             "processed": 1,
    #             "failed": 0,
    #             "total": 1,
    #             "time": "0.000051",
    #             "chunk": 1
    #         }
    #     ],
    #     zabbix.example.local:10051: [
    #         {
    #             "processed": 1,
    #             "failed": 0,
    #             "total": 1,
    #             "time": "0.000057",
    #             "chunk": 1
    #         }
    #     ]
    # }

    for node, chunks in response.details.items():
        for resp in chunks:
            print(f"Processed {resp.processed} of {resp.total} at {node.address}:{node.port}")
            # Processed 1 of 1 at 127.0.0.1:10051
            # Processed 1 of 1 at zabbix.example.local:10051
```

[comment]: # ({/new-d7964287})
