Observed since 20 Oct, 19:48 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
A small python SDK for the ADP Workforce Now API
Minimal Python client for the ADP Workforce Now API using OAuth2 client credentials + mutual TLS (mTLS).
uv add adpapi
or
pip install adpapi
Provide credentials via environment variables (or a .env file):
CLIENT_ID=...
CLIENT_SECRET=...
CERT_PATH=certificate.pem
KEY_PATH=adp.key
import os
from dotenv import load_dotenv
from adpapi import AdpApiClient, AdpCredentials, FilterExpression
from adpapi.logger import configure_logging
# Optional helper: Configure logger with file handlers and stream handling
configure_logging()
load_dotenv()
# Decide which OData columnns are required from your pull
cols = [
"workers/person/legalName",
"workers/person/birthDate",
"workers/workAssignments/reportsTo",
"workers/associateOID",
"workers/businessCommunication/emails",
]
# Load API Credentials from environment
credentials = AdpCredentials.from_env()
# Define your API Client
with AdpApiClient(credentials) as api:
workers = api.call_endpoint(
endpoint="/hr/v2/workers",
select=cols,
masked=True, # set False to request unmasked fields if your tenant allows it
page_size=100, # ADP max
max_requests=1, # increase/remove for full exports
)
# Or call rest endpoints with path parameters
some_workers = api.call_rest_endpoint(
endpoint = '/hr/v1/workers/{aoid}',
aoid = ['0000000001', '0000000002', '0000000003', '0000000004'],
select = cols,
masked = False,
inject_path_params = True # Inject AOID into each response dict for downstream processing
)
Use FilterExpression to build OData $filter parameters. Pass filters to call_endpoint() using the filters parameter:
from adpapi.odata_filters import FilterExpression
# Simple equality
filter1 = FilterExpression.field("workers.status").eq("Active")
# Combine conditions with logical operators
filter2 = (
FilterExpression.field("workers.status").eq("Active")
& FilterExpression.field("workers.hireDate").ge("2020-01-01")
)
# Multiple values (IN operator)
filter3 = FilterExpression.field("workers.status").isin(["Active", "OnLeave", "Pending"])
# String search
filter4 = FilterExpression.field("workers.person.legalName.familyName").contains("Smith")
# Pass to API call
workers = api.call_endpoint(
endpoint="/hr/v2/workers",
filters=filter2,
select=cols,
masked=True,
)
Supported Operators:
eq, ne, gt, ge, lt, lecontains(), startswith(), endswith()& (and), | (or), ~ (not)isin([...])Notes:
workers.status) but convert to forward slashes in OData syntax (workers/status)filters="workers/status eq 'Active'"$top, $skip, $select) and stops on HTTP 204 (No Content).masked=False requests Accept: application/json;masked=false (subject to tenant permissions).app.log and to the console.Monofile.ipynbFor clients such as Microsoft Fabric, Azure Databricks, or other notebook-driven programming environments, running a single notebook with magic commands may be more efficient than creating a custom runtime with the pip version of the package. To allow for this, monofile.ipynb can simply be uploaded to the desired location and ran there.
Import Syntax Changes to
%run monofile.ipynb # Or whatever monofile has been renamed to in the notebook client
# Now, imports are no longer necessary and the top-level Api objects are exposed in the main namespace
configure_logging()
with AdpApiClient(...) as api:
api.call_endpoint(...)
Download counts and popularity will appear here as the collector receives them.
| Version | Published | Direct dependencies | Open version |
|---|---|---|---|
| 1.5.2 | 27 Mar 2026 | 1 |
This describes our collected data; it is not a security assessment.
Shared repository data · GitHub
Observed since 20 Oct, 19:48 UTC
No superseded metadata states collected. Unchanged observations do not create new revisions.
package:5e27f11c1c6e5916f8579065136932f24ae61ec9b45c722a7a92364a983243be