Getting started¶
Requires Python 3.10+.
Provide your API key¶
Set AFFINITY_API_KEY:
export AFFINITY_API_KEY="your-api-key"
Then create a client from the environment:
from affinity import Affinity
client = Affinity.from_env()
To load a local .env file, install the optional extra and set load_dotenv=True:
pip install "affinity-sdk[dotenv]"
from affinity import Affinity
client = Affinity.from_env(load_dotenv=True)
Disable writes (policy)¶
If you want the SDK to guarantee it does not perform write operations (POST/PUT/PATCH/DELETE), disable writes via policy:
from affinity import Affinity
from affinity.policies import Policies, WritePolicy
client = Affinity.from_env(policies=Policies(write=WritePolicy.DENY))
Create a client¶
from affinity import Affinity
client = Affinity(api_key="your-api-key")
Prefer the context manager to ensure resources are closed:
from affinity import Affinity
with Affinity(api_key="your-api-key") as client:
...
Make your first request¶
This snippet covers authentication, a first request, and common failures:
from affinity import Affinity
from affinity.exceptions import AuthenticationError, RateLimitError
try:
with Affinity.from_env() as client:
me = client.whoami()
print(f"Authenticated as: {me.user.email}")
except AuthenticationError:
print("Check AFFINITY_API_KEY is set correctly")
except RateLimitError as e:
print(f"Rate limited. Retry after: {e.retry_after}")
Resolve a list by name¶
If you have a list name from configuration (and not a ListId), you can resolve it:
from affinity import Affinity
from affinity.types import ListType
with Affinity.from_env() as client:
pipeline = client.lists.resolve(name="Deal Pipeline", list_type=ListType.OPPORTUNITY)
if pipeline is None:
raise ValueError("List not found")
for entry in client.lists.entries(pipeline.id).all():
...
SDK-specific gotchas
- Use typed IDs (e.g.,
CompanyId(123)) instead of raw integers. - Entity
fieldsare only present when requested viafield_idsorfield_typesparameters. Theentity.fields.requestedboolean indicates whether field data was actually fetched (True) or omitted (False). WhenTrue,entity.fields.datacontains the field values (which may be empty if the entity has no field values). UseFieldResolverto look up values by field name instead of raw IDs — see Field Lookup Patterns. - Some write operations still route to V1; see the V1 vs V2 routing guide.
Sync vs async¶
- Use
Affinityfor synchronous code. - Use
AsyncAffinityfor async/await code.
See Sync vs async.
Next steps¶
- Authentication
- Examples
- Pagination
- Filtering
- CSV Export (CLI)
- Errors & retries
- Configuration
- Field types & values
- API versions & routing
- AI Integrations - MCP Server & Claude Code plugins
- API reference