This release is a pre-release and may not be stable for production use.
factorial-api-client (Python)
Official Python SDK for the Factorial API.
Versioning
The SDK uses standard semver (MAJOR.MINOR.PATCH), independent of the Factorial API version date.
| SDK version | Factorial API version |
|---|---|
1.x.y |
2026-04-01 |
2.x.y |
2026-07-01 |
Factorial releases new API versions quarterly (Jan/Apr/Jul/Oct).
See the Factorial API versioning docs for details.
Installation
pip install factorial-api-client
Quick start
from factorial_api_client import FactorialClient
client = FactorialClient(api_key="YOUR_KEY")
# First page only
result = client.employees.employee.list()
employees = result.data
# Cursor-paginated iterator (sync)
for emp in client.employees.employee.paginate(max_items=100):
print(emp.full_name)
# Collect all pages into a list
all_employees = client.employees.employee.all()
# Async iterator
import asyncio
async def main():
async for emp in await client.employees.employee.paginate_async(max_items=100):
print(emp.full_name)
asyncio.run(main())
Authentication
Pass your API key via api_key= or an OAuth2 bearer token via token=:
# API key (sent as x-api-key header)
client = FactorialClient(api_key="YOUR_KEY")
# OAuth2 bearer token
client = FactorialClient(token="YOUR_BEARER_TOKEN")
Environment variables
When an argument is omitted, the client falls back to environment variables. Explicit arguments always take precedence.
| Variable | Maps to | Sent as |
|---|---|---|
FACTORIAL_API_KEY |
api_key |
x-api-key header |
FACTORIAL_TOKEN |
token |
Authorization: Bearer |
FACTORIAL_BASE_URL |
base_url |
— (defaults to https://api.factorialhr.com) |
# No arguments needed — reads FACTORIAL_API_KEY / FACTORIAL_TOKEN / FACTORIAL_BASE_URL
client = FactorialClient()
Error handling
The client fails loudly on non-2xx responses (bad/expired token, wrong base
URL, 4xx/5xx) instead of silently returning None. These raise
UnexpectedStatus:
from factorial_api_client.generated.errors import UnexpectedStatus
try:
employees = client.employees.employee.list()
except UnexpectedStatus as e:
print(e.status_code) # e.g. 401
print(e.content) # raw response body (bytes)
Domain namespaces
The client is organised as client.{domain}.{resource}.{method}().
| Domain | Example |
|---|---|
employees |
client.employees.employee.list() |
ats |
client.ats.application.list() |
attendance |
client.attendance.shift.list() |
timeoff |
client.timeoff.leave.list() |
contracts |
client.contracts.contract_version.list() |
payroll |
client.payroll.supplement.list() |
documents |
client.documents.document.list() |
performance |
client.performance.review_process.list() |
| ... | 36 domains total |
Available methods per resource: list, get, create, update, delete,
paginate, paginate_async, all, plus any custom action endpoints.
Pagination
All list endpoints support cursor-based pagination via paginate() / paginate_async() / all():
# Stop after 50 items
for emp in client.employees.employee.paginate(max_items=50):
...
# Collect everything (use carefully on large datasets)
all_leaves = client.timeoff.leave.all()
High-volume retrieval
Pages are capped at 100 items — a server-side hard max
(pagination docs); a larger
limit has no effect. Cursor pagination is sequential, so all() on a large
dataset issues one request per 100 records. For big pulls:
- Filter with the endpoint's query params (date ranges,
ids,employee_ids, …) instead of pulling everything. - Sync incrementally where
updated_at-style filters exist, and cache locally. - Split one large query into filtered sub-queries (date windows, id chunks) and
run them concurrently with
paginate_async()— faster wall-clock, same total request count, so mind rate limits. - Pass
max_itemsas a safety cap.
There is no server-side aggregation endpoint; compute totals client-side.
Webhooks
Manage subscriptions through the client, and type your handler payloads with the generated webhook catalog (re-exported from the package root).
from factorial_api_client import (
FactorialClient,
AtsApplicationCreateWebhook,
WEBHOOK_CATALOG,
WEBHOOK_PAYLOAD_TYPES,
)
client = FactorialClient(api_key="YOUR_KEY")
# Subscribe to an event. The `challenge` is a secret you choose; Factorial echoes
# it back in the `x-factorial-wh-challenge` header on every delivery so you can
# verify the request really came from Factorial.
client.api_public.webhook_subscription.create(body={
"subscription_type": "ats/application/create",
"target_url": "https://example.com/webhooks/factorial",
"company_id": 55,
"challenge": "a-random-secret-you-generate",
})
# Type a handler directly
def on_application_created(payload: AtsApplicationCreateWebhook) -> None:
print(payload.id)
# Look up the payload model class for a subscription_type at runtime
model_cls = WEBHOOK_PAYLOAD_TYPES["ats/application/create"]
print(len(WEBHOOK_CATALOG), "webhook events available")
Factorial delivers the resource object at the top level of the POST body (no
{type, data} envelope). A full event→payload reference and an SDK usage guide
for coding agents are available as a skill:
npx skills add https://github.com/factorialco/factorial-api-sdks --skill factorial-api-sdks
Release files for factorial-api-client 3.0.0b2026100127
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| factorial_api_client-3.0.0b2026100127.tar.gz | 584.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| factorial_api_client-3.0.0b2026100127-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 2.7 MB
Release files / factorial_api_client-3.0.0b2026100127.tar.gz
| Download URL | factorial_api_client-3.0.0b2026100127.tar.gz |
|---|---|
| Size | 584.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9555e14c16ccbdec14d67d3dbc310cbcfb36f249659880b20e25eddd34969a7b
|
|
BLAKE2b-256 checksum How to use checksums |
3ec553aa57129c639facf6ed30a57e510bab1f79f46c4f081e4496da4d6f937d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / factorial_api_client-3.0.0b2026100127-py3-none-any.whl
| Download URL | factorial_api_client-3.0.0b2026100127-py3-none-any.whl |
|---|---|
| Size | 2.1 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
84e77348ae49ce4328c25d92c6e458da3fbbedb539441b8c97ae29f87d711719
|
|
BLAKE2b-256 checksum How to use checksums |
33f316af504d2e88056c4af374cfca81a58bf2e71a63e14185ab8c9cbf8a0895
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|