Skip to main content

ServiceTrade Python SDK

A Python client for the ServiceTrade REST API with OAuth2 authentication and automatic token refresh.

Installation

pip install servicetrade

Quick Start

Using Client Credentials

from servicetrade import ServicetradeClient

client = ServicetradeClient(
    client_id="your-client-id",
    client_secret="your-client-secret"
)

# No need to call login() — the SDK authenticates lazily on the first API call.
jobs = client.get("/job")
print(jobs)

Using Refresh Token

from servicetrade import ServicetradeClient

client = ServicetradeClient(
    refresh_token="your-refresh-token"
)

jobs = client.get("/job")

Using a Pre-existing Token

from servicetrade import ServicetradeClient

client = ServicetradeClient(
    token="your-bearer-token",
    # Provide credentials for auto-refresh when the token expires
    client_id="your-client-id",
    client_secret="your-client-secret"
)

# No login needed if token is valid
jobs = client.get("/job")

API Methods

HTTP Methods

# GET request
result = client.get("/job/123")

# GET with query parameters
jobs = client.get("/job", params={"status": "scheduled", "locationId": 456})

# POST request
job = client.post("/job", {
    "type": "inspection",
    "description": "Quarterly HVAC Inspection",
    "locationId": 123,
    "vendorId": 456,
})

# PUT request
client.put("/job/123", {"description": "Updated Inspection Description"})

# DELETE request (returns None)
client.delete("/location/456")

Paginator

Iterate over all pages of a paginated endpoint automatically:

from servicetrade import Paginator, ServicetradeClient, ServicetradeAPIError

client = ServicetradeClient(
    client_id="your-client-id",
    client_secret="your-client-secret"
)

paginator = Paginator(client, "/job", "jobs", params={"status": "scheduled"})
try:
    for job in paginator:
        print(f"Job #{job['id']}: {job['description']}")
except ServicetradeAPIError as e:
    print(f"Error during pagination: {e.message}")

The Paginator constructor takes:

  • client — a ServicetradeClient instance
  • path — the API endpoint path (e.g., "/job")
  • items_key — the key in the response that contains the list of items (e.g., "jobs")
  • params — optional dict of query parameters to include on every request

File Attachments

from servicetrade import ServicetradeClient, FileAttachment

client = ServicetradeClient(client_id="your-client-id", client_secret="your-client-secret")

# Upload a file
file = FileAttachment(
    value=b"file contents here",
    filename="document.pdf",
    content_type="application/pdf"
)

result = client.attach(
    {"entityType": 3, "entityId": 123, "purposeId": 7},
    file
)

Reading from a file path

from pathlib import Path
from servicetrade import FileAttachment

file = FileAttachment(
    value=Path("/path/to/document.pdf"),
    filename="document.pdf",
    content_type="application/pdf"
)

Response Handling

Return values

  • get(), post(), put() return the data field from the response when present, or the full response dict/list otherwise.
  • delete() returns None.
  • When fetching a single resource by ID (e.g., /job/123), the response is a dict. When querying a collection (e.g., /job), the response may be a dict containing a list under a resource-specific key (e.g., "jobs").

Accessing the full response

Use get_last_response() to access status code, headers, and full body:

result = client.get("/job/123")
response = client.get_last_response()

print(response.status_code)   # 200
print(response.is_success())  # True
print(response.body)          # Full response body (dict)
print(response.headers)       # Response headers (dict)

Accessing the auth token

token = client.get_auth_token()

Configuration Options

client = ServicetradeClient(
    # API Configuration
    base_url="https://api.servicetrade.com",  # Default
    api_prefix="/api",                         # Default
    user_agent="My App/1.0",                   # Custom user agent

    # Authentication (pick one)
    client_id="your-client-id",
    client_secret="your-client-secret",
    # OR
    refresh_token="your-refresh-token",
    # OR
    token="your-bearer-token",

    # Options
    auto_refresh_auth=True,                    # Auto-refresh tokens (default)

    # Callbacks
    on_set_auth=lambda token: save_token(token),
    on_unset_auth=lambda: clear_token(),
)

Custom Headers

client.set_custom_header("X-Custom-Header", "value")

Error Handling

from servicetrade import (
    ServicetradeClient,
    ServicetradeAuthError,
    ServicetradeAPIError,
)

client = ServicetradeClient(client_id="your-client-id", client_secret="your-client-secret")

try:
    result = client.get("/nonexistent")
except ServicetradeAPIError as e:
    print(f"API error: {e.message}")
    print(f"Status code: {e.status_code}")
    print(f"Response data: {e.response_data}")

    # Structured errors from ServiceTrade API
    if e.error_messages:
        print(f"Errors: {e.error_messages}")
    if e.validation:
        print(f"Validation: {e.validation}")

Authentication Flow

The SDK supports two OAuth2 grant types, prioritized in this order:

  1. Refresh Token Grant — Most secure, only stores refresh token
  2. Client Credentials Grant — For service-to-service authentication

Lazy Authentication

The SDK automatically authenticates on the first API call if no token exists. You can also call login() explicitly for eager authentication:

client = ServicetradeClient(client_id="your-client-id", client_secret="your-client-secret")

# Eager authentication (optional)
client.login()

# Or just make API calls — login happens automatically
jobs = client.get("/job")

Automatic Token Refresh

By default, the SDK automatically refreshes tokens when:

  • A request returns a 401 Unauthorized response
  • The token is about to expire (within 5 minutes of expiry)

To disable automatic refresh:

client = ServicetradeClient(
    client_id="id",
    client_secret="secret",
    auto_refresh_auth=False
)

Development

Setup

# Clone the repository
git clone https://github.com/servicetrade/servicetrade-python-sdk.git
cd servicetrade-python-sdk

# Install development dependencies
pip install -e ".[dev]"

Running Tests

pytest

Type Checking

mypy src

License

MIT License - see LICENSE file for details.

Metadata

Release files for servicetrade 1.0.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 servicetrade 1.0.0
File Size Uploaded
servicetrade-1.0.0.tar.gz 9.8 kB Details

Built distribution (wheel)

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

Total release size: 21.1 kB

Release files / servicetrade-1.0.0.tar.gz

Download URL servicetrade-1.0.0.tar.gz
Size 9.8 kB
Tags Source
SHA-256 checksum
How to use checksums
708df3245209322e86749548592c4903799843946cb90ea2c96241da6e57b25f
BLAKE2b-256 checksum
How to use checksums
3c28d5a8694a46f379524abd73c0609284377fd0e8c08d07fe79eb4d4fe63f43
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Mar 23, 2026.

Transparency log

Release files / servicetrade-1.0.0-py3-none-any.whl

Download URL servicetrade-1.0.0-py3-none-any.whl
Size 11.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
02d93e85dd46af295797ea8a025d5d49a1e8894b62378623abd1e1f3334ca413
BLAKE2b-256 checksum
How to use checksums
6ae8a53fcfbc4a6f6ea4e408a064d845dded553ee817a6f18f1daca49f7c454c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

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 Mar 23, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

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