Skip to main content

CI PyPI Python versions License

Deltek Ajera Python client

A typed Python client and command-line interface for the Deltek Ajera API.

Ajera exposes a single JSON-RPC style endpoint; this package wraps it in an ergonomic, fully type-hinted client built on Pydantic models, plus an ajera CLI for quick access from the terminal. Responses are validated and normalized into predictable Python objects so you can work with employees, projects, vendors, invoices, and general-ledger data without hand-rolling request payloads.

Features

  • Typed models - every response is parsed into Pydantic models with descriptive fields.
  • Python client and CLI - use it as a library or straight from the shell via ajera.
  • Sync and async - AjeraClient and AsyncAjeraClient expose the same methods over httpx.
  • Sensible defaults - handles session tokens and per-method API versions for you.
  • Read and write - list, get, update, and create across the supported APIs.

Installation

The package is published on PyPI as ajera:

pip install ajera
# or, with uv:
uv add ajera

Requires Python 3.12+.

Configuration

Credentials are read from environment variables (or can be passed directly to AjeraClient):

Variable Description
AJERA_API_URL The Ajera API endpoint URL for your tenant.
AJERA_API_USERNAME API username.
AJERA_API_PASSWORD API password.
export AJERA_API_URL="https://ajera.com/V0000000/AjeraAPI.ashx?..."
export AJERA_API_USERNAME="your-username"
export AJERA_API_PASSWORD="your-password"

For setting up an API user and generating credentials, see the Deltek Ajera Learning Hub API docs.

Timeouts and retries

Every request carries a timeout (default (5, 30) seconds for connect and read) so a stalled connection can't hang the caller forever. Pass timeout= to override it (a single float, a (connect, read) tuple, or None to disable), and retries= to retry connection-establishment failures:

# Wait longer, and retry a dropped/stale connection up to 3 times.
client = AjeraClient(timeout=60, retries=3)

retries retries only the connection stage (before any bytes reach the server), which is safe for the non-idempotent writes this client performs: a create whose response is merely lost is never resubmitted. The CLI reads AJERA_API_TIMEOUT (seconds) and AJERA_API_RETRIES (count) for the same behavior.

Quick start

Python

from ajera import AjeraClient

# Reads AJERA_API_URL / AJERA_API_USERNAME / AJERA_API_PASSWORD from the
# environment, or pass url=, username=, password= explicitly.
client = AjeraClient()

for employee in client.list_employees():
    print(employee.employee_key, employee.first_name, employee.last_name)

Python (async)

AsyncAjeraClient mirrors AjeraClient method for method - same arguments, same return types, awaited. Share one instance across tasks so they reuse its connection pool and session token, and bound the fan-out with a semaphore (the API throttles at roughly 9 requests per second):

import asyncio

from ajera import AsyncAjeraClient


async def main() -> None:
    async with AsyncAjeraClient() as client:
        projects = await client.list_projects()
        limit = asyncio.Semaphore(5)

        async def totals(project_key: int):
            async with limit:
                return await client.get_project_totals(project_key)

        for total in await asyncio.gather(
            *(totals(project.project_key) for project in projects)
        ):
            print(total.project_key, total.totals)


asyncio.run(main())

Outside a context manager, call await client.aclose() when you're done with it.

CLI

$ ajera employees list
[
  {
    "employee_key": 42,
    "first_name": "John",
    "last_name": "Smith",
    ...
  },
  ...
]

Note: List commands backed by an active/inactive status return only active records by default. Pass --status to override - e.g. --status Inactive, or --status Active --status Inactive to include both.

Reference Documentation

This client adheres (to the extent possible) to the API documentation provided by Deltek Ajera, which can be found at:

https://help.deltek.com/product/Ajera/api/index.html

API reference

Each section below maps a CLI command group to the Ajera API(s) it is built on. The Python client exposes the same operations as client.<method>() (e.g. client.list_employees(), client.get_projects(...)), and AsyncAjeraClient exposes every one of them as a coroutine of the same name.

Employees

Docs: Employees API · List Methods API

pays, payroll-taxes, and wage-tables come from the List Methods API; the rest come from the Employees API.

Command Description
ajera employees list List employees.
ajera employees get <key>... Get one or more employees by key.
ajera employees update <key> [options] Update simple fields on one employee.
ajera employees types List employee types.
ajera employees deductions List deductions.
ajera employees fringes List fringes.
ajera employees pays List pay types.
ajera employees payroll-taxes List payroll taxes.
ajera employees wage-tables List wage tables.

Note: employees list returns company_key and department_key as bare integers - the API attaches no names to them. To group employees by department, join department_key against the department_key of client.list_departments() (ajera departments).

Clients

Docs: Clients API

Command Description
ajera clients list List clients.
ajera clients get <key>... Get one or more clients by key.
ajera clients update <key> [options] Update simple fields on one client.
ajera clients types List client types.

Contacts

Docs: Contacts API

Command Description
ajera contacts list List contacts.
ajera contacts get <key>... Get one or more contacts by key.
ajera contacts update <key> [options] Update simple fields on one contact.
ajera contacts types List contact types.

Vendors

Docs: Vendors API · Vendor Invoices API (v2)

The invoices subcommands come from the Vendor Invoices (v2) API; the rest come from the Vendors API.

Command Description
ajera vendors list List vendors.
ajera vendors get <key>... Get one or more vendors by key.
ajera vendors update <key> [options] Update simple fields on one vendor.
ajera vendors types List vendor types.
ajera vendors invoices list List vendor invoices, optionally filtered.
ajera vendors invoices get <key>... Get one or more vendor invoices, with their line items.
ajera vendors invoices create [options] Create a vendor invoice with a single line item.

Ajera reports no payment property on a vendor invoice: paid, unpaid, and voided exist only as list filters. Passing --with-payment-status (or with_payment_status=True to list_vendor_invoices) derives it and fills in each invoice's payment field with Paid, Unpaid, or Voided. It costs one extra request, so the field stays null unless you ask for it:

for invoice in client.list_vendor_invoices(with_payment_status=True):
    print(invoice.vendor_invoice_key, invoice.payment)

Projects

Docs: Projects API (v2) · Projects API (v1) · List Methods API

list, get, update, and create use the v2 Projects API; totals, types, and templates use the v1 Projects API; chargeable-phases comes from the List Methods API.

Command Description
ajera projects list List projects, optionally filtered.
ajera projects get <key>... Get one or more projects by key.
ajera projects create <description> [options] Create a new project.
ajera projects update <key> [options] Update simple fields on one project.
ajera projects totals <key> Get a project's financial totals.
ajera projects types List project types.
ajera projects templates list List project templates, optionally filtered.
ajera projects templates get <key>... Get one or more project templates by key.
ajera projects chargeable-phases <project-key> List the chargeable phases of a project.

General Ledger

Docs: GL Accounts API · List Methods API

account-groups comes from the List Methods API; list and get come from the GL Accounts API.

Command Description
ajera ledger list List general ledger accounts.
ajera ledger get [id]... Get general ledger account details, with calculated amounts.
ajera ledger account-groups List general ledger account groups.

Reference lists

Docs: List Methods API

Lightweight lookup lists. (Other List Methods endpoints are grouped with their domain - see employees, ledger, and projects above.)

Command Description
ajera activities List activities.
ajera bank-accounts List bank accounts.
ajera companies List companies.
ajera departments List departments.
ajera invoice-formats List invoice formats.
ajera rate-tables List rate tables.

Release files for ajera 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for ajera 0.3.0
File Size Uploaded
ajera-0.3.0.tar.gz 66.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for ajera 0.3.0
File Interpreter ABI Platform
ajera-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 160.9 kB

Release files / ajera-0.3.0.tar.gz

Download URL ajera-0.3.0.tar.gz
Size 66.9 kB
Tags Source
SHA-256 checksum
How to use checksums
26be93ce8583fad0132762209563f8b9bb7f0501e4a9f8e3f98f049c9aa73340
BLAKE2b-256 checksum
How to use checksums
9e262c1ac08f337b5d8ffffb13213eb6a8f4b15a328abbb033edcc7bbeb09ca9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release files / ajera-0.3.0-py3-none-any.whl

Download URL ajera-0.3.0-py3-none-any.whl
Size 94.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
14acd68eee94914a945bccaa37af25ea5575e56a6be0664c721d6319f77bbf2c
BLAKE2b-256 checksum
How to use checksums
67b310dab7499a76f6c13bf1894410857f28bfdc4f2e5ed6cd564201b00d9aa4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Aug 2, 2026.

Transparency log

Release history Release notifications | RSS feed

0.5.0

2 release files

0.4.0

2 release files

This release

0.3.0 This release

2 release files

0.2.0

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.4

2 release files

0.1.3

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page