Part 60: The Major Salesforce APIs
Welcome back to the Salesforce series. Over the last several installments we have been deep inside the Apex programming language — syntax, triggers, testing, governor limits, and integration patterns. Before we go further into object-oriented design, we need to take a step back and look at the full landscape of APIs that Salesforce exposes. Understanding these APIs is essential whether you are an Apex developer writing callouts, a front-end engineer building custom UIs, an integration architect connecting Salesforce to external systems, or a DevOps engineer automating deployments.
This is Part 60 of the series. We will cover nine distinct Salesforce APIs plus a hands-on project. By the end of this post you will know what each API does, when to reach for it, how it authenticates, and how it fits into the broader Salesforce ecosystem.
Why Does Salesforce Have So Many APIs?
Salesforce is not a single product. It is a platform that serves wildly different use cases — CRM data entry, bulk data migration, real-time event streaming, metadata deployment, AI predictions, report generation, and mobile-optimized record access. A single API cannot serve all of these needs efficiently. Each API is purpose-built for a specific interaction pattern:
- CRUD on individual records? REST API or SOAP API.
- Millions of records? Bulk API.
- Deploying code and configuration? Metadata API.
- Querying metadata or running anonymous Apex? Tooling API.
- Building custom Lightning UIs? UI API.
- Reacting to data changes in real time? Streaming API.
- Running reports programmatically? Reports and Dashboards API.
- AI predictions? Models API.
Let us work through each one.
1. The REST API
The REST API is the most commonly used Salesforce API. It follows standard REST conventions — resources identified by URIs, standard HTTP methods, JSON or XML payloads, and stateless interactions. If you are building an integration and are not sure which API to use, the REST API is almost always the right starting point.
Base URL and Versioning
Every REST API call targets your Salesforce instance with a versioned path:
https://yourInstance.salesforce.com/services/data/vXX.0/
The version number (v60.0, v61.0, etc.) determines which features are available. Salesforce adds a new API version with every release (three times per year). You can query available versions with a simple GET /services/data/ call.
Authentication — OAuth 2.0 Flows
Before making any API call, you need an access token. Salesforce supports several OAuth 2.0 flows:
-
Web Server Flow (Authorization Code Grant): The most common flow for web applications. The user is redirected to Salesforce to log in, Salesforce redirects back to your app with an authorization code, and your server exchanges that code for an access token and refresh token. This is the recommended flow for server-side integrations where a user is present.
-
JWT Bearer Token Flow: Ideal for server-to-server integrations with no interactive user. You create a JSON Web Token signed with your private key, send it to Salesforce’s token endpoint, and receive an access token. This is the flow most CI/CD pipelines use.
-
Client Credentials Flow: Introduced more recently, this flow allows a connected app to authenticate directly using its client ID and client secret without any user context. It is simpler than JWT but requires careful security consideration since the connected app acts as a specific integration user.
-
Device Flow: Designed for devices with limited input capabilities (IoT devices, CLI tools). The device displays a code, the user enters it on a separate device with a browser, and the original device polls for the token.
-
Username-Password Flow: The simplest flow — send a username, password, and security token directly to the token endpoint. This flow is deprecated for production use because it exposes credentials directly and does not support MFA properly. Use it only for quick local testing.
-
Refresh Token Flow: Not a standalone flow but a mechanism to get a new access token using a refresh token obtained from one of the above flows. Access tokens expire (typically after two hours), but refresh tokens can last much longer.
Core CRUD Operations
Once authenticated, you interact with standard and custom objects using standard HTTP methods:
Create a Record (POST):
POST /services/data/v60.0/sobjects/Account/
Content-Type: application/json
Authorization: Bearer <access_token>
{
"Name": "Acme Corporation",
"Industry": "Technology",
"BillingCity": "San Francisco"
}
The response returns the new record’s ID and a success flag.
Read a Record (GET):
GET /services/data/v60.0/sobjects/Account/001xx000003DGbYAAW
Authorization: Bearer <access_token>
You can request specific fields to reduce payload size:
GET /services/data/v60.0/sobjects/Account/001xx000003DGbYAAW?fields=Name,Industry,BillingCity
Update a Record (PATCH):
PATCH /services/data/v60.0/sobjects/Account/001xx000003DGbYAAW
Content-Type: application/json
Authorization: Bearer <access_token>
{
"Industry": "Finance"
}
Only include the fields you want to change. Omitted fields are not modified.
Delete a Record (DELETE):
DELETE /services/data/v60.0/sobjects/Account/001xx000003DGbYAAW
Authorization: Bearer <access_token>
Query Records (GET with SOQL):
GET /services/data/v60.0/query/?q=SELECT+Id,Name,Industry+FROM+Account+WHERE+Industry='Technology'
Authorization: Bearer <access_token>
For large result sets, the response includes a nextRecordsUrl that you follow to get subsequent batches.
Composite Resources
The REST API provides composite endpoints that let you combine multiple operations into a single HTTP request, reducing round trips:
- Composite: Send up to 25 subrequests in a single call. Subrequests can reference each other’s results — for example, create a parent Account and then create a child Contact that references the new Account’s ID, all in one request.
{
"compositeRequest": [
{
"method": "POST",
"url": "/services/data/v60.0/sobjects/Account",
"referenceId": "newAccount",
"body": { "Name": "Composite Corp" }
},
{
"method": "POST",
"url": "/services/data/v60.0/sobjects/Contact",
"referenceId": "newContact",
"body": {
"LastName": "Smith",
"AccountId": "@{newAccount.id}"
}
}
]
}
-
Composite Graph: Like Composite but supports up to 500 nodes organized in graphs with dependency ordering. Useful for complex data models.
-
SObject Tree: Create a record and its children in a single call. You post a tree structure and Salesforce creates everything, returning all the new IDs.
-
SObject Collections: Perform the same operation (create, update, upsert, or delete) on up to 200 records of the same type in one call. This is the batch equivalent of individual CRUD.
Limits
The REST API is subject to Salesforce API request limits, which vary by edition and license count. A typical Enterprise Edition org gets 100,000 API calls per 24-hour rolling window (plus 1,000 per user license). You can check your current consumption:
GET /services/data/v60.0/limits/
This returns a JSON object with dozens of limit categories — API requests remaining, daily Bulk API batches, streaming events delivered, and more.
2. The SOAP API
The SOAP API predates the REST API and uses XML-based messaging over HTTP with a WSDL (Web Services Description Language) contract. While REST has become the default for most new integrations, the SOAP API remains important for enterprise environments that rely on strongly-typed contracts and code generation.
Enterprise WSDL vs Partner WSDL
Salesforce provides two WSDLs:
-
Enterprise WSDL: Strongly typed and specific to your org. It includes every standard and custom object, every field, and every relationship in your specific Salesforce instance. When you add a custom field, you must regenerate the WSDL to include it. This WSDL is ideal when you are integrating a single org and want compile-time type safety.
-
Partner WSDL: Loosely typed and generic. It works with any Salesforce org without modification. Instead of typed objects like
Accountwith aNamefield, it uses genericSObjectwith name-value pairs. This WSDL is what ISV partners and AppExchange apps use because their code must work across thousands of different orgs.
How SOAP API Calls Work
A typical SOAP interaction looks like this:
- Login: Send a
login()call with username and password (or use OAuth). Salesforce returns a session ID and a server URL. - Set the endpoint: Use the server URL from the login response for subsequent calls.
- Make API calls: Send SOAP requests for
create(),update(),upsert(),delete(),query(),search(), and other operations. - Handle responses: Parse the XML response for record IDs, error details, and query results.
When to Use SOAP Over REST
- Legacy systems: Many enterprise middleware tools (MuleSoft classic connectors, older versions of Informatica, SAP PI/PO) have built-in SOAP support with WSDL import.
- Strong typing requirements: If your integration platform generates client code from WSDLs, SOAP gives you compile-time safety.
- Metadata operations: Some older metadata operations were historically only available via SOAP, though the Metadata REST API has largely caught up.
- Describe calls: While REST also supports describe, the SOAP
describeSObjects()call returns extremely detailed schema information in a single call.
For new integrations, REST is almost always preferred. SOAP is heavier, more verbose, and harder to debug. But knowing it exists is important because you will encounter it in brownfield projects.
3. The Bulk API
The Bulk API is designed for loading or extracting large volumes of data — tens of thousands to millions of records. While the REST API handles individual records or small batches efficiently, the Bulk API is optimized for asynchronous, high-volume operations that run in the background.
Bulk API 2.0
Salesforce offers two versions. Bulk API 1.0 is the older version with a more complex job/batch model. Bulk API 2.0 is the modern, simplified version that you should use for all new work. We will focus on 2.0 here.
The Job Lifecycle
A Bulk API 2.0 job follows this lifecycle:
- Create a Job: Specify the object, operation (insert, update, upsert, delete, or query), and options like line ending and column delimiter.
POST /services/data/v60.0/jobs/ingest
Content-Type: application/json
{
"object": "Contact",
"operation": "insert",
"lineEnding": "LF",
"columnDelimiter": "COMMA"
}
- Upload Data: Send your CSV data to the job. For ingest jobs, you PUT the CSV content directly.
PUT /services/data/v60.0/jobs/ingest/<jobId>/batches
Content-Type: text/csv
FirstName,LastName,Email
John,Doe,john@example.com
Jane,Smith,jane@example.com
- Close the Job: Tell Salesforce you are done uploading data. This signals Salesforce to begin processing.
PATCH /services/data/v60.0/jobs/ingest/<jobId>
Content-Type: application/json
{
"state": "UploadComplete"
}
- Monitor the Job: Poll the job status until it completes.
GET /services/data/v60.0/jobs/ingest/<jobId>
The response includes state (UploadComplete, InProgress, JobComplete, Failed, Aborted), numberRecordsProcessed, numberRecordsFailed, and timing information.
- Get Results: Once the job completes, retrieve success and failure results.
GET /services/data/v60.0/jobs/ingest/<jobId>/successfulResults/
GET /services/data/v60.0/jobs/ingest/<jobId>/failedResults/
GET /services/data/v60.0/jobs/ingest/<jobId>/unprocessedrecords/
CSV Format
Bulk API 2.0 exclusively uses CSV (comma-separated values) as its data format. This is a deliberate design choice — CSV is simple, widely supported, and efficient for large datasets. Your CSV must include a header row with API field names, and each subsequent row is a record.
For upsert operations, you must include the external ID field in your CSV. For delete operations, you only need the Id column.
Ingest vs Query
The Bulk API supports two operation categories:
- Ingest jobs (
/jobs/ingest): Insert, update, upsert, or delete records. You provide the data. - Query jobs (
/jobs/query): Execute a SOQL query that returns a large result set. Salesforce processes the query asynchronously and makes the results available as CSV files for download.
POST /services/data/v60.0/jobs/query
Content-Type: application/json
{
"operation": "query",
"query": "SELECT Id, Name, Email FROM Contact WHERE MailingState = 'CA'"
}
Monitoring Jobs
You can list all recent jobs to get an overview:
GET /services/data/v60.0/jobs/ingest
GET /services/data/v60.0/jobs/query
This returns a list of jobs with their states, record counts, and timing. You can filter by job state or creation date.
When to Use the Bulk API
- Data migration: Moving records from a legacy system into Salesforce.
- Nightly data syncs: Syncing large datasets from a data warehouse or ERP.
- Mass updates: Updating a field on hundreds of thousands of records.
- Large exports: Extracting all Contacts or Cases for analytics.
- Any operation exceeding a few thousand records: The REST API’s SObject Collections endpoint handles up to 200 records per call. If you need more, switch to Bulk.
The Bulk API has its own set of limits — a maximum of 15,000 batches per rolling 24-hour period and a maximum upload file size of 150 MB per batch. These limits are separate from the REST API limits.
4. The Tooling API
The Tooling API is a specialized API for working with Salesforce metadata and development artifacts. Think of it as the API that powers the Developer Console and VS Code extensions. It gives you programmatic access to things like Apex classes, triggers, Visualforce pages, Lightning components, debug logs, and code coverage.
Querying Metadata
The Tooling API supports SOQL queries against metadata objects. This is extremely powerful for introspection:
GET /services/data/v60.0/tooling/query/?q=SELECT+Id,Name,Body+FROM+ApexClass+WHERE+Name='MyController'
Some commonly queried Tooling API objects:
ApexClass— Apex classes (Id, Name, Body, ApiVersion, Status)ApexTrigger— Apex triggersApexPage— Visualforce pagesApexComponent— Visualforce componentsAuraDefinitionBundle— Aura component bundlesLightningComponentBundle— LWC bundlesApexCodeCoverage— Code coverage results per class/triggerApexCodeCoverageAggregate— Aggregated coverage per classApexLog— Debug logsTraceFlag— Active trace flags for debuggingCustomField— Custom field definitionsCustomObject— Custom object definitionsValidationRule— Validation rule metadataWorkflowRule— Workflow rule metadataFlowDefinition— Flow definitions
Executing Anonymous Apex
One of the most useful Tooling API features is the ability to execute anonymous Apex remotely:
GET /services/data/v60.0/tooling/executeAnonymous/?anonymousBody=System.debug('Hello+from+the+API');
The response tells you whether execution succeeded, any compile errors, and the debug log output. This is invaluable for scripting — you can write automation that executes Apex in a target org without deploying a class.
Accessing Debug Logs
You can query and download debug logs through the Tooling API:
GET /services/data/v60.0/tooling/query/?q=SELECT+Id,LogLength,Operation,Status+FROM+ApexLog+ORDER+BY+StartTime+DESC+LIMIT+10
Then retrieve a specific log’s body:
GET /services/data/v60.0/tooling/sobjects/ApexLog/<logId>/Body
Creating and Updating Metadata Programmatically
The Tooling API lets you create and update metadata objects directly. For example, you can create a new Apex class:
POST /services/data/v60.0/tooling/sobjects/ApexClass
Content-Type: application/json
{
"Body": "public class HelloWorld {\n public static String greet() {\n return 'Hello, World!';\n }\n}",
"ApiVersion": 60.0
}
This is a synchronous operation — the class is compiled immediately and you get back either a success response with the new ID or a compile error. This makes the Tooling API fundamentally different from the Metadata API, which is asynchronous. The Tooling API is better for creating or updating individual components, while the Metadata API is better for deploying bundles of components.
5. The Metadata API
The Metadata API is the deployment and retrieval API. It is what powers the Salesforce CLI (sf project deploy and sf project retrieve), change sets under the hood, and third-party CI/CD tools. Its job is to move metadata — object definitions, fields, page layouts, Apex classes, Lightning components, permission sets, flows, and hundreds of other metadata types — between orgs or between your local filesystem and an org.
The Deploy/Retrieve Cycle
The Metadata API operates on .zip files containing metadata in XML format:
Retrieve: You send a retrieve request specifying which components you want (via a package.xml manifest or a list of specific components). Salesforce assembles them into a .zip file that you download.
Deploy: You send a .zip file containing metadata components. Salesforce validates, compiles (if Apex is included), and deploys them. If any component fails validation, the entire deployment rolls back.
package.xml
The package.xml file is the manifest that tells the Metadata API which components to retrieve or deploy. It lists metadata types and their members:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
<types>
<members>MyController</members>
<members>AccountService</members>
<name>ApexClass</name>
</types>
<types>
<members>AccountTrigger</members>
<name>ApexTrigger</name>
</types>
<types>
<members>Account.Custom_Field__c</members>
<name>CustomField</name>
</types>
<types>
<members>*</members>
<name>CustomObject</name>
</types>
<version>60.0</version>
</Package>
Using * as a member retrieves all components of that type. You can also use specific names to target individual components.
Relationship to Salesforce CLI
The Salesforce CLI (sf command) is the primary tool developers use to interact with the Metadata API. Under the hood:
sf project retrieve startcalls the Metadata API’s retrieve operation.sf project deploy startcalls the Metadata API’s deploy operation.sf project deploy validateperforms a validation-only deployment (check-only deploy) — Salesforce compiles everything and runs tests but does not commit the changes.
The CLI also supports source tracking, which maintains a mapping between your local files and the org’s metadata. When you run sf project retrieve start in a scratch org, it only retrieves components that have changed since the last sync.
When to Use the Metadata API Directly
Most of the time you interact with the Metadata API through the CLI or a CI/CD tool. But there are cases where you might call it directly:
- Custom CI/CD pipelines: If you are building your own deployment tooling rather than using off-the-shelf tools.
- Automated org configuration: Scripts that set up a new org with a baseline configuration.
- Metadata backup solutions: Tools that periodically retrieve all metadata for backup purposes.
- Cross-org comparison: Retrieving metadata from two orgs and comparing them programmatically.
6. The UI API
The UI API is designed specifically for building custom user interfaces that look and behave like native Salesforce Lightning pages. It returns not just data but also layout information, field labels, picklist values, record type mappings, and action availability — everything a front-end component needs to render a record correctly.
What Makes It Different from REST API
When you fetch a record through the REST API, you get raw field values. But to render that record properly in a UI, you also need:
- Which fields appear on which sections of the layout
- The display labels (which might differ from API names and vary by record type)
- Picklist values and their display labels
- Which fields are required, read-only, or hidden
- Which actions (buttons) are available for the current user
- Help text and inline edit support
The UI API bundles all of this into a single response.
Key Endpoints
Get Record with Layout:
GET /services/data/v60.0/ui-api/record-ui/<recordId>
Returns the record’s data, the layout metadata, and object information in one call.
Get Layout for an Object:
GET /services/data/v60.0/ui-api/layout/Account
Returns the full page layout definition including sections, fields, and ordering.
Get List View Records:
GET /services/data/v60.0/ui-api/list-records/<listViewId>
Returns records for a specific list view along with the column definitions.
Get Picklist Values:
GET /services/data/v60.0/ui-api/object-info/Account/picklist-values/<recordTypeId>
Returns all picklist field values for a given object and record type.
Get Actions:
GET /services/data/v60.0/ui-api/actions/record/<recordId>
Returns available actions (global, standard, and custom) for the current user on the given record.
When to Use the UI API
The UI API is primarily used inside Lightning Web Components (LWC). In fact, the lightning-record-form, lightning-record-view-form, and lightning-record-edit-form base components use the UI API internally. If you are building a custom LWC that needs to display or edit records, the UI API saves you from making multiple REST API calls to assemble layout and data information.
It is also useful for building custom external UIs (like a customer portal or mobile app) that should respect the Salesforce admin’s layout configuration.
7. The Streaming API
The Streaming API enables real-time notifications from Salesforce to external systems or client applications. Instead of polling for changes, clients subscribe to channels and receive push notifications when events occur.
The Four Event Types
Salesforce Streaming API supports four categories of events:
PushTopic Events
PushTopic events are the original streaming mechanism. You create a PushTopic record with a SOQL query, and any record change that matches that query generates a notification.
PushTopic pushTopic = new PushTopic();
pushTopic.Name = 'NewHighValueAccounts';
pushTopic.Query = 'SELECT Id, Name, AnnualRevenue FROM Account WHERE AnnualRevenue > 1000000';
pushTopic.ApiVersion = 60.0;
pushTopic.NotifyForOperationCreate = true;
pushTopic.NotifyForOperationUpdate = true;
pushTopic.NotifyForFields = 'Referenced';
insert pushTopic;
Clients subscribe to /topic/NewHighValueAccounts and receive notifications whenever an Account with AnnualRevenue > 1000000 is created or updated.
Change Data Capture (CDC)
Change Data Capture events are generated automatically whenever a record is created, updated, deleted, or undeleted. Unlike PushTopic events, you do not write a query — you select which objects to track in Setup, and Salesforce generates events for all changes to those objects.
CDC events include:
- The changed fields and their new values (for updates, only the changed fields)
- The change type (CREATE, UPDATE, DELETE, UNDELETE)
- Header information (commit timestamp, transaction key, sequence number)
- The user who made the change
Subscribe to /data/AccountChangeEvent to receive all Account changes.
CDC is the recommended streaming mechanism for data replication and synchronization use cases. It captures all changes regardless of how they were made (UI, API, Apex, Flow) and includes enough metadata to reconstruct the change.
Platform Events
Platform Events are custom event objects that you define in Setup. They are Salesforce’s pub/sub messaging system. Unlike PushTopic and CDC events which are driven by record changes, Platform Events are explicitly published by your code.
Define a Platform Event (e.g., Order_Status_Update__e) with custom fields, then publish it from Apex:
Order_Status_Update__e event = new Order_Status_Update__e(
Order_Id__c = '12345',
Status__c = 'Shipped',
Tracking_Number__c = 'UPS1234567890'
);
Database.SaveResult sr = EventBus.publish(event);
Subscribers (external systems, Flows, Apex triggers on the event) receive the event in near real-time.
Platform Events are ideal for:
- Decoupling systems (publish an event, let subscribers handle it)
- Cross-org communication
- Triggering processes from external systems
- Event-driven architectures
Generic Events
Generic events are the simplest streaming type. You create a Streaming Channel and push arbitrary JSON payloads to it. There is no schema validation. This is useful for lightweight, ad-hoc notifications but lacks the structure and reliability of Platform Events.
CometD Protocol
All Streaming API subscriptions use the CometD protocol (Bayeux protocol) over long polling or WebSocket. CometD is a scalable server-push technology. The client:
- Sends a handshake request to
/cometd/60.0 - Receives a client ID
- Subscribes to one or more channels
- Maintains a long-poll connection that Salesforce uses to push events
Most languages have CometD client libraries (Java, Python, JavaScript, .NET). The Salesforce Lightning platform handles CometD internally for LWC components using the lightning/empApi module.
When to Use Each
| Event Type | Best For | Driven By |
|---|---|---|
| PushTopic | Monitoring specific record changes matching a SOQL query | Record DML matching query |
| Change Data Capture | Data replication, syncing external systems | Any record DML on subscribed objects |
| Platform Events | Custom event-driven messaging, cross-system integration | Explicit publish from code |
| Generic Events | Simple, schema-free notifications | Explicit push to channel |
8. The Reports and Dashboards API
The Reports and Dashboards API lets you run reports, apply filters, and retrieve dashboard data programmatically. This is useful for embedding report results in external applications, building custom analytics, or automating data extraction from existing reports.
Running a Report
To run a report, you need its report ID (the 15 or 18-character ID visible in the URL when viewing the report):
GET /services/data/v60.0/analytics/reports/<reportId>
This runs the report synchronously and returns the results, including:
- Report metadata: Column names, grouping fields, report type
- Fact map: The actual data organized by groupings and detail rows
- Summary fields: Aggregates (SUM, AVG, COUNT, etc.)
Applying Dynamic Filters
You can override report filters at runtime without modifying the saved report:
POST /services/data/v60.0/analytics/reports/<reportId>
Content-Type: application/json
{
"reportMetadata": {
"reportFilters": [
{
"column": "ACCOUNT.INDUSTRY",
"operator": "equals",
"value": "Technology"
}
]
}
}
This is powerful for building parameterized dashboards or letting users slice data without creating dozens of saved reports.
Async Reports
For reports that process large amounts of data (or when you do not want to wait for synchronous execution), you can run reports asynchronously:
POST /services/data/v60.0/analytics/reports/<reportId>/instances
This creates a report instance. You then poll for completion:
GET /services/data/v60.0/analytics/reports/<reportId>/instances/<instanceId>
Once complete, the instance contains the full report results.
Dashboard Data
You can retrieve dashboard metadata and data:
GET /services/data/v60.0/analytics/dashboards/<dashboardId>
This returns each dashboard component’s status and data. You can also refresh a dashboard:
PUT /services/data/v60.0/analytics/dashboards/<dashboardId>
Use Cases
- External dashboards: Pulling Salesforce report data into a custom web application or BI tool.
- Automated monitoring: Running reports on a schedule and triggering alerts when thresholds are exceeded.
- Data export: Extracting report data for offline analysis.
- Dynamic reporting: Embedding Salesforce reports in portals where end users can apply filters.
9. The Models API
The Models API provides access to Einstein AI models for predictions and generative AI capabilities. It is the newest of the major APIs and reflects Salesforce’s push toward AI-native features.
What It Exposes
The Models API lets you:
- Generate text: Send a prompt and receive generated text from a large language model.
POST /services/data/v60.0/einstein/llm/prompt/generations
Content-Type: application/json
{
"promptTextorId": "Summarize the following case description: ...",
"provider": "OpenAI",
"additionalConfig": {
"maxTokens": 256,
"temperature": 0.7
}
}
-
Generate embeddings: Convert text into vector embeddings for semantic search and similarity matching.
-
Run predictions: Use Einstein prediction models (built via Einstein Prediction Builder or custom models) to score records.
-
Access model metadata: List available models, their capabilities, and configuration.
Connect API for Einstein
Much of the AI functionality is also available through the Connect REST API under the /services/data/v60.0/einstein/ path. This includes:
- Sentiment analysis
- Intent detection
- Object detection in images
- OCR (optical character recognition)
- Recommendation generation
When to Use the Models API
- Custom AI features: Building LWC components that generate AI-powered recommendations.
- Case summarization: Automatically summarizing long case histories.
- Lead scoring: Enriching leads with prediction scores.
- Semantic search: Converting text to embeddings for similarity-based search across knowledge articles.
- Content generation: Drafting email responses, creating knowledge article summaries, or generating chatbot replies.
The Models API requires Einstein AI credits or Einstein-enabled licenses. Availability depends on your Salesforce edition and add-ons.
API Comparison Table
| API | Protocol | Data Format | Authentication | Max Records Per Call | Sync/Async | Primary Use Case |
|---|---|---|---|---|---|---|
| REST API | REST (HTTP) | JSON, XML | OAuth 2.0 | 200 (Collections), 2000 (Query) | Sync | General-purpose CRUD, queries, integration |
| SOAP API | SOAP (HTTP+XML) | XML | Session ID or OAuth | 200 | Sync | Legacy integrations, strongly-typed clients |
| Bulk API 2.0 | REST (HTTP) | CSV | OAuth 2.0 | Millions per job | Async | High-volume data loads and extracts |
| Tooling API | REST (HTTP) | JSON | OAuth 2.0 | Varies | Sync | Metadata queries, anonymous Apex, dev tooling |
| Metadata API | SOAP (HTTP+XML) | XML (zipped) | Session ID or OAuth | 10,000 components per deploy | Async | Deploying/retrieving metadata, CI/CD |
| UI API | REST (HTTP) | JSON | OAuth 2.0 | Varies | Sync | Building UIs with layout-aware data |
| Streaming API | CometD (Bayeux) | JSON | OAuth 2.0 | Event-based | Push (real-time) | Real-time notifications, data sync |
| Reports & Dashboards | REST (HTTP) | JSON | OAuth 2.0 | Report-dependent | Sync or Async | Running reports, extracting analytics data |
| Models API | REST (HTTP) | JSON | OAuth 2.0 | Request-dependent | Sync | AI predictions, text generation, embeddings |
Decision Guide: Which API Should I Use?
Choosing the right API depends on your use case. Here is a quick decision framework:
Are you doing CRUD on a few records at a time? Use the REST API. It is the simplest, best-documented, and most widely supported.
Are you loading or extracting more than a few thousand records? Use the Bulk API 2.0. It is built for volume and runs asynchronously so you do not hit timeout limits.
Are you integrating with a system that requires WSDL-based contracts? Use the SOAP API. Generate client code from the WSDL and use strongly-typed methods.
Are you deploying code, objects, or configuration to an org? Use the Metadata API (via the Salesforce CLI). It is the standard deployment mechanism.
Are you querying metadata, running anonymous Apex, or building developer tools? Use the Tooling API. It gives you access to the metadata model that the Metadata API deploys.
Are you building a custom UI that should respect admin layout configuration? Use the UI API. It gives you data plus layout information in one call.
Do you need real-time notifications when data changes? Use the Streaming API — specifically Change Data Capture for data sync or Platform Events for custom messaging.
Do you need to run Salesforce reports from an external application? Use the Reports and Dashboards API. It lets you run saved reports with dynamic filters.
Do you need AI predictions or text generation? Use the Models API or the Einstein Connect API.
Project: Use the Tooling API to Create an Apex Trigger Dynamically
Let us put what we have learned into practice. In this project, we will authenticate with Salesforce, use the Tooling API to create an Apex trigger on the Account object, and then verify that the trigger was created successfully. We will do this entirely through API calls — no IDE, no Salesforce CLI, no Developer Console.
Prerequisites
- A Salesforce Developer Edition org or scratch org
- A Connected App with OAuth 2.0 enabled (the callback URL can be
https://login.salesforce.com/services/oauth2/success) curlor Postman (we will usecurlin the examples)
Step 1: Authenticate with OAuth 2.0 (Username-Password Flow for Simplicity)
For this demo, we will use the Username-Password flow. In production, use JWT Bearer or Web Server flow.
curl -X POST https://login.salesforce.com/services/oauth2/token \
-d "grant_type=password" \
-d "client_id=YOUR_CONSUMER_KEY" \
-d "client_secret=YOUR_CONSUMER_SECRET" \
-d "username=YOUR_USERNAME" \
-d "password=YOUR_PASSWORD_PLUS_SECURITY_TOKEN"
The response looks like:
{
"access_token": "00Dxx0000001gPL!AQ...",
"instance_url": "https://yourInstance.salesforce.com",
"id": "https://login.salesforce.com/id/00Dxx0000001gPL/005xx000001Sv1A",
"token_type": "Bearer",
"issued_at": "1716000000000"
}
Save the access_token and instance_url — we need them for every subsequent call.
export ACCESS_TOKEN="00Dxx0000001gPL!AQ..."
export INSTANCE_URL="https://yourInstance.salesforce.com"
Step 2: Create the Apex Trigger via the Tooling API
We will create a simple trigger that sets the Description field on Account whenever a new Account is created:
curl -X POST "$INSTANCE_URL/services/data/v60.0/tooling/sobjects/ApexTrigger" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"Name": "SetAccountDescription",
"TableEnumOrId": "Account",
"Body": "trigger SetAccountDescription on Account (before insert) {\n for (Account acc : Trigger.new) {\n if (String.isBlank(acc.Description)) {\n acc.Description = '\''Account created via API on '\'' + String.valueOf(Date.today());\n }\n }\n}",
"ApiVersion": 60.0
}'
Let us break down the key fields:
- Name: The trigger’s name. Must be a valid Apex identifier.
- TableEnumOrId: The object the trigger is on. Use the API name (
Account,Contact,Custom_Object__c) or the object’s 15/18-character ID. - Body: The full trigger source code. This is compiled synchronously — if there is a compile error, the API returns it immediately.
- ApiVersion: The Apex API version for compilation.
A successful response returns the new trigger’s ID:
{
"id": "01qxx000000XXXXAAA",
"success": true,
"errors": []
}
If there is a compile error, the response includes the error details:
{
"message": "SetAccountDescription: line 3, column 45: unexpected token: 'Account'",
"errorCode": "INVALID_FIELD"
}
Step 3: Verify the Trigger Exists
Query the Tooling API to confirm the trigger was created:
curl -G "$INSTANCE_URL/services/data/v60.0/tooling/query/" \
--data-urlencode "q=SELECT Id, Name, Body, Status, TableEnumOrId FROM ApexTrigger WHERE Name = 'SetAccountDescription'" \
-H "Authorization: Bearer $ACCESS_TOKEN"
Response:
{
"size": 1,
"totalSize": 1,
"done": true,
"records": [
{
"attributes": {
"type": "ApexTrigger",
"url": "/services/data/v60.0/tooling/sobjects/ApexTrigger/01qxx000000XXXXAAA"
},
"Id": "01qxx000000XXXXAAA",
"Name": "SetAccountDescription",
"Body": "trigger SetAccountDescription on Account (before insert) {\n for (Account acc : Trigger.new) {\n if (String.isBlank(acc.Description)) {\n acc.Description = 'Account created via API on ' + String.valueOf(Date.today());\n }\n }\n}",
"Status": "Active",
"TableEnumOrId": "Account"
}
]
}
The trigger is active and compiled. Its Status is Active, which means it will fire on the next Account insert.
Step 4: Test the Trigger
Create a new Account through the REST API and verify that the trigger populated the Description:
curl -X POST "$INSTANCE_URL/services/data/v60.0/sobjects/Account/" \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"Name": "API Test Account"}'
Then retrieve the Account to check the Description:
curl -G "$INSTANCE_URL/services/data/v60.0/sobjects/Account/001xx000003NEW_ID" \
--data-urlencode "fields=Name,Description" \
-H "Authorization: Bearer $ACCESS_TOKEN"
Expected response:
{
"Name": "API Test Account",
"Description": "Account created via API on 2026-05-20"
}
The trigger fired and populated the Description field automatically.
Step 5: Clean Up (Optional)
If you want to delete the trigger after testing:
curl -X DELETE "$INSTANCE_URL/services/data/v60.0/tooling/sobjects/ApexTrigger/01qxx000000XXXXAAA" \
-H "Authorization: Bearer $ACCESS_TOKEN"
What We Learned from This Project
- How to authenticate with Salesforce using OAuth 2.0
- How the Tooling API differs from the Metadata API — it compiles synchronously and is ideal for individual component operations
- How to create Apex code dynamically without an IDE or CLI
- How to verify metadata through Tooling API queries
- How to chain REST API and Tooling API calls for end-to-end testing
This pattern is useful for building custom developer tools, automated org provisioning scripts, and CI/CD pipelines that need fine-grained control over individual metadata components.
Common Authentication Patterns Across APIs
Regardless of which API you use, authentication follows the same OAuth 2.0 framework. Here is a summary of what works where:
| Flow | REST | SOAP | Bulk | Tooling | Metadata | Streaming | UI | Reports | Models |
|---|---|---|---|---|---|---|---|---|---|
| Web Server | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| JWT Bearer | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Client Credentials | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Username-Password | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
| Device | Yes | No | No | Yes | No | No | Yes | Yes | Yes |
The SOAP API also supports direct login() with username and password (returning a session ID), which predates OAuth. The session ID obtained this way can be used with any API.
Rate Limits and Governance
Each API has its own limits, but they often share a common pool:
- REST, SOAP, Bulk, Tooling, UI, Reports, Models: All count against the org’s total API request limit (e.g., 100,000 per 24 hours for Enterprise Edition base, plus per-user additions).
- Bulk API: Has additional limits — 15,000 batches per 24 hours, 150 MB per batch upload.
- Streaming API: Limited by the number of concurrent CometD clients (typically 2,000 for Enterprise Edition) and daily delivered events (varies by event type).
- Metadata API: Deploy operations count as one API call regardless of how many components are in the package. Retrieves also count as one call.
Monitor your consumption regularly through the REST API’s /limits/ endpoint or in Setup under System Overview.
Error Handling Best Practices
Across all Salesforce APIs, you will encounter these common error patterns:
HTTP Status Codes
- 200 OK: Success (GET, PATCH)
- 201 Created: Successfully created a new record (POST)
- 204 No Content: Successfully deleted (DELETE)
- 400 Bad Request: Malformed request (check your JSON, field names, or SOQL syntax)
- 401 Unauthorized: Invalid or expired access token (re-authenticate)
- 403 Forbidden: The user does not have permission for the requested operation
- 404 Not Found: The record, object, or endpoint does not exist
- 429 Too Many Requests: You have exceeded a rate limit (back off and retry)
- 500 Internal Server Error: Something went wrong on Salesforce’s side (retry with exponential backoff)
Error Response Format
REST API errors include an error code and message:
[
{
"message": "Session expired or invalid",
"errorCode": "INVALID_SESSION_ID"
}
]
Always check for INVALID_SESSION_ID and implement token refresh logic. For bulk operations, check individual record results — a bulk call can partially succeed (some records succeed, others fail).
Retry Strategy
Implement exponential backoff for transient errors (429, 500, 503). A simple pattern:
- First retry after 1 second
- Second retry after 2 seconds
- Third retry after 4 seconds
- Give up after 3-5 retries
For Bulk API jobs, poll with reasonable intervals (5-10 seconds) rather than hammering the status endpoint.
Summary
Salesforce exposes a rich set of APIs, each optimized for a specific interaction pattern. Here is what we covered:
- REST API — The default choice for most integrations. JSON-based, well-documented, supports CRUD, queries, composite operations, and more.
- SOAP API — XML-based with WSDL contracts. Use it for legacy systems or when strong typing is required.
- Bulk API 2.0 — Asynchronous, CSV-based, built for high-volume data operations. Use it when dealing with thousands or millions of records.
- Tooling API — For querying and manipulating metadata, executing anonymous Apex, and building developer tools.
- Metadata API — For deploying and retrieving metadata packages. The backbone of CI/CD in Salesforce.
- UI API — Returns data with layout information. Use it when building custom UIs that should respect admin configuration.
- Streaming API — Real-time push notifications via CometD. Supports PushTopic events, Change Data Capture, Platform Events, and Generic Events.
- Reports and Dashboards API — Run saved reports with dynamic filters and retrieve dashboard data programmatically.
- Models API — Access Einstein AI for predictions, text generation, and embeddings.
Understanding which API to use for which scenario is a foundational skill for any Salesforce developer or architect. The APIs overlap in some areas (you can read records through REST, SOAP, Bulk, Tooling, and UI APIs), but each has a sweet spot that makes it the right tool for the job.
Next up — Part 61: Object Oriented Concepts in Apex. We will dive into classes, interfaces, abstract classes, inheritance, polymorphism, and encapsulation in the Apex programming language. If you have been writing Apex procedurally so far, Part 61 will transform how you structure your code. See you there.