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

# Python

> Send server-side events with the glyphhq package.

`glyphhq` sends events from Python services, scripts, and data pipelines. It has no dependencies and supports Python 3.9+.

The SDK reports its package name and version with each request. Glyph stores this as diagnostic metadata alongside the write key used for ingestion. It is not added to event properties.

## Install

<CodeGroup>
  ```bash pip theme={"dark"}
  pip install glyphhq
  ```

  ```bash uv theme={"dark"}
  uv add glyphhq
  ```
</CodeGroup>

## Initialize

```py theme={"dark"}
import os

from glyphhq import Glyph

glyph = Glyph(write_key=os.environ["GLYPH_WRITE_KEY"])
```

Keep the write key in an environment variable. Use a `glyph_pk_...` key in production and a `glyph_test_...` key while testing the integration. [Configuration & delivery](/docs/reference/configuration) lists all options. Python uses snake\_case keyword arguments: `flush_at`, `flush_interval_seconds`, `timeout_seconds`, `max_retries`, `api_host`, and `on_error`.

## Identify customers with `profile`

Call `profile` when a customer signs up or when a profile trait changes. `user_id` is the stable identity key, and every profile must include `email`.

```py theme={"dark"}
glyph.profile(
    "user_123",
    {"email": "sarah@example.com", "name": "Sarah Chen", "plan": "pro"},
)
```

## Record actions with `track`

```py theme={"dark"}
glyph.track("user_123", "Invoice paid", {"amount": 4900, "currency": "USD"})
```

To backfill an event, pass its original time with the `timestamp` keyword. It accepts a `datetime`, an ISO 8601 string, or epoch milliseconds:

```py theme={"dark"}
from datetime import datetime, timezone

glyph.track(
    "user_123",
    "Invoice paid",
    {"amount": 4900},
    timestamp=datetime(2026, 6, 1, 9, 30, tzinfo=timezone.utc),
)
```

## Record account context with `group`

`group` records a group/account event on the customer's timeline. Glyph does not currently create a separate account record or provide group-level analytics.

```py theme={"dark"}
glyph.group("user_123", "acme_inc", {"plan": "enterprise"})
```

## Flushing

The Python client is synchronous and has no background thread. It checks whether to flush **when a message is enqueued**. A flush runs when the queue reaches `flush_at` (default 20), when `flush_interval_seconds` (default 5) has elapsed at enqueue time, or when you call `flush()` or `close()`.

<Warning>
  A process that stops enqueuing will not flush on its own. Always call `close()` (or `flush()`) before exiting, or use the client as a context manager.
</Warning>

```py theme={"dark"}
with Glyph(write_key=os.environ["GLYPH_WRITE_KEY"]) as glyph:
    glyph.track("user_123", "Report exported")
# close() runs on exit and flushes the queue
```

## Handle delivery failures

The SDK retries transient delivery failures. Add an `on_error` callback to log batches that still fail:

```py theme={"dark"}
from glyphhq import DeliveryContext, Glyph


def on_error(error: Exception, messages: list, context: DeliveryContext) -> None:
    print(f"Glyph delivery failed (status={context.status})")


glyph = Glyph(write_key=os.environ["GLYPH_WRITE_KEY"], on_error=on_error)
```

Retryable failures leave the batch queued for a later flush. Non-retryable failures, such as a `401` from an invalid write key, call `on_error` and then drop the batch. See [Configuration & delivery](/docs/reference/configuration).
