Skip to main content

pypresscart — A Python Library for the Presscart API

CI Docs PyPI Python License: MIT Ruff Checked with mypy Types - Pydantic pre-commit

📚 Documentation · PyPI · Issues

⚠️ Unofficial library. This project is not affiliated with, endorsed by, or supported by Presscart. It's a community-maintained Python client for the Presscart API.

  • Pythonic, typed resource methods (client.outlets.list(...), client.orders.create_checkout(...), ...)
  • Dual-mode: pass Pydantic models or plain dicts; get Pydantic models or plain dicts back
  • Single dependency stack — requests + pydantic
  • Built-in exponential-backoff retry for 429 / 5xx responses, honoring Retry-After
  • Typed exception hierarchy (BadRequestError, AuthenticationError, RateLimitError, ...)

Install

uv add pypresscart
# or
pip install pypresscart

Python 3.10+.

Quickstart

from pypresscart import PresscartClient

client = PresscartClient(api_token="pc_...")

me = client.auth.whoami()
print(me.team_id, me.scopes)

outlets = client.outlets.list(limit=5, filters={"country": "United States"})
for row in outlets.records:
    print(row.outlet_name, row.website_url)

Dual-mode usage

The library accepts both Pydantic models and raw dicts on the way in, and returns either on the way out.

from pypresscart import CheckoutLineItem, CheckoutRequest, PresscartClient

client = PresscartClient(api_token="pc_...")

# Pydantic in, Pydantic out (default)
order = client.orders.create_checkout(
    CheckoutRequest(
        profile_id="YOUR_PROFILE_ID",
        line_items=[CheckoutLineItem(product_id="YOUR_PRODUCT_ID", quantity=1)],
    )
)
print(order.reference_number)

# Dict in, dict out
raw = client.orders.create_checkout(
    {
        "profile_id": "YOUR_PROFILE_ID",
        "line_items": [
            {"product_id": "YOUR_PRODUCT_ID", "quantity": 1, "is_add_on": False}
        ],
        "discount": 0,
    },
    as_json=True,
)
print(raw["reference_number"])

You can also set the default for the whole client:

client = PresscartClient(api_token="pc_...", response_mode="json")

Per-call as_json=True / as_json=False always wins over the client default.

Error handling

from pypresscart import (
    AuthenticationError,
    NotFoundError,
    PresscartAPIError,
    RateLimitError,
    ValidationError,
)

try:
    client.campaigns.get("missing")
except NotFoundError as exc:
    print("not found:", exc.message)
except AuthenticationError:
    print("token invalid or expired")
except RateLimitError as exc:
    print("slow down; retry after", exc.retry_after)
except ValidationError as exc:
    for issue in exc.issues:
        print("field", issue["path"], issue["message"])
except PresscartAPIError as exc:
    print(exc.status_code, exc.name, exc.message, exc.payload)

All API errors inherit from PresscartAPIError. Network-level failures raise PresscartTransportError (wrapping the underlying requests exception).

Client options

Parameter Default Description
api_token (required) Bearer token (pc_...)
base_url https://api.presscart.com API origin
timeout 30.0 Per-request timeout (seconds)
max_retries 3 Additional attempts on 429/5xx/network errors
retry_backoff_base 0.25 Base backoff seconds
retry_backoff_max 4.0 Backoff cap
retry_jitter 0.1 Fractional jitter
response_mode "pydantic" "pydantic" or "json"
user_agent "pypresscart/<version>" User-Agent header
session None Inject a pre-configured requests.Session

Use as a context manager to close the session automatically:

with PresscartClient(api_token="pc_...") as client:
    ...

Endpoints

Every endpoint documented at docs.presscart.com is covered by a method on one of the resource services:

Service Methods
client.auth whoami
client.outlets list, get, list_products, list_countries, list_states, list_cities, list_tags, list_disclaimers
client.products get, list_listings, list_categories
client.orders list, get, create_checkout
client.order_items list
client.profiles list_team_profiles, list_orders, list_order_items, list_campaigns
client.campaigns list, get, create, update, list_articles, article_status_counts, assign_order_items, link_questionnaire
client.articles get, update, approve_brief, approve_draft
client.files list, get, upload, download, move, delete
client.folders list, create, rename, delete

Each method's docstring includes the required token scope.

Documentation

Full documentation lives at https://pypresscart.github.io/py-presscart/ — Sphinx site built from docs/ and deployed automatically by .github/workflows/docs.yml on every push to main.

Build locally:

uv sync --group docs
uv run sphinx-build -b html -n -W --keep-going docs docs/_build/html
open docs/_build/html/index.html

# Or with live reload:
uv run sphinx-autobuild docs docs/_build/html --watch src/pypresscart

Development

uv sync --group dev
uv run pytest -v
uv run ruff check .
uv run ruff format --check .
uv run mypy src
uv build

Pre-commit hooks

This repo ships a .pre-commit-config.yaml with ruff, mypy, whitespace/EOL/yaml/toml checks, and a pytest hook on pre-push.

uv run pre-commit install                      # ruff + mypy on every commit
uv run pre-commit install --hook-type pre-push # pytest on every push
uv run pre-commit run --all-files              # run everything now

Branch protection

main is protected — no direct pushes. Every change goes through a pull request that must pass CI (ruff, ruff format, mypy, pytest on Python 3.10 through 3.13, and the distribution build) before it can merge. Force-pushes and branch deletion are denied.

Releasing

Releases are cut by a human via the Release GitHub Actions workflow, which uses OIDC trusted publishing — no PyPI tokens are stored anywhere.

Prerequisites (one-time)

  • Project pypresscart registered as a trusted publisher on both PyPI and TestPyPI, pointing at this repo + workflow release.yml + environments pypi / testpypi.
  • GitHub repo environments pypi and testpypi exist. Optionally add required reviewers on pypi if you want a human to approve every prod publish.

Cutting a new release

  1. Decide the version. Follow SemVer:

    • MAJOR — anything that could break existing callers (renames, removed fields, changed defaults, raised minimum Python).
    • MINOR — backwards-compatible additions (new endpoints, new optional parameters).
    • PATCH — bug fixes only.
  2. Open a bump PR. On a branch, update both:

    • src/pypresscart/_version.py — __version__ = "X.Y.Z"
    • pyproject.toml — [project].version = "X.Y.Z"

    Also add an entry to docs/changelog.md under ## [Unreleased] and roll it into a new ## [X.Y.Z] — <date> heading.

  3. Merge the PR. Wait for CI to go green, then merge. The docs site rebuilds and picks up the new version string automatically.

  4. Publish to TestPyPI first. Actions → Release → Run workflow → pick testpypi → run. Wait for green.

  5. Smoke-test from TestPyPI in a throwaway environment:

    uv run --no-project \
        --with pypresscart==X.Y.Z \
        --index-url https://test.pypi.org/simple/ \
        --extra-index-url https://pypi.org/simple/ \
        python -c "from pypresscart import PresscartClient, __version__; print(__version__)"
    
  6. Publish to PyPI. Actions → Release → Run workflow → pick pypi → run. (If you added required reviewers on the pypi environment, approve the deployment when prompted.)

  7. Tag and announce.

    git tag -a vX.Y.Z -m "pypresscart X.Y.Z"
    git push origin vX.Y.Z
    
    gh release create vX.Y.Z \
        --title "pypresscart X.Y.Z" \
        --notes-file <(awk '/^## \[X\.Y\.Z\]/,/^## \[/' docs/changelog.md | sed '$d')
    

If something goes wrong

  • Published a broken version? You can't overwrite a PyPI release. Cut a new patch release (X.Y.Z+1) that reverts or fixes the issue. yank on PyPI hides the broken version from resolvers but keeps it available to pinned installs.
  • TestPyPI upload failed but PyPI didn't run? Re-dispatch with target=testpypi. The workflow has skip-existing: true on TestPyPI so re-uploading the same version is a no-op.
  • Trusted publisher rejected the OIDC token? Confirm the pending publisher on PyPI has the exact workflow filename (release.yml) and environment name (pypi / testpypi). One-character typos fail silently.

License

MIT — see LICENSE.

Presscart™ and the Presscart logo are trademarks of their respective owners. This library is an independent, unofficial client.

Metadata

Release files for pypresscart 0.1.8

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

Source distribution (sdist)

Source distribution for pypresscart 0.1.8
File Size Uploaded Provenance Actions
pypresscart-0.1.8.tar.gz 28.9 kB With provenance Details for pypresscart-0.1.8.tar.gz

Built distribution (wheel)

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

Total release size: 66.6 kB

Release files / pypresscart-0.1.8.tar.gz

Download URL pypresscart-0.1.8.tar.gz
Size 28.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1a9385416f5ccb3bcfb396f6f6a2a4c22d647f40522ef83d491503941eca2a3a
BLAKE2b-256 checksum
How to use checksums
ea0f6e60f41a6a628eb53ba4c06ed4338a01d906e80ebac16c0443d1dbb5ebf5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 12, 2026.

Transparency log

Release files / pypresscart-0.1.8-py3-none-any.whl

Download URL pypresscart-0.1.8-py3-none-any.whl
Size 37.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
77be08cb483d5a4abbc176ec92c0603ba942e7f7972d3a3e8746c3ef2f1e3a7c
BLAKE2b-256 checksum
How to use checksums
58468d17179903382cddb177cecb6964852c985297f504cd7f688dc67f96f6f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

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 Jun 12, 2026.

Transparency log

Security for pypresscart 0.1.8

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pypresscart-0.1.8.tar.gz

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 Jun 12, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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