simple-salesforce
A lightweight Python client for the Salesforce REST, Bulk, and Metadata APIs with four built-in authentication flows.
Repository Health
Technical Analysis
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__cobject) with.create(),.get(),.update(),.upsert(),.delete(),.deleted(), and.updated()methods - SOQL and SOSL query support, including
query_all/query_all_iterpagination helpers and aformat_soqltemplate 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 likeCustomObjectandCustomField, 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.
Used by 3 apps in this directory
Agno
Devops · AI Development · Automation
Build, run, and manage agent platforms with a full production stack — SDK, runtime, and control plane included.
Apache Airflow
Data Engineering
Define, schedule, and monitor complex data workflows as Python code — with a powerful UI, 80+ provider integrations, and battle-tested scalability across thousands of production deployments.
PostHog
Analytics · Monitoring · Developer Tools
The all-in-one open source product platform combining analytics, session replay, feature flags, error tracking, AI observability, and a built-in data warehouse in a single self-hostable stack.