# Salesforce in Python

Call the Salesforce REST API from Python services, run SOQL queries with pagination, create and upsert records idempotently, and sync your CRM with other systems on a schedule.

[Get started ▶](https://zato.io/docs/admin/guide/install/index.html) [Read the docs](https://zato.io/docs/index.html)

REST

API client

SOQL

queries and pagination

24/7

scheduled syncs

7

guides and docs

Code examples

## Records in, records out

Create records from a REST channel or pull them out with SOQL on a schedule, all with the same services.

**Create a record**

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class CreateCampaign(Service):
    name = 'crm.create-campaign'

    input = 'name', 'segment'

    def handle(self) -> 'None':

        # Get a connection to Salesforce ..
        conn = self.salesforce['My Salesforce Connection']

        # .. map our input to what Salesforce expects ..
        request = {
            'Name': self.request.input.name,
            'Segment__c': self.request.input.segment,
        }

        # .. and create the record now.
        response = conn.post('/sobjects/Campaign/', request)

        campaign_id = response['id']
        self.response.payload = {'campaign_id': campaign_id}
```

**Run a scheduled sync**

```python
# -*- coding: utf-8 -*-

# stdlib
from urllib.parse import quote

# Zato
from zato.server.service import Service

class SyncOpportunities(Service):
    name = 'crm.sync-opportunities'

    # Invoked by a scheduler job, e.g. every two hours
    def handle(self) -> 'None':

        # Only one sync runs at a time ..
        with self.lock():

            conn = self.salesforce['My Salesforce Connection']

            # .. pull the opportunities that changed ..
            query = 'SELECT Id, Name, Amount, Account.Name FROM Opportunity'
            response = conn.get('/query/?q=' + quote(query))

            # .. and push each one to the target system.
            for record in response['records']:
                _ = self.invoke('crm.push-to-erp', record)
```

Features

## Everything for Salesforce integrations

The REST API, SOQL and scheduled syncs, with the reliability patterns Salesforce leaves to you.

[The REST API from Python](https://zato.io/docs/dev/salesforce/rest-api-python.html)

Call the Salesforce REST API with self.salesforce - get, post, patch and delete against any sObject path, with OAuth tokens obtained and attached for you.

```python
conn = self.salesforce['My Salesforce Connection']

# Create, read, update and delete records
response = conn.post('/sobjects/Campaign/', request)
campaign = conn.get(f'/sobjects/Campaign/{campaign_id}')
_ = conn.patch(f'/sobjects/Campaign/{campaign_id}', changes)
_ = conn.delete(f'/sobjects/Campaign/{campaign_id}')
```

[SOQL and pagination](https://zato.io/docs/dev/salesforce/soql-relationships-and-pagination.html)

Query records with SOQL, traverse parent relationships in one request and page through large result sets with nextRecordsUrl.

[Upserts and external IDs](https://zato.io/docs/dev/salesforce/upserts-and-external-ids.html)

Create-or-update in one idempotent call, keyed by your own identifiers instead of Salesforce record IDs.

[Scheduled syncs](https://zato.io/docs/dev/salesforce/crm-sync-scenario.html)

Pull changed records on a schedule, correlate them with other systems and update only what drifted - no cron jobs or external workers.

```python
# Invoked by a scheduler job, e.g. every two hours
class SyncOpportunities(Service):
    name = 'crm.sync-opportunities'

    def handle(self) -> 'None':
        with self.lock():
            self.pull_from_salesforce()
            self.push_changes_downstream()
```

[Errors and API limits](https://zato.io/docs/dev/salesforce/error-handling-and-api-limits.html)

Read Salesforce error arrays, watch your daily API request allowance and decide per record whether to continue or fail the run.

[Configuration as YAML](https://zato.io/docs/admin/enmasse-reference.html)

Define Salesforce connections declaratively with enmasse and deploy them across environments.

Tutorials

## Learn by building

Step-by-step guides for building API integrations in Python.

[API In-depth API integration tutorial Learn how to design and build scalable, real-world, production-ready API integrations in Python. Start tutorial](https://zato.io/tutorials/main/01.html) [REST REST APIs in Python Build REST channels and outgoing connections - the same building blocks Salesforce integrations use. Start tutorial](https://zato.io/tutorials/rest-api/python.html) [Case study How Keysight syncs Salesforce A real production integration - channel opportunities flowing from Salesforce to a partner portal. Read the story](https://zato.io/customer-success/keysight/salesforce.html)

Guides

## Salesforce integration guides

Each guide covers one part of a Salesforce integration, with complete Python services you can copy and run.

[REST The REST API from Python Credentials, connected apps, connections and the self.salesforce API](https://zato.io/docs/dev/salesforce/rest-api-python.html) [Records Create and query records A REST channel that creates campaigns and reads them back with SOQL](https://zato.io/docs/dev/salesforce/create-and-query-records.html) [SOQL SOQL relationships and pagination Parent traversal in the select list and the nextRecordsUrl loop](https://zato.io/docs/dev/salesforce/soql-relationships-and-pagination.html) [Upserts Upserts and external IDs Idempotent create-or-update and parent references by external ID](https://zato.io/docs/dev/salesforce/upserts-and-external-ids.html) [Updates Updates and deletes Diff-driven partial updates and deleting records by ID](https://zato.io/docs/dev/salesforce/updates-and-deletes.html) [Errors Error handling and API limits Error arrays, per-record success flags and the daily request allowance](https://zato.io/docs/dev/salesforce/error-handling-and-api-limits.html) [CRM sync A CRM sync, end to end A scheduled one-way sync with correlation keys and drift detection](https://zato.io/docs/dev/salesforce/crm-sync-scenario.html)

FAQ

## Frequently asked questions

Create a connected app in your Salesforce org and take its consumer key and consumer secret, then store them together with a username and password in a Zato Salesforce connection. Your services never handle tokens - Zato obtains and attaches OAuth access tokens for every request. The [getting started guide](https://zato.io/docs/dev/salesforce/rest-api-python.html) shows where to find each credential.

The REST API - the same one behind Salesforce's own tooling. You call sObject and query paths such as /sobjects/Campaign/ or /query/ and Zato prefixes them with the address and API version of your connection.

The API version is a field on the connection definition, e.g. 54.0. Upgrading is a one-field change in the Dashboard or in your enmasse YAML, with no code redeployment.

SOQL responses carry done and nextRecordsUrl fields - when done is false, follow nextRecordsUrl until it is true. The [SOQL guide](https://zato.io/docs/dev/salesforce/soql-relationships-and-pagination.html) has the complete loop.

Upsert by an external ID field instead of creating blindly - one idempotent call that creates the record if it is missing and updates it if it exists. The [upserts guide](https://zato.io/docs/dev/salesforce/upserts-and-external-ids.html) covers external IDs and parent references.

A JSON array of objects with errorCode and message fields, e.g. REQUIRED\_FIELD\_MISSING or INVALID\_SESSION\_ID, and some operations report success per record rather than per request. The [error handling guide](https://zato.io/docs/dev/salesforce/error-handling-and-api-limits.html) shows how to read both.

Every org has a rolling 24-hour API request allowance. Prefer one SOQL query with relationship traversal over many per-record lookups, batch your work with the scheduler and read the Sforce-Limit-Info response header - the [limits guide](https://zato.io/docs/dev/salesforce/error-handling-and-api-limits.html) explains the arithmetic.

Point a Salesforce outbound message or an Apex callout at a Zato REST channel. The [webhooks guide](https://zato.io/docs/dev/rest/webhooks.html) includes a Salesforce example with signature verification.

Yes - define Salesforce connections in YAML with [enmasse](https://zato.io/docs/admin/enmasse-reference.html) and import them per environment, with credentials read from environment variables.

Create is a post to /sobjects/Type/, Update is a patch to the record path, Upsert is a patch to an external-ID path, Delete is a delete on the record path and Query is a get on /query/. The [getting started guide](https://zato.io/docs/dev/salesforce/rest-api-python.html) has the full mapping table.

## Ready to build Salesforce integrations?

Get started with Zato and connect Salesforce to your enterprise systems in minutes.

[Get started ▶](https://zato.io/docs/admin/guide/install/index.html) [Read the docs](https://zato.io/docs/index.html)

Open source In Python
