Skip to main content

A production-ready Python SDK for the Procore REST API.

Project description

PyProcore

A production-ready Python SDK for building automation, integrations, and AI workflows on top of the Procore REST API.

PyPI Python License Tests Coverage

PyProcore handles the parts of a Procore integration that are tedious and easy to get wrong — OAuth, token refresh, pagination, retries, typed responses, structured logging, and attachment downloads — so you work with Python objects instead of raw JSON and API plumbing.


Why PyProcore

Calling the Procore REST API directly means managing the OAuth handshake, refreshing expired tokens, following pagination headers, retrying failed requests, and parsing untyped JSON on every call.

PyProcore does that once, correctly, behind a clean interface. You call a service method and get back a typed Pydantic object. It is designed as the foundation layer for higher-level tools built on Procore data: engineering assistants, document analysis, workflow automation, and AI-powered review.


Features

Authentication and transport

  • OAuth 2.0 authorization-code flow
  • Automatic access-token refresh
  • Automatic pagination via Procore response headers
  • Request retries and structured logging with secret redaction

API coverage (v1.0)

  • Companies
  • Projects
  • RFIs
  • Submittals
  • Attachment downloads

Developer experience

  • Typed Pydantic response models
  • Command-line interface
  • 73 unit tests at 94% coverage, mocked with no live Procore dependency

Architecture

Package Responsibility
pyprocore/auth/ OAuth exchange, token persistence, token refresh
pyprocore/core/ Configuration, endpoint paths, HTTP client, logging, exceptions
pyprocore/models/ Pydantic response models
pyprocore/services/ Company, project, RFI, submittal, and file services
pyprocore/parser/ Email parsing utilities for future automation
tests/ Mocked unit tests with no live Procore dependency

Installation

Requires Python 3.12+.

pip3 install pyprocore

For local development:

git clone https://github.com/vibhanshu-mishra/pyprocore.git
cd pyprocore
python3 -m venv .venv
.venv/bin/python -m pip install --upgrade pip
.venv/bin/python -m pip install -e .

Quick Example

from pyprocore.services import list_projects

projects = list_projects(company_id=123456)

for project in projects:
    print(project.name)

Configuration

Copy the example file and fill in real values:

cp .env.example .env

Required variables:

PROCORE_CLIENT_ID=your_client_id
PROCORE_CLIENT_SECRET=your_client_secret
PROCORE_REDIRECT_URI=http://localhost:8080/callback
PROCORE_LOGIN_URL=https://login.procore.com
PROCORE_API_BASE=https://api.procore.com
PROCORE_COMPANY_ID=123456

Secrets, tokens, URLs, and company IDs are never hardcoded in source.


Authentication

Exchange the first authorization code and save the token locally:

from pyprocore.auth.oauth import exchange_authorization_code
from pyprocore.auth.token_manager import TokenManager

token_response = exchange_authorization_code("authorization-code-from-procore")
TokenManager().save_oauth_response(token_response)

After that, SDK clients read the token automatically:

from pyprocore.auth.token_manager import get_access_token

access_token = get_access_token()

Expired access tokens refresh automatically whenever a refresh token is available.


Usage

Once .env is configured and the one-time OAuth exchange is complete, calls return typed objects:

from pyprocore.services import list_projects

for project in list_projects(company_id=123456):
    print(project.name)

Full service surface:

from pyprocore.services import (
    download_rfi_attachments,
    download_submittal_attachments,
    get_rfi,
    get_submittal,
    list_companies,
    list_projects,
    list_rfis,
    list_submittals,
)

companies = list_companies()
projects = list_projects(company_id=123456)

rfis = list_rfis(project_id=352338)
rfi = get_rfi(project_id=352338, rfi_id=102784)
first_attachment_url = rfi.questions[0].attachments[0].url

submittals = list_submittals(project_id=352338)
submittal = get_submittal(project_id=352338, submittal_id=309641)

Every typed model serializes back to JSON:

json_payload = rfi.model_dump(mode="json")
json_string = rfi.model_dump_json()

Downloading Attachments

Attachment URLs live at:

RFI:        questions[].attachments[].url
Submittal:  attachments[].url

Download through the service functions:

rfi_files = download_rfi_attachments(project_id=352338, rfi_id=102784)
submittal_files = download_submittal_attachments(
    project_id=352338,
    submittal_id=309641,
)

The shared file service supports safe filenames, streaming writes, retries, progress logging, batch downloads, and skip-existing behavior by default:

from pyprocore.services.files import FileDownloadService

files = FileDownloadService().download_attachments(
    attachments,
    "downloads/custom",
    fallback_prefix="attachment",
    overwrite=False,
)

CLI

procore-sdk companies
procore-sdk projects
procore-sdk rfis --project 352338
procore-sdk rfi --project 352338 --id 102784
procore-sdk submittals --project 352338
procore-sdk submittal --project 352338 --id 309641
procore-sdk download-rfi --project 352338 --id 102784
procore-sdk download-submittal --project 352338 --id 309641

The CLI prints formatted JSON. Typed models are serialized with model_dump(mode="json").


Pagination

Collection methods use ProcoreClient.get_all(), which follows Procore pagination headers automatically. Business logic should call the service method or get_all() directly and never request page 2 by hand.


Logging

Structured logs are written to:

logs/sdk.log
logs/errors.log

Request logs record method, endpoint, status, elapsed time, and retry count. Exception logs record stack traces, exception type, request URL, HTTP status, and response body when available. The logger redacts sensitive keys such as authorization headers, access tokens, refresh tokens, and client secrets.


Implemented Endpoints (v1.0)

GET /rest/v1.0/companies
GET /rest/v1.0/companies/{company_id}/projects
GET /rest/v1.1/projects/{project_id}/rfis
GET /rest/v1.1/projects/{project_id}/rfis/{rfi_id}
GET /rest/v1.1/projects/{project_id}/submittals
GET /rest/v1.1/projects/{project_id}/submittals/{submittal_id}

Roadmap

Planned endpoints

  • Drawings
  • Documents
  • Specifications
  • Daily Logs
  • Photos
  • Correspondence
  • Observations

Planned capabilities

  • AI workflow examples built on the SDK

Troubleshooting

Error Likely cause and fix
ConfigurationError .env is missing or a required key is absent.
AuthenticationError Complete the first OAuth code exchange; confirm pyprocore/auth/token_store.json holds a refresh token.
AuthorizationError The Procore user lacks access to the target company, project, or resource.
ResourceNotFoundError Project, RFI, or submittal ID is wrong for the configured company.
Attachments not downloading Check logs/errors.log for HTTP status and response body. Existing files are skipped unless overwrite=True.

Tests

Run unit tests:

.venv/bin/python -m unittest discover -s tests

Run coverage:

.venv/bin/python -m coverage run -m unittest discover -s tests
.venv/bin/python -m coverage report

Supported Environments

  • Procore Production
  • Procore Sandbox

Contributing

Contributions, issues, and feature requests are welcome. Please open an issue before submitting large changes.


License

Released under the MIT License. See LICENSE for details.


Disclaimer — PyProcore is an independent open-source project and is not affiliated with or endorsed by Procore Technologies.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyprocore-1.0.1.tar.gz (37.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyprocore-1.0.1-py3-none-any.whl (32.5 kB view details)

Uploaded Python 3

File details

Details for the file pyprocore-1.0.1.tar.gz.

File metadata

  • Download URL: pyprocore-1.0.1.tar.gz
  • Upload date:
  • Size: 37.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for pyprocore-1.0.1.tar.gz
Algorithm Hash digest
SHA256 7b4b41de18f43c39031ae538d1a6036def8a6b0c0506ec73c20cae65f1af25af
MD5 c9738487d2fe788a4479c154b4ae894b
BLAKE2b-256 0e71a411ab7ee3b7f41eb42f639808440d4a33246034be7624af5f93c549ad95

See more details on using hashes here.

File details

Details for the file pyprocore-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: pyprocore-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 32.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.6

File hashes

Hashes for pyprocore-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 805bed09cec798bb29afc04a9c9f4ff4c142d268051d8533b95b456f69e8912e
MD5 a6359e504fb4cc3bd707ce2657a18e22
BLAKE2b-256 18ee975aff10b97a472e9a9900e4dcb4f05145ad103d3104e06babfff8be9785

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page