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

# Unify Python library

> Install and use the official Unify Python library.

## Overview

Unify provides an official Python library that makes it easy to interact with
the Unify API from your code. The library handles authentication, request
formatting, and response parsing so you can focus on building your integration.

<Info>
  The Python library source code is [available on GitHub](https://github.com/unifygtm/sdk-python).
</Info>

## Quick start

You can install the Unify Python library with your preferred package manager:

<CodeGroup>
  ```shell pip theme={null}
  pip install unifygtm-sdk
  ```

  ```shell uv theme={null}
  uv add unifygtm-sdk
  ```

  ```shell poetry theme={null}
  poetry add unifygtm-sdk
  ```
</CodeGroup>

Generate an API key in [Settings → Developers](https://app.unifygtm.com/dashboard/settings/integrations/api-keys)
and then initialize the client:

<CodeGroup>
  ```python Sync theme={null}
  import os
  from unify import Unify

  client = Unify(
      # This is the default and can be omitted
      api_key=os.environ.get("UNIFY_API_KEY"),
  )

  objects = client.data.objects.list()
  print(objects.data)
  ```

  ```python Async theme={null}
  import os
  import asyncio
  from unify import AsyncUnify

  client = AsyncUnify(
      # This is the default and can be omitted
      api_key=os.environ.get("UNIFY_API_KEY"),
  )

  async def main() -> None:
      objects = await client.data.objects.list()
      print(objects.data)

  asyncio.run(main())
  ```
</CodeGroup>

<Note>
  The Python library reads the `UNIFY_API_KEY` environment variable by default.
  If the variable is set, you can omit the `api_key` parameter when
  initializing the client.
</Note>

## Usage

<AccordionGroup>
  <Accordion title="List objects">
    Retrieve all objects in your Unify workspace to see what's available:

    ```python Python theme={null}
    from unify import Unify

    client = Unify()

    objects = client.data.objects.list()

    for obj in objects.data:
        print(f"{obj.api_name} ({obj.display_name})")
    ```

    ```text theme={null}
    company (Company)
    person (Person)
    opportunity (Opportunity)
    ...
    ```
  </Accordion>

  <Accordion title="List attributes on an object">
    Retrieve the attributes defined on a specific object to understand its schema:

    ```python Python theme={null}
    attributes = client.data.attributes.list("person")

    for attr in attributes.data:
        print(f"{attr.api_name} — {attr.type} (required: {attr.is_required})")
    ```

    ```text theme={null}
    email — Text (required: True)
    first_name — Text (required: False)
    last_name — Text (required: False)
    title — Text (required: False)
    company — Reference (required: False)
    ...
    ```
  </Accordion>

  <Accordion title="Upsert a person record">
    Use the upsert endpoint to create a new record or update an existing one based
    on a unique attribute. This is the recommended approach for syncing data into
    Unify because it handles deduplication automatically.

    ```python Python theme={null}
    record = client.data.records.upsert(
        object_name="person",
        match={
            "email": "jane@acme.com",
        },
        create_or_update={
            "email": "jane@acme.com",
            "first_name": "Jane",
            "last_name": "Smith",
            "title": "Head of Growth",
            "company": {
                "match": {
                    "domain": "acme.com",
                },
                "create_or_update_if_empty": {
                    "domain": "acme.com",
                    "name": "Acme Corp",
                },
            },
        },
    )

    print(f"Record ID: {record.data.id}")
    print(f"Created at: {record.data.created_at}")
    print(f"Updated at: {record.data.updated_at}")
    ```

    ```text theme={null}
    Record ID: b0fdcba7-a8e5-4fa3-abb0-26ee20f5ba47
    Created at: 2025-04-15T14:30:00Z
    Updated at: 2025-04-15T14:30:00Z
    ```

    Running this again with updated attributes (for example, a new title) will
    update the existing record rather than creating a duplicate, because `email` is
    used as the match key.
  </Accordion>

  <Accordion title="Find a record by unique attribute">
    If you know the value of a unique attribute (like an email address), you can
    look up the record directly without needing the record ID:

    ```python Python theme={null}
    record = client.data.records.find_unique(
        object_name="person",
        match={
            "email": "jane@acme.com",
        },
    )

    print(f"Record ID: {record.data.id}")
    print(f"Name: {record.data.attributes.first_name} {record.data.attributes.last_name}")
    print(f"Title: {record.data.attributes.title}")
    ```

    ```text theme={null}
    Record ID: b0fdcba7-a8e5-4fa3-abb0-26ee20f5ba47
    Name: Jane Smith
    Title: Head of Growth
    ```
  </Accordion>
</AccordionGroup>

## Next steps

<CardGroup cols={2}>
  <Card title="Send records via API" href="/developers/guides/send-data/overview" icon="rocket" horizontal>
    Send data from external tools and systems via API.
  </Card>

  <Card title="Data API reference" href="/developers/api/data/overview" icon="book" horizontal>
    Explore the full Data API reference.
  </Card>
</CardGroup>
