Automate CRM Data Entry with Python and Salesforce API
Your sales team spends 30% of their time updating Salesforce. Manually entering leads from forms, copying data from emails, updating contact information. It's tedious, error-prone, and keeps them from actually selling.
Python + Salesforce API eliminates this waste. Automatically create leads, update contacts, log activitiesβall without touching the Salesforce interface.
What You'll Learn
- Authenticating with Salesforce API
- Creating and updating Salesforce records programmatically
- Querying Salesforce data with Python
- Building automated workflows for common CRM tasks
- Handling errors and maintaining data quality
- Real-world integration examples
Prerequisites
- Python 3.7 or higher
- Salesforce account (any edition with API access)
- Basic understanding of Salesforce objects (Leads, Contacts, Accounts)
Understanding Salesforce API
Salesforce provides REST API for programmatic access to your CRM data.
What you can do:
- Create/Read/Update/Delete (CRUD) any Salesforce object
- Query data using SOQL (Salesforce Object Query Language)
- Execute bulk operations
- Access custom objects and fields
API limits: Varies by Salesforce edition (typically 15,000-100,000 API calls per 24 hours)
Setting Up API Access
Step 1: Get Salesforce Credentials
You need:
- Username
- Password
- Security Token
- Domain (e.g.,
login.salesforce.comor custom domain)
Get your security token:
- Log into Salesforce
- Click your profile β Settings
- My Personal Information β Reset My Security Token
- Check email for security token
Step 2: Create Connected App (For Production)
For production use, create a connected app:
- Setup β App Manager β New Connected App
- Fill required fields:
- Connected App Name: "Python Integration"
- API Name: Auto-generated
- Contact Email: Your email
- Enable OAuth Settings:
- Callback URL:
https://localhost:8080/callback - Selected OAuth Scopes:
- Full access (full)
- Perform requests on your behalf at any time (refresh_token, offline_access)
- Callback URL:
- Save and note Consumer Key and Consumer Secret
Step 3: Install simple-salesforce Library
1pip install simple-salesforce
Authenticating with Salesforce
Method 1: Username/Password (Development)
1from simple_salesforce import Salesforce23# Connect to Salesforce4sf = Salesforce(5 username='your-email@company.com',6 password='your-password',7 security_token='your-security-token'8)910print("β Connected to Salesforce")
Method 2: OAuth (Production)
1from simple_salesforce import Salesforce23sf = Salesforce(4 instance_url='https://your-instance.salesforce.com',5 session_id='oauth-session-id'6)
Method 3: From Environment Variables (Best Practice)
1import os2from simple_salesforce import Salesforce34sf = Salesforce(5 username=os.getenv('SF_USERNAME'),6 password=os.getenv('SF_PASSWORD'),7 security_token=os.getenv('SF_SECURITY_TOKEN')8)
Set environment variables:
1export SF_USERNAME="your-email@company.com"2export SF_PASSWORD="your-password"3export SF_SECURITY_TOKEN="your-security-token"
Creating Records
Create a Lead
1def create_lead(first_name, last_name, company, email, phone=None):2 """3 Create a new lead in Salesforce.45 Args:6 first_name: Lead's first name7 last_name: Lead's last name8 company: Company name9 email: Email address10 phone: Phone number (optional)1112 Returns:13 Lead ID if successful14 """1516 lead_data = {17 'FirstName': first_name,18 'LastName': last_name,19 'Company': company,20 'Email': email,21 'Status': 'Open - Not Contacted',22 'LeadSource': 'Web'23 }2425 if phone:26 lead_data['Phone'] = phone2728 try:29 result = sf.Lead.create(lead_data)3031 if result['success']:32 lead_id = result['id']33 print(f"β Lead created: {lead_id}")34 return lead_id35 else:36 print(f"β Failed to create lead: {result}")37 return None3839 except Exception as e:40 print(f"β Error creating lead: {e}")41 return None424344# Usage45create_lead(46 first_name="Sarah",47 last_name="Johnson",48 company="Acme Corp",49 email="sarah@acme.com",50 phone="555-0123"51)
Create a Contact
1def create_contact(first_name, last_name, account_id, email, phone=None, title=None):2 """3 Create a new contact in Salesforce.45 Args:6 first_name: Contact's first name7 last_name: Contact's last name8 account_id: Associated Account ID9 email: Email address10 phone: Phone number (optional)11 title: Job title (optional)1213 Returns:14 Contact ID if successful15 """1617 contact_data = {18 'FirstName': first_name,19 'LastName': last_name,20 'AccountId': account_id,21 'Email': email22 }2324 if phone:25 contact_data['Phone'] = phone26 if title:27 contact_data['Title'] = title2829 try:30 result = sf.Contact.create(contact_data)3132 if result['success']:33 print(f"β Contact created: {result['id']}")34 return result['id']35 else:36 print(f"β Failed to create contact: {result}")37 return None3839 except Exception as e:40 print(f"β Error creating contact: {e}")41 return None
Create an Opportunity
1def create_opportunity(name, account_id, amount, close_date, stage='Prospecting'):2 """3 Create a new opportunity in Salesforce.45 Args:6 name: Opportunity name7 account_id: Associated Account ID8 amount: Deal value9 close_date: Expected close date (YYYY-MM-DD)10 stage: Sales stage (default: Prospecting)1112 Returns:13 Opportunity ID if successful14 """1516 opp_data = {17 'Name': name,18 'AccountId': account_id,19 'Amount': amount,20 'CloseDate': close_date,21 'StageName': stage22 }2324 try:25 result = sf.Opportunity.create(opp_data)2627 if result['success']:28 print(f"β Opportunity created: {result['id']}")29 return result['id']30 else:31 print(f"β Failed to create opportunity: {result}")32 return None3334 except Exception as e:35 print(f"β Error creating opportunity: {e}")36 return None373839# Usage40create_opportunity(41 name="Q4 2025 Deal - Acme Corp",42 account_id="001...", # Account ID43 amount=50000,44 close_date="2025-12-31",45 stage="Qualification"46)
Querying Records
Simple Query
1# Get all open leads2leads = sf.query("SELECT Id, Name, Email, Company FROM Lead WHERE Status='Open - Not Contacted'")34print(f"Found {leads['totalSize']} open leads")56for lead in leads['records']:7 print(f"- {lead['Name']} from {lead['Company']} ({lead['Email']})")
Query with Filtering
1def get_high_value_opportunities(min_amount=10000):2 """Get opportunities above a certain value."""34 query = f"""5 SELECT Id, Name, Amount, StageName, CloseDate, Account.Name6 FROM Opportunity7 WHERE Amount >= {min_amount}8 AND IsClosed = false9 ORDER BY Amount DESC10 """1112 results = sf.query(query)1314 opportunities = []15 for record in results['records']:16 opportunities.append({17 'id': record['Id'],18 'name': record['Name'],19 'amount': record['Amount'],20 'stage': record['StageName'],21 'close_date': record['CloseDate'],22 'account': record['Account']['Name']23 })2425 return opportunities262728# Usage29high_value_opps = get_high_value_opportunities(min_amount=50000)3031for opp in high_value_opps:32 print(f"{opp['name']}: ${opp['amount']:,.2f} - {opp['stage']}")
Query with Date Filters
1from datetime import datetime, timedelta23def get_recent_leads(days=7):4 """Get leads created in the last N days."""56 # Calculate date N days ago7 date_filter = (datetime.now() - timedelta(days=days)).strftime('%Y-%m-%dT%H:%M:%SZ')89 query = f"""10 SELECT Id, Name, Email, Company, CreatedDate11 FROM Lead12 WHERE CreatedDate >= {date_filter}13 ORDER BY CreatedDate DESC14 """1516 results = sf.query(query)17 return results['records']181920# Get leads from last 7 days21recent_leads = get_recent_leads(7)22print(f"Found {len(recent_leads)} leads in the last 7 days")
Updating Records
Update a Single Record
1def update_lead_status(lead_id, new_status, notes=None):2 """3 Update lead status.45 Args:6 lead_id: Salesforce Lead ID7 new_status: New status value8 notes: Optional notes to add910 Returns:11 True if successful12 """1314 update_data = {15 'Status': new_status16 }1718 if notes:19 update_data['Description'] = notes2021 try:22 sf.Lead.update(lead_id, update_data)23 print(f"β Lead {lead_id} updated to {new_status}")24 return True2526 except Exception as e:27 print(f"β Error updating lead: {e}")28 return False293031# Usage32update_lead_status(33 lead_id="00Q...",34 new_status="Working - Contacted",35 notes="Had initial phone call, scheduling demo"36)
Bulk Update
1def bulk_update_leads(lead_ids, field, value):2 """3 Update multiple leads at once.45 Args:6 lead_ids: List of Lead IDs7 field: Field name to update8 value: New value910 Returns:11 Number of successfully updated records12 """1314 success_count = 01516 for lead_id in lead_ids:17 try:18 sf.Lead.update(lead_id, {field: value})19 success_count += 120 except Exception as e:21 print(f"β Failed to update {lead_id}: {e}")2223 print(f"β Updated {success_count}/{len(lead_ids)} leads")24 return success_count252627# Usage: Mark multiple leads as contacted28lead_ids = ["00Q...", "00Q...", "00Q..."]29bulk_update_leads(lead_ids, 'Status', 'Working - Contacted')
Real-World Automation Examples
Example 1: Web Form to Salesforce Lead
Automatically create leads from website form submissions:
1import requests2from simple_salesforce import Salesforce34def process_web_form_submission(form_data):5 """6 Create Salesforce lead from web form.78 Args:9 form_data: Dictionary with form fields1011 Returns:12 Lead ID or None13 """1415 # Connect to Salesforce16 sf = Salesforce(17 username=os.getenv('SF_USERNAME'),18 password=os.getenv('SF_PASSWORD'),19 security_token=os.getenv('SF_SECURITY_TOKEN')20 )2122 # Map form fields to Salesforce fields23 lead_data = {24 'FirstName': form_data.get('first_name'),25 'LastName': form_data.get('last_name'),26 'Company': form_data.get('company'),27 'Email': form_data.get('email'),28 'Phone': form_data.get('phone'),29 'LeadSource': 'Website',30 'Status': 'Open - Not Contacted',31 'Description': form_data.get('message', '')32 }3334 # Add custom fields if present35 if 'industry' in form_data:36 lead_data['Industry'] = form_data['industry']3738 try:39 result = sf.Lead.create(lead_data)4041 if result['success']:42 lead_id = result['id']43 print(f"β Lead created from web form: {lead_id}")4445 # Send confirmation email (optional)46 send_confirmation_email(form_data['email'], lead_id)4748 return lead_id49 else:50 print(f"β Failed to create lead: {result}")51 return None5253 except Exception as e:54 print(f"β Error processing form: {e}")55 return None565758# Usage59form_data = {60 'first_name': 'John',61 'last_name': 'Smith',62 'company': 'Tech Startup Inc',63 'email': 'john@techstartup.com',64 'phone': '555-9999',65 'industry': 'Technology',66 'message': 'Interested in enterprise plan'67}6869process_web_form_submission(form_data)
Example 2: Email to Contact Updates
Parse emails and update Salesforce contacts:
1import imaplib2import email3from email.header import decode_header45def sync_email_to_salesforce(email_address):6 """7 Find contact by email and log email activity.89 Args:10 email_address: Contact's email address1112 Returns:13 True if logged successfully14 """1516 # Find contact by email17 query = f"SELECT Id, Name FROM Contact WHERE Email='{email_address}' LIMIT 1"18 results = sf.query(query)1920 if results['totalSize'] == 0:21 print(f"β No contact found for {email_address}")22 return False2324 contact_id = results['records'][0]['Id']25 contact_name = results['records'][0]['Name']2627 # Create task to log the email28 task_data = {29 'WhoId': contact_id,30 'Subject': 'Email Received',31 'Status': 'Completed',32 'Description': f'Email conversation with {contact_name}',33 'ActivityDate': datetime.now().strftime('%Y-%m-%d')34 }3536 try:37 result = sf.Task.create(task_data)3839 if result['success']:40 print(f"β Email activity logged for {contact_name}")41 return True42 else:43 print(f"β Failed to log activity: {result}")44 return False4546 except Exception as e:47 print(f"β Error logging email: {e}")48 return False
Example 3: Lead Scoring and Auto-Assignment
Score leads based on criteria and assign to sales reps:
1def score_lead(lead):2 """3 Calculate lead score based on various factors.45 Args:6 lead: Lead record from Salesforce78 Returns:9 Integer score (0-100)10 """1112 score = 01314 # Company size scoring15 if lead.get('NumberOfEmployees'):16 employees = lead['NumberOfEmployees']17 if employees > 1000:18 score += 3019 elif employees > 100:20 score += 2021 elif employees > 10:22 score += 102324 # Industry scoring25 high_value_industries = ['Technology', 'Financial Services', 'Healthcare']26 if lead.get('Industry') in high_value_industries:27 score += 202829 # Engagement scoring30 if lead.get('Website'):31 score += 1032 if lead.get('Phone'):33 score += 103435 # Job title scoring36 decision_maker_titles = ['CEO', 'CTO', 'VP', 'Director', 'Manager']37 title = lead.get('Title', '').lower()38 if any(role.lower() in title for role in decision_maker_titles):39 score += 204041 return min(score, 100) # Cap at 100424344def auto_assign_leads():45 """46 Score and assign unassigned leads to sales reps.47 """4849 # Get unassigned leads50 query = """51 SELECT Id, FirstName, LastName, Company, Email, Phone,52 Industry, Title, NumberOfEmployees53 FROM Lead54 WHERE OwnerId='00G...' # Replace with your queue/default owner ID55 AND Status='Open - Not Contacted'56 """5758 results = sf.query(query)5960 # Sales rep assignments (simplified)61 sales_reps = {62 'high': '005...', # Senior rep for high-score leads63 'medium': '005...', # Mid-level rep64 'low': '005...' # Junior rep65 }6667 for lead in results['records']:68 score = score_lead(lead)6970 # Determine assignment based on score71 if score >= 70:72 owner_id = sales_reps['high']73 priority = 'High'74 elif score >= 40:75 owner_id = sales_reps['medium']76 priority = 'Medium'77 else:78 owner_id = sales_reps['low']79 priority = 'Low'8081 # Update lead82 sf.Lead.update(lead['Id'], {83 'OwnerId': owner_id,84 'Rating': priority,85 'Lead_Score__c': score # Custom field86 })8788 print(f"β Assigned {lead['FirstName']} {lead['LastName']} (score: {score}) to rep")899091# Run auto-assignment92auto_assign_leads()
Example 4: Duplicate Detection
Prevent duplicate leads:
1def find_duplicate_lead(email, phone=None):2 """3 Check if lead already exists.45 Args:6 email: Email address to check7 phone: Phone number to check (optional)89 Returns:10 Lead ID if duplicate found, None otherwise11 """1213 # Search by email14 query = f"SELECT Id, Name, Email, Status FROM Lead WHERE Email='{email}' LIMIT 1"15 results = sf.query(query)1617 if results['totalSize'] > 0:18 lead = results['records'][0]19 return {20 'id': lead['Id'],21 'name': lead['Name'],22 'status': lead['Status'],23 'match_type': 'email'24 }2526 # Search by phone if provided27 if phone:28 query = f"SELECT Id, Name, Phone, Status FROM Lead WHERE Phone='{phone}' LIMIT 1"29 results = sf.query(query)3031 if results['totalSize'] > 0:32 lead = results['records'][0]33 return {34 'id': lead['Id'],35 'name': lead['Name'],36 'status': lead['Status'],37 'match_type': 'phone'38 }3940 return None414243def create_lead_with_duplicate_check(lead_data):44 """45 Create lead only if no duplicate exists.4647 Args:48 lead_data: Dictionary with lead information4950 Returns:51 Lead ID or existing duplicate ID52 """5354 # Check for duplicates55 duplicate = find_duplicate_lead(56 lead_data['Email'],57 lead_data.get('Phone')58 )5960 if duplicate:61 print(f"β οΈ Duplicate found: {duplicate['name']} (matched on {duplicate['match_type']})")62 print(f" Existing lead ID: {duplicate['id']}")63 print(f" Status: {duplicate['status']}")6465 # Optionally update existing lead instead66 return duplicate['id']6768 # No duplicate, create new lead69 result = sf.Lead.create(lead_data)7071 if result['success']:72 print(f"β New lead created: {result['id']}")73 return result['id']74 else:75 print(f"β Failed to create lead: {result}")76 return None
Error Handling Best Practices
1from simple_salesforce.exceptions import SalesforceError2import logging34logging.basicConfig(level=logging.INFO)5logger = logging.getLogger(__name__)67def safe_salesforce_operation(operation_func, *args, **kwargs):8 """9 Wrapper for safe Salesforce operations with retry logic.1011 Args:12 operation_func: Function to execute13 *args, **kwargs: Arguments to pass to function1415 Returns:16 Operation result or None if failed17 """1819 max_retries = 320 retry_count = 02122 while retry_count < max_retries:23 try:24 result = operation_func(*args, **kwargs)25 return result2627 except SalesforceError as e:28 # API-specific errors29 logger.error(f"Salesforce API error: {e}")3031 if 'UNABLE_TO_LOCK_ROW' in str(e):32 # Record locked, retry33 retry_count += 134 logger.info(f"Retrying ({retry_count}/{max_retries})...")35 time.sleep(2 ** retry_count) # Exponential backoff36 else:37 # Other API errors, don't retry38 return None3940 except Exception as e:41 # General errors42 logger.error(f"Unexpected error: {e}")43 return None4445 logger.error(f"Max retries ({max_retries}) exceeded")46 return None474849# Usage50result = safe_salesforce_operation(51 sf.Lead.create,52 {'FirstName': 'John', 'LastName': 'Doe', 'Company': 'Acme', 'Email': 'john@acme.com'}53)
Performance Tips
β
Batch operations when possible (use bulk API for >200 records)
β
Query only needed fields (don't SELECT *)
β
Use SOQL indexes (filter on indexed fields like Id, Name, Email)
β
Cache frequently accessed data (don't query same data repeatedly)
β
Monitor API limits (check daily usage)
β
Use async operations for large data sets
Key Takeaways
- simple-salesforce library simplifies Salesforce API integration
- Authentication requires username, password, and security token
- CRUD operations are straightforward with simple-salesforce
- SOQL queries allow flexible data retrieval
- Error handling is critical for reliable automation
- Duplicate checking prevents data quality issues
- API limits must be monitored and respected
Conclusion
Manual Salesforce data entry is now obsolete for your team. With Python and the Salesforce API, you've automated lead creation, contact updates, and opportunity tracking.
Build on these examples to create workflows specific to your business. Integrate with your marketing tools, customer support systems, and internal databases. Your CRM becomes a hub that updates itself, freeing your team to focus on relationships, not data entry.
Related articles: Automate Sales Pipeline CRM Workflows, Automated Lead Scoring Marketing Guide
Sponsored Content
Interested in advertising? Reach automation professionals through our platform.
