A salesforce integration almost never fails on protocol. Authenticating is a solved problem and posting a record is a solved problem. What ends projects is the daily request allocation and the shape of the data model, both usually discovered about three weeks after go-live, when the nightly job starts returning errors and nobody can say why it worked in testing.
The pattern is consistent enough to predict. A developer builds against a Developer Edition org, everything passes, the client signs off. The code then meets a production org already containing a marketing connector, a warehouse extract and an Apex trigger from 2019, and the request budget that looked generous turns out to be a shared pot other people are already spending.
This article front-loads the surprises: which API you should be using, how the allocation is calculated, what happens when your write triggers code you did not write, how authentication has changed, and the data model decisions that are expensive to reverse.
What decides whether a Salesforce integration succeeds? Quota and data model, not protocol. The daily API request allocation is org-wide and derived from edition and licence count, so a well-behaved integration can be starved by a badly written one in the same org. Design for batching from the first line of code, agree external IDs and upsert before anything is written, and assume every record you send will trigger somebody else’s Apex.
What a Salesforce Integration Has to Get Right
There are four things, and they are not equally weighted.
The first is quota. Every synchronous call your code makes is drawn from a single org-wide daily allocation shared with every other consumer of the org.
The second is the platform underneath. Salesforce is not a database with an HTTP interface. It is an application platform, and your writes execute triggers, flows, validation rules, duplicate rules and roll-up summaries configured by administrators who have never heard of your project.
The third is the data model. Lead, Contact, Account and Opportunity are not interchangeable, conversion between them is one-way and has side effects, and choosing wrongly means a data migration rather than a code change.
The fourth is identity: how your system and Salesforce agree on which record is which. Get it wrong and you create duplicates at machine speed. Everything else here follows from one of those four.
The API Surface, and Which One You Actually Need
Salesforce publishes a large family of APIs. The Salesforce API index is the authoritative list, and the names below come from it rather than from memory.
REST API and SOAP API
REST API is the default for anything record-shaped: create, read, update, delete, query, describe. It is the right choice for a web form writing a Lead, for a portal reading a customer’s open cases, and for any low-volume interactive path.
SOAP API does the same work through a WSDL and survives because a great deal of enterprise middleware speaks it natively, and because it gives you a strongly typed contract to generate a client from. The thing people get wrong is assuming SOAP is legacy and REST modern. Both are current, and SOAP create() and update() accept up to 200 records each, which matters more than the wire format.
Bulk API 2.0
Bulk API 2.0 is the asynchronous, job-based path for volume. You upload a CSV, Salesforce chunks and processes it in the background, and you poll for results. Salesforce’s Bulk API limits allow up to 15,000 batches per rolling 24 hours and up to 150 million records ingested in the same window, with a 150 MB job file ceiling.
The thing people get wrong is treating Bulk as a tuning step for later. It is a different programming model: results come back asynchronously, per record, and your code has to consume them that way from the start.
Composite and sObject Collections
These two are the highest-value and least-used part of the REST API. A composite request carries up to 25 subrequests in a single call, of which up to 5 can be queries or sObject Collections operations, and later subrequests can reference IDs returned by earlier ones. sObject Collections handles up to 200 records of the same object in one request. Both count as a single call against your daily allocation, which is the entire point.
The thing people get wrong is not knowing they exist. Creating an Account, then a Contact, then an Opportunity as three sequential calls costs three times the quota of one composite request, and three times the latency.
Streaming, Change Data Capture and Pub/Sub
Streaming API is the CometD-based subscription channel for PushTopic, generic, platform and change events. Change Data Capture publishes near real time notifications when records are created, updated, deleted or undeleted, so an external store can follow Salesforce without polling. Platform Events are your own custom event definitions.
Pub/Sub API is the newer gRPC and HTTP/2 interface consolidating publish, subscribe, schema retrieval and topic discovery into one API, with payloads in Avro rather than JSON. For a new event-driven build, start there.
The thing people get wrong is treating events as a guaranteed feed. They do not replace reconciliation, for the reason in the next section.
API Request Limits Are the Real Constraint
This is the section that decides your architecture, and it is the one most often read after the design is finished.
How the daily allocation is calculated
Salesforce’s API request limits documentation sets the allocation by edition and licence count, not by user or by application. Enterprise and Professional editions with API access get 100,000 calls plus 1,000 per Salesforce or Salesforce Platform licence. Unlimited and Performance editions get 100,000 plus 5,000 per licence. Developer Edition gets a flat 15,000, and a Full sandbox gets 5,000,000.
Two things follow. A 60 user Enterprise org has roughly 160,000 calls a day, not an unlimited supply. And because the allocation derives from licences, the only ways to raise it are more user licences or extra API calls, both bought through Salesforce’s Your Account app.
What counts, and what happens when you run out
The allocation is measured against the aggregate of all calls to the org in a 24 hour period, covering REST API, SOAP API, Bulk API, Bulk API 2.0 and most Connect REST API calls together. Calls from certain Salesforce connected apps, such as the mobile app, are excluded.
That aggregation is what catches people. Your integration has no budget of its own. It shares one with the reporting connector, the marketing platform and every other integration in the org, and a single badly written consumer polling every thirty seconds can drain it and starve code that is behaving perfectly.
When the org exceeds its allocation, requests fail with a 403 and REQUEST_LIMIT_EXCEEDED. Salesforce allows paid production orgs some overage before enforcing hard, but trial and Developer Edition orgs get no such grace. Design as though there is none.
Measure before you commit to a design
Get the org’s allocation and current daily consumption from the administrator before writing code; the REST API exposes an org limits resource for it. If existing consumers already use 70%, a synchronous per-record integration is not viable and tuning will not make it so.
Batching Is a Design Decision, Not an Optimisation
Once you accept that quota is finite and shared, the design falls out on its own.
Never loop a single-record call. A job creating 5,000 Contacts one at a time spends 5,000 calls. The same 5,000 through sObject Collections at 200 per request spend 25. That factor of 200 is the difference between an integration that fits inside a mid-sized org’s allocation and one that does not.
Use Composite where the work is a graph rather than a list. Creating a parent and its children in one request removes both the round trips and the intermediate state your code would otherwise hold while waiting for an ID.
Use Bulk API 2.0 for anything resembling a load or an export rather than a transaction. It is wrong for interactive paths, being asynchronous by design and returning no synchronous answer for the user.
Cache reference data. Picklist values, record type IDs and describe results change rarely and get re-fetched every run for no reason. That one change often removes a quarter of a naive integration’s call volume.
Governor Limits: Your Write Runs Someone Else’s Code
Salesforce executes customer-written Apex inside strict per-transaction ceilings. The Apex governor limits that matter to an integration are 100 SOQL queries per synchronous transaction, 50,000 records retrieved by SOQL, 150 DML statements, 10,000 records processed by DML, 10 seconds of synchronous CPU time and 6 MB of heap.
You do not write that Apex. You still hit those limits, because your inbound write starts a transaction running whatever triggers exist on the object.
Bulkification, without the Apex
The idea is worth understanding even if you never open an Apex file.
Salesforce hands a trigger a collection of records, not one record. A trigger written correctly processes the whole collection with one query and one update. A trigger written as though it always receives a single record runs one query and one update per record.
That second trigger works perfectly for years, because users save records one at a time through the interface. Then your integration sends 200 records in one request, the trigger runs its query 200 times, blows past the 100 query ceiling, and the whole batch fails.
Bulk API 2.0 processes ingest data in chunks of 200 records, each a separate transaction, so this is not theoretical. It is the standard shape of a first bulk load into an org with history.
What to do about it
Audit the triggers and flows on every object you will write to, before you agree a delivery date. If a trigger is not bulkified, someone has to fix it, and that someone needs Apex skills and a deployment window. Budget it as a line item.
Where a fix is out of scope, reduce batch size. Two hundred per request is a maximum, not a requirement, and dropping to 50 sometimes buys enough headroom to ship while the trigger work is scheduled. It costs quota, so treat it as temporary.
Authentication That Will Still Work Next Year
This area has changed materially and a lot of published guidance is now wrong.
The OAuth 2.0 username-password flow is the one to avoid. It exposes credentials directly in the request, Salesforce blocks it by default in newer orgs, and its retirement for connected apps is scheduled. Any integration still using it needs a migration plan with a date attached.
For server to server work with no human in the loop the two current answers are the JWT bearer flow, which signs an assertion with a certificate, and the client credentials flow, which exchanges a consumer key and secret for a token. Salesforce’s guidance on invoking REST APIs with the integration user and client credentials is explicit that this flow issues no refresh token, so the client requests a new access token when the old one expires.
Connected apps and external client apps
The container for all of this used to be the connected app. It is now the external client app. Salesforce states plainly that connected app creation is restricted as of Spring ‘26 and recommends external client apps instead, describing them as the new generation designed to improve security and resolve packaging problems.
If your integration documentation says “create a connected app”, it describes a path new orgs may not offer. Check which applies to the target org before scoping the work.
Run as a dedicated integration user, and plan for rotation
Give the integration its own user with a minimum-access, API-only profile. Do not run it as a named employee. When that employee leaves and their account is deactivated the integration stops, at the worst possible moment and with an error that points nowhere useful.
Certificates expire and secrets get rotated. Both are silent until the day they are not, and both take an integration offline completely rather than partially. Put the expiry dates in a calendar owned by a person, store credentials in a secret manager, and test the rotation in a sandbox before you need it in anger.
The Data Model Traps
These are the ones that cost three weeks, because reversing them means moving data rather than changing code.
Lead, Contact, Account and Person Account
A Lead is an unqualified prospect not yet connected to a company record. A Contact is a person attached to an Account. An Account is an organisation. Conversion turns a Lead into an Account and a Contact, optionally with an Opportunity, and the SOAP convertLead call is explicit that only empty fields on the target are overwritten, so your carefully populated Lead fields may not land where you expect.
Person Accounts complicate this. Business-to-consumer orgs enable them so an individual is represented as a combined Account and Contact, and an integration written against a business Account model will not work unmodified against an org using them. This is discovered late with dispiriting regularity.
Decide with the business, in writing, which object a given inbound record becomes. It is not a technical decision.
External IDs and upsert
This is the only sane idempotency mechanism Salesforce gives you, and it should be non-negotiable. Create a custom field on the object marked as an External ID and store your system’s own primary key in it. You can then use the upsert operation, a PATCH against /sobjects/{Object}/{ExternalIdField}/{Value}, which creates the record when nothing matches and updates it when exactly one does. Zero matches returns 201, one match returns 200, and multiple matches fail with a 300 rather than guessing.
The consequence is worth stating plainly. With upsert, retrying a failed request is safe. Without it every retry is a potential duplicate, and a network blip during a nightly job becomes a cleanup exercise measured in days.
Rules that fire on your writes
Duplicate rules can block or alert on records your integration creates. Validation rules reject records failing conditions an administrator configured. Required fields can be added months after you ship, at which point a working integration starts failing on every record.
None of these are bugs; they are the org working as configured. The mistake is treating a rejected write as a transport error and retrying forever, when the correct response is to surface it to a human with the field-level reason attached. Our guide to third-party API integration failure modes covers the same category elsewhere.
Error Handling, Idempotency and Replay
An integration without a replay mechanism becomes a manual data repair job. That is not a prediction, it is what happens.
Partial success is the normal case. sObject Collections defaults allOrNone to false, so a 200-record request can return 187 successes and 13 failures with individual reasons, and Bulk API 2.0 returns per-record results the same way. Code checking only the outer HTTP status will report success while silently dropping records.
Classify failures before retrying. Transient conditions such as row locks, timeouts and quota exhaustion deserve exponential backoff. Deterministic failures such as validation errors and missing required fields will fail identically forever, and retrying them burns quota you cannot spare.
Every unrecoverable record goes to a dead letter store with its payload and its error, for a person to inspect and resubmit. Because your writes are keyed on an external ID, resubmission is safe. Log the correlation between your identifier and the Salesforce ID on both sides; in six months that log is the only thing that will explain why one customer’s record is wrong.
And reconcile. Platform and change events are retained in the event bus for 72 hours, and Salesforce’s platform event allocations cap daily delivery at 25,000 events on Enterprise and 50,000 on Unlimited and Performance. A scheduled comparison of record counts and modified dates catches what the stream dropped.
Middleware or Direct
Point to point is correct more often than platform vendors admit. One source, one target, one direction, modest volume, a stable contract: build it directly and skip the licence.
Middleware earns its cost when the topology stops being a line. Several systems exchanging data, transformations business users need to change without a deployment, orchestration across systems that fail independently, a real need for centralised monitoring and retry.
The honest note is that middleware moves the cost rather than removing it. You still pay for the mapping, the error handling and the operational knowledge, and you add a licence, a second pipeline and a second skill set to hire for. The API allocation does not change, because middleware calls the same APIs your code would have.
Choose it because your topology needs it, not because it appears to reduce code. Our build versus buy decision guide works through the same trade-off for the underlying systems, and the CRM and ERP integration guide covers the multi-system case. If you want this assessed rather than argued about, that is where our software development engagements start.
Sandboxes, Deployment and API Versions
Build in a sandbox. Never against production, and never against a Developer Edition org that does not share the production configuration, because the configuration is what will break you.
Understand what a refresh does. It replaces the sandbox with a fresh copy from production, so any test data that existed only there is gone. Anything that must survive a refresh has to be scripted and re-runnable. Teams learn this by losing a week of test fixtures.
Pin your API version explicitly in every request path and know the retirement policy. Salesforce’s API end-of-life policy commits to supporting each version for a minimum of three years and to notifying customers at least a year before support ends. Versions 21.0 through 30.0 were retired in Summer ‘25, and requests against a retired version return 410 Gone.
That is a hard stop, not a degradation, which is why version pinning belongs in your maintenance plan. The same discipline applies to any API you publish yourself, as covered in our post on API versioning.
UK Data Protection and CRM Data
A CRM is almost entirely personal data: names, employers, phone numbers, email addresses, notes about conversations. Moving it between systems is processing under the UK GDPR.
The first question is who is the controller. The ICO’s guidance on controllers and processors defines a controller as the party determining the purposes and means of processing, and a processor as one processing on the controller’s behalf. When an agency builds and operates an integration for you, that agency is usually a processor, and a written contract meeting the Article 28 requirements is mandatory rather than optional.
The second is international transfer. Salesforce orgs and any middleware may sit outside the UK, and the ICO’s international transfers guidance sets out the available mechanisms and when a transfer risk assessment is needed. Establish where the data lands before signing.
Three consequences follow. Do not copy fields you do not need, because minimisation is both a legal requirement and less mapping work. Do not put production personal data in a sandbox without a considered decision. And make sure deletion propagates, because a contact erased in Salesforce and left intact in your warehouse is a live compliance problem, and the same applies when AI agents reach those records.
What a Salesforce Integration Costs
Salesforce publishes its own edition and licence pricing on its pricing pages, and we quote no figure for it here, because the number governing integration design is the API allocation those licences produce rather than the list price.
The figures below are Mecanik’s own UK professional services estimates, not vendor pricing, and assume an org that already exists with an administrator who can answer questions.
A simple one-way integration, a website form creating a Lead with an external ID and sensible error handling, typically runs GBP 3,000 to GBP 7,000. A bidirectional synchronisation of one or two objects with conflict resolution and a reconciliation job is usually GBP 15,000 to GBP 40,000. An event-driven integration on Pub/Sub with replay, dead letter handling and monitoring generally lands between GBP 30,000 and GBP 80,000.
What should be in the deliverable
A field-level mapping document agreed with the business rather than inferred. An external ID on every synchronised object. Error handling with a dead letter store and a documented resubmission process. A reconciliation job. Monitoring of API consumption against the org allocation, alerting well below the ceiling. Runbooks for token rotation, certificate expiry and replay. Sandbox configuration scripted so it survives a refresh. An integration without those is a prototype, whatever the invoice says.
Ongoing cost
Budget GBP 400 to GBP 1,500 a month for monitoring, three Salesforce releases a year, credential rotation and the field changes an administrator will make without telling you. The orgs that fund this are the orgs where integrations keep working.
Getting It Built
The failure modes here are boringly consistent: quota discovered late, a trigger nobody audited, a Lead that should have been a Contact, no way to replay a failed batch. All four are cheap to prevent at design time and expensive to fix once real records exist.
Mecanik builds and maintains Salesforce integrations as part of our software development work, starting with an audit of the org’s allocation, triggers and data model before any code is written. For a defined piece of integration work rather than a full engagement, you can hire a web developer directly.
Frequently Asked Questions
How many API calls does a Salesforce integration get per day? It depends on edition and licence count, and the allocation is org-wide rather than per integration. Enterprise and Professional editions with API access get 100,000 calls plus 1,000 per Salesforce or Salesforce Platform licence, Unlimited and Performance get 100,000 plus 5,000 per licence, and Developer Edition gets a flat 15,000 in a rolling 24 hour period.
Should I use REST API or Bulk API for Salesforce? Use REST API for interactive, low-volume, record-shaped work, and Bulk API 2.0 for loads and exports where volume is high and an asynchronous answer is acceptable. For anything in between, sObject Collections at 200 records per request and Composite at 25 subrequests per request both count as a single call against your allocation and remove most of the pressure.
Which OAuth flow should a server-to-server Salesforce integration use? The JWT bearer flow or the client credentials flow, running as a dedicated API-only integration user. The username-password flow is blocked by default in newer orgs and scheduled for retirement, so it should not be used for new work. Note also that connected app creation is restricted as of Spring ‘26 and Salesforce now recommends external client apps.
Why does my Salesforce integration fail only on large batches? Almost always an Apex trigger that was not bulkified. Salesforce passes triggers a collection of records, and a trigger written as though it receives one record at a time will run its queries once per record, exceeding the 100 SOQL query limit when your batch arrives. It works fine for interface users saving one record at a time, which is why it survived undetected.
Do I need middleware to integrate with Salesforce? Not for a single source, single target, one-directional integration at modest volume, where building directly is cheaper and simpler. Middleware earns its cost once several systems exchange data, business users need to change mappings without a deployment, or you need centralised orchestration and retry. It moves cost rather than removing it, and it does not increase your API allocation.
Comments