Apideck Python SDK Skill
Overview
The Apideck Unified API provides a single integration layer to connect with 200+ third-party services across accounting, CRM, HRIS, file storage, ATS, e-commerce, and more. The official Python SDK (apideck-unify) provides typed clients for all unified APIs.
Installation
pip install apideck-unify
Requires Python 3.9+. Dependencies: httpx, pydantic.
IMPORTANT RULES
- ALWAYS use the
apideck-unify SDK. DO NOT make raw httpx/requests calls to the Apideck API.
- ALWAYS pass
api_key, app_id, and consumer_id when initializing the client.
- ALWAYS set the
APIDECK_API_KEY environment variable rather than hardcoding API keys.
- USE
service_id to specify which downstream connector to use (e.g., "salesforce", "quickbooks"). If a consumer has multiple connections for an API, service_id is required.
- USE context managers (
with / async with) for client lifecycle management.
- USE the
fields parameter to request only the columns you need.
- USE the
filter_ parameter (note the trailing underscore) to narrow results server-side.
- ALWAYS handle errors with try/except using
models.ApideckError as the base class.
Quick Start
from apideck_unify import Apideck
import os
with Apideck(
api_key=os.getenv("APIDECK_API_KEY", ""),
app_id="your-app-id",
consumer_id="your-consumer-id",
) as apideck:
res = apideck.crm.contacts.list(
service_id="salesforce",
limit=20,
filter_={"email": "john@example.com"},
)
while res is not None:
for contact in res.data:
print(contact.name, contact.emails)
res = res.next()
SDK Patterns
Client Setup
from apideck_unify import Apideck
import os
with Apideck(
api_key=os.getenv("APIDECK_API_KEY", ""),
app_id="your-app-id",
consumer_id="your-consumer-id",
) as apideck:
# Make API calls here
pass
The consumer_id identifies the end-user whose connections are being used. In multi-tenant apps, set this per-request or per-user session.
CRUD Operations
All resources follow the same pattern: apideck.{api}.{resource}.{operation}().
import apideck_unify
from apideck_unify import Apideck
import os
with Apideck(
api_key=os.getenv("APIDECK_API_KEY", ""),
app_id="your-app-id",
consumer_id="your-consumer-id",
) as apideck:
# LIST - retrieve multiple records
res = apideck.crm.contacts.list(
service_id="salesforce",
limit=20,
filter_={"email": "john@example.com", "company_id": "12345"},
sort={"by": apideck_unify.ContactsSortBy.CREATED_AT, "direction": apideck_unify.SortDirection.DESC},
fields="id,name,email",
)
# CREATE - create a new record
res = apideck.crm.contacts.create(
service_id="salesforce",
first_name="John",
last_name="Doe",
emails=[{"email": "john@example.com", "type": apideck_unify.EmailType.PRIMARY}],
phone_numbers=[{"number": "+1234567890", "type": apideck_unify.PhoneNumberType.PRIMARY}],
)
print(res.create_contact_response)
# GET - retrieve a single record
res = apideck.crm.contacts.get(id="contact_123", service_id="salesforce")
# UPDATE - modify an existing record
res = apideck.crm.contacts.update(id="contact_123", service_id="salesforce", first_name="Jane")
# DELETE - remove a record
res = apideck.crm.contacts.delete(id="contact_123", service_id="salesforce")
Pagination
Use the .next() method on response objects for cursor-based pagination:
res = apideck.accounting.invoices.list(service_id="quickbooks", limit=50)
while res is not None:
for invoice in res.data:
print(invoice.number, invoice.total)
res = res.next()
Async Support
Every sync method has an _async counterpart. Use async with as context manager:
import asyncio
from apideck_unify import Apideck
import os
async def main():
async with Apideck(
api_key=os.getenv("APIDECK_API_KEY", ""),
app_id="your-app-id",
consumer_id="your-consumer-id",
) as apideck:
res = await apideck.crm.contacts.list_async(
service_id="salesforce",
limit=20,
)
while res is not None:
for contact in res.data:
print(contact.name)
res = res.next()
asyncio.run(main())
Error Handling
from apideck_unify import Apideck, models
try:
res = apideck.crm.contacts.get(id="invalid", service_id="salesforce")
except models.BadRequestResponse as e:
print("Bad request:", e.message, e.status_code)
except models.UnauthorizedResponse as e:
print("Invalid API key or missing credentials")
except models.NotFoundResponse as e:
print("Record not found")
except models.PaymentRequiredResponse as e:
print("API limit reached")
except models.UnprocessableResponse as e:
print("Validation error:", e.message)
except models.ApideckError as e:
print(f"API error {e.status_code}: {e.message}")
All exceptions inherit from models.ApideckError with properties: message, status_code, headers, body, raw_response.
Common Parameters
| Parameter |
Type |
Description |
service_id |
str |
Downstream connector ID (e.g., "quickbooks", "salesforce") |
limit |
int |
Max results per page (1-200, default 20) |
cursor |
str |
Pagination cursor from previous response |
filter_ |
dict |
Resource-specific filter criteria (note trailing underscore) |
sort |
dict |
{"by": SortField, "direction": SortDirection} |
fields |
str |
Comma-separated field names to return |
pass_through |
dict |
Pass-through query parameters for the downstream API |
raw |
bool |
Include raw downstream response when True |
retry_config |
RetryConfig |
Per-call retry override |
Pass-Through Parameters
# Query pass-through
res = apideck.accounting.invoices.list(
service_id="quickbooks",
pass_through={"search": "overdue"},
)
# Body pass-through for connector-specific fields
res = apideck.crm.contacts.create(
service_id="salesforce",
first_name="John",
last_name="Doe",
pass_through=[{
"service_id": "salesforce",
"operation_id": "contactsAdd",
"extend_object": {"custom_sf_field__c": "value"},
}],
)
Retry Configuration
from apideck_unify import Apideck
from apideck_unify.utils import BackoffStrategy, RetryConfig
with Apideck(
api_key=os.getenv("APIDECK_API_KEY", ""),
app_id="your-app-id",
consumer_id="your-consumer-id",
retry_config=RetryConfig("backoff", BackoffStrategy(1, 50, 1.1, 100), False),
) as apideck:
pass
API Namespaces
| Namespace |
Resources |
apideck.accounting.* |
invoices, bills, payments, customers, suppliers, ledger_accounts, journal_entries, tax_rates, credit_notes, purchase_orders, balance_sheet, profit_and_loss, expenses, attachments, and more |
apideck.crm.* |
contacts, companies, leads, opportunities, activities, notes, pipelines, users |
apideck.hris.* |
employees, companies, departments, payrolls, time_off_requests |
apideck.file_storage.* |
files, folders, drives, shared_links, upload_sessions |
apideck.ats.* |
applicants, applications, jobs |
apideck.vault.* |
connections, consumers, sessions, custom_mappings, logs |
apideck.webhook.* |
webhooks, event_logs |
1---2name: apideck-python3description: Apideck Unified API integration patterns for Python. Use when building integrations with accounting software (QuickBooks, Xero, NetSuite), CRMs (Salesforce, HubSpot, Pipedrive), HRIS platforms (Workday, BambooHR), file storage (Google Drive, Dropbox, Box), ATS systems (Greenhouse, Lever), e-commerce, or any of Apideck's 200+ connectors using Python. Covers the apideck-unify SDK, authentication, CRUD operations, pagination, filtering, async support, and Vault connection management.4license: Apache-2.05---67# Apideck Python SDK Skill89## Overview1011The [Apideck Unified API](https://apideck.com) provides a single integration layer to connect with 200+ third-party services across accounting, CRM, HRIS, file storage, ATS, e-commerce, and more. The official Python SDK (`apideck-unify`) provides typed clients for all unified APIs.1213## Installation1415```sh16pip install apideck-unify17```1819Requires Python 3.9+. Dependencies: `httpx`, `pydantic`.2021## IMPORTANT RULES2223- ALWAYS use the `apideck-unify` SDK. DO NOT make raw `httpx`/`requests` calls to the Apideck API.24- ALWAYS pass `api_key`, `app_id`, and `consumer_id` when initializing the client.25- ALWAYS set the `APIDECK_API_KEY` environment variable rather than hardcoding API keys.26- USE `service_id` to specify which downstream connector to use (e.g., `"salesforce"`, `"quickbooks"`). If a consumer has multiple connections for an API, `service_id` is required.27- USE context managers (`with` / `async with`) for client lifecycle management.28- USE the `fields` parameter to request only the columns you need.29- USE the `filter_` parameter (note the trailing underscore) to narrow results server-side.30- ALWAYS handle errors with try/except using `models.ApideckError` as the base class.3132## Quick Start3334```python35from apideck_unify import Apideck36import os3738with Apideck(39 api_key=os.getenv("APIDECK_API_KEY", ""),40 app_id="your-app-id",41 consumer_id="your-consumer-id",42) as apideck:43 res = apideck.crm.contacts.list(44 service_id="salesforce",45 limit=20,46 filter_={"email": "john@example.com"},47 )48 while res is not None:49 for contact in res.data:50 print(contact.name, contact.emails)51 res = res.next()52```5354## SDK Patterns5556### Client Setup5758```python59from apideck_unify import Apideck60import os6162with Apideck(63 api_key=os.getenv("APIDECK_API_KEY", ""),64 app_id="your-app-id",65 consumer_id="your-consumer-id",66) as apideck:67 # Make API calls here68 pass69```7071The `consumer_id` identifies the end-user whose connections are being used. In multi-tenant apps, set this per-request or per-user session.7273### CRUD Operations7475All resources follow the same pattern: `apideck.{api}.{resource}.{operation}()`.7677```python78import apideck_unify79from apideck_unify import Apideck80import os8182with Apideck(83 api_key=os.getenv("APIDECK_API_KEY", ""),84 app_id="your-app-id",85 consumer_id="your-consumer-id",86) as apideck:8788 # LIST - retrieve multiple records89 res = apideck.crm.contacts.list(90 service_id="salesforce",91 limit=20,92 filter_={"email": "john@example.com", "company_id": "12345"},93 sort={"by": apideck_unify.ContactsSortBy.CREATED_AT, "direction": apideck_unify.SortDirection.DESC},94 fields="id,name,email",95 )9697 # CREATE - create a new record98 res = apideck.crm.contacts.create(99 service_id="salesforce",100 first_name="John",101 last_name="Doe",102 emails=[{"email": "john@example.com", "type": apideck_unify.EmailType.PRIMARY}],103 phone_numbers=[{"number": "+1234567890", "type": apideck_unify.PhoneNumberType.PRIMARY}],104 )105 print(res.create_contact_response)106107 # GET - retrieve a single record108 res = apideck.crm.contacts.get(id="contact_123", service_id="salesforce")109110 # UPDATE - modify an existing record111 res = apideck.crm.contacts.update(id="contact_123", service_id="salesforce", first_name="Jane")112113 # DELETE - remove a record114 res = apideck.crm.contacts.delete(id="contact_123", service_id="salesforce")115```116117### Pagination118119Use the `.next()` method on response objects for cursor-based pagination:120121```python122res = apideck.accounting.invoices.list(service_id="quickbooks", limit=50)123124while res is not None:125 for invoice in res.data:126 print(invoice.number, invoice.total)127 res = res.next()128```129130### Async Support131132Every sync method has an `_async` counterpart. Use `async with` as context manager:133134```python135import asyncio136from apideck_unify import Apideck137import os138139async def main():140 async with Apideck(141 api_key=os.getenv("APIDECK_API_KEY", ""),142 app_id="your-app-id",143 consumer_id="your-consumer-id",144 ) as apideck:145 res = await apideck.crm.contacts.list_async(146 service_id="salesforce",147 limit=20,148 )149 while res is not None:150 for contact in res.data:151 print(contact.name)152 res = res.next()153154asyncio.run(main())155```156157### Error Handling158159```python160from apideck_unify import Apideck, models161162try:163 res = apideck.crm.contacts.get(id="invalid", service_id="salesforce")164except models.BadRequestResponse as e:165 print("Bad request:", e.message, e.status_code)166except models.UnauthorizedResponse as e:167 print("Invalid API key or missing credentials")168except models.NotFoundResponse as e:169 print("Record not found")170except models.PaymentRequiredResponse as e:171 print("API limit reached")172except models.UnprocessableResponse as e:173 print("Validation error:", e.message)174except models.ApideckError as e:175 print(f"API error {e.status_code}: {e.message}")176```177178All exceptions inherit from `models.ApideckError` with properties: `message`, `status_code`, `headers`, `body`, `raw_response`.179180### Common Parameters181182| Parameter | Type | Description |183|-----------|------|-------------|184| `service_id` | `str` | Downstream connector ID (e.g., `"quickbooks"`, `"salesforce"`) |185| `limit` | `int` | Max results per page (1-200, default 20) |186| `cursor` | `str` | Pagination cursor from previous response |187| `filter_` | `dict` | Resource-specific filter criteria (note trailing underscore) |188| `sort` | `dict` | `{"by": SortField, "direction": SortDirection}` |189| `fields` | `str` | Comma-separated field names to return |190| `pass_through` | `dict` | Pass-through query parameters for the downstream API |191| `raw` | `bool` | Include raw downstream response when `True` |192| `retry_config` | `RetryConfig` | Per-call retry override |193194### Pass-Through Parameters195196```python197# Query pass-through198res = apideck.accounting.invoices.list(199 service_id="quickbooks",200 pass_through={"search": "overdue"},201)202203# Body pass-through for connector-specific fields204res = apideck.crm.contacts.create(205 service_id="salesforce",206 first_name="John",207 last_name="Doe",208 pass_through=[{209 "service_id": "salesforce",210 "operation_id": "contactsAdd",211 "extend_object": {"custom_sf_field__c": "value"},212 }],213)214```215216### Retry Configuration217218```python219from apideck_unify import Apideck220from apideck_unify.utils import BackoffStrategy, RetryConfig221222with Apideck(223 api_key=os.getenv("APIDECK_API_KEY", ""),224 app_id="your-app-id",225 consumer_id="your-consumer-id",226 retry_config=RetryConfig("backoff", BackoffStrategy(1, 50, 1.1, 100), False),227) as apideck:228 pass229```230231## API Namespaces232233| Namespace | Resources |234|-----------|-----------|235| `apideck.accounting.*` | invoices, bills, payments, customers, suppliers, ledger_accounts, journal_entries, tax_rates, credit_notes, purchase_orders, balance_sheet, profit_and_loss, expenses, attachments, and more |236| `apideck.crm.*` | contacts, companies, leads, opportunities, activities, notes, pipelines, users |237| `apideck.hris.*` | employees, companies, departments, payrolls, time_off_requests |238| `apideck.file_storage.*` | files, folders, drives, shared_links, upload_sessions |239| `apideck.ats.*` | applicants, applications, jobs |240| `apideck.vault.*` | connections, consumers, sessions, custom_mappings, logs |241| `apideck.webhook.*` | webhooks, event_logs |