simple-salesforce

A lightweight Python client for the Salesforce REST, Bulk, and Metadata APIs with four built-in authentication flows.

SDK
PyPI
v1.12.10
1,893stars
Apache License 2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
64/100Good
Development Activity36
Maintenance32
Community88
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
72/100Good
Architecture68
Code Quality75
Innovation70
Learning Curve75

Simple Salesforce is a lightweight Python client for Salesforce.com’s REST API, built to stay close to the wire rather than wrap it in a heavy ORM-style abstraction. It handles the four ways Salesforce issues credentials — username/password with a security token, IP-whitelisted organization ID, JWT-signed private keys, and OAuth2 connected apps — and exposes the result as a Salesforce object that proxies attribute access straight onto Salesforce object names (sf.Contact, sf.Account), returning plain dictionaries shaped like the underlying JSON responses.

Beyond basic CRUD, it layers on SOQL/SOSL query helpers (including a format_soql value-escaping helper to guard against injection), a Bulk API 1.0 and 2.0 handler for large batch inserts/updates/deletes, and a full Metadata API wrapper for creating, reading, and deploying org metadata such as custom objects and fields. Because it deliberately avoids modeling Salesforce’s object schema, it works with any org’s custom objects and fields without needing regenerated bindings.

What You Get

  • Four authentication flows out of the box — username/password + security token, IP-whitelisted organization ID, JWT bearer flow with a private key, and OAuth2 connected apps with consumer key/secret
  • Attribute-style access to any Salesforce object (sf.Contact, sf.Account, or any custom __c object) with .create(), .get(), .update(), .upsert(), .delete(), .deleted(), and .updated() methods
  • SOQL and SOSL query support, including query_all/query_all_iter pagination helpers and a format_soql template function for safely escaping user-supplied values
  • Bulk API 1.0 and 2.0 handlers (sf.bulk, sf.bulk2) for high-volume insert/update/upsert/delete jobs, plus a CSV-oriented interface
  • A full Metadata API wrapper (sf.mdapi) for CRUD operations on metadata types like CustomObject and CustomField, and file-based deploys of packaged metadata zips

Common Use Cases

  • Syncing CRM records between an internal application and Salesforce (create/update/delete Contacts, Accounts, Opportunities, Leads) without a heavyweight ORM
  • Running ad-hoc SOQL/SOSL reporting queries against a Salesforce org from a Python script or notebook
  • Bulk-loading or bulk-updating large datasets (e.g., migrating tens of thousands of records) via the Bulk 2.0 CSV-based API
  • Automating metadata deployments — provisioning custom objects/fields or deploying a converted SFDX package as part of a CI pipeline

Under The Hood

Architecture The library centers on a single Salesforce facade class in api.py (1,384 lines) whose __init__ resolves credentials into a session_id/instance_url pair via SalesforceLogin (login.py), which branches across four independent auth flows — password+security-token, IP-whitelisted org ID, JWT bearer (signed with pyjwt), and OAuth2 connected-app — before returning to the constructor. Rather than modeling Salesforce’s object schema, __getattr__ on Salesforce (api.py:345) lazily wires any attribute access into an SFType scoped to that object name, so sf.Contact and sf.MyCustomObject__c are handled identically with no generated bindings. Bulk data operations are split into two independently-maintained handlers — SFBulkHandler (bulk.py) for Bulk API 1.0 and a separate bulk2.py (1,401 lines, using more_itertools.chunked to batch CSV rows) for Bulk API 2.0 — while metadata CRUD is routed through SfdcMetadataApi (metadata.py), which builds SOAP/XML envelopes via zeep instead of REST JSON, a genuinely different protocol living behind the same sf.mdapi attribute. Because each of these four surfaces (REST, Bulk 1.0, Bulk 2.0, Metadata SOAP) carries its own session/header handling rather than sharing one HTTP client wrapper, a change to how sessions or auth headers are constructed has to be propagated through api.py, bulk.py, bulk2.py, and login.py separately.

Tech Stack Built for Python 3.9-3.14 with a setuptools/pyproject.toml build (dynamic versioning pulled from __version__.py), the library’s only hard runtime dependencies are requests for REST transport, zeep for the SOAP-based Metadata API client, pyjwt[crypto] for signing JWT bearer assertions, typing-extensions for backporting newer typing constructs, and more-itertools for chunking Bulk 2.0 CSV batches. There is no web framework, ORM, or database layer — this is a thin, dependency-light HTTP/SOAP client, not an application framework. Development tooling is comprehensive: tox drives a per-Python-version test matrix plus a dedicated static environment running pylint and mypy, black/isort/flake8 cover formatting, and Sphinx (with sphinx-rtd-theme) builds the docs published to Read the Docs.

Code Quality Tests live under simple_salesforce/tests/ (test_api.py at 1,297 lines alone) and mock outbound HTTP with the responses library (@responses.activate) plus unittest.mock.patch, giving deterministic coverage of auth flows, CRUD calls, and bulk jobs without hitting a real org; CI runs this suite across six Python versions (3.9-3.14, including PyPy) with pytest-cov enforcing a coverage floor. Static analysis is unusually strict for the ecosystem — the mypy config enables disallow_untyped_defs, disallow_any_generics, warn_return_any, and strict_equality, and the package ships a py.typed marker so consumers get real type checking. One caveat: both pylint and mypy are invoked in tox with a leading dash, meaning failures there don’t fail CI — they’re advisory rather than a hard gate. Naming and error handling are consistent, with a small typed exception hierarchy (SalesforceError and subclasses like SalesforceMoreThanOneRecord) carrying structured url/status/content fields rather than swallowing errors.

API Design The public surface is intentionally minimal and close to Salesforce’s own REST semantics: instantiate Salesforce(...) once, then call .create()/.get()/.update()/.upsert()/.delete() on whatever object attribute you access, or drop into raw .query()/.search() for SOQL/SOSL. This keeps boilerplate low — no schema generation or model classes are required to start reading and writing records for any org, including custom objects the library has never seen. The format_soql/format_external_id helpers are a deliberate DX/security addition, giving callers a str.format-like templating syntax that escapes interpolated values to prevent SOQL injection, a more ergonomic and safer pattern than requiring callers to hand-build query strings. The tradeoff is that four different auth flows, plus three additional protocol surfaces (Bulk 1.0, Bulk 2.0, Metadata SOAP) accessed through differently-shaped sub-objects (sf.bulk, sf.bulk2, sf.mdapi), mean the docs (and the extensive README) do a lot of the work of explaining which entry point to use for a given task.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search