GTM Galaxy

Article

The Bulk API 2.0 Latency Tax: Why Composite REST Wins for Mid-Sized GTM Payloads

Most RevOps teams default to Salesforce Bulk API 2.0 for any data load larger than a single record. We are told it’s the professional way to move data: efficient, governed, and built for scale. While that is true for a million-row warehouse sync, it is often the wrong choice for the operational payloads GTM teams actually move daily.

When you are pushing 1,500 leads from a webinar or updating 5,000 accounts after a territory shift, using the Bulk API 2.0 is like calling a freight train to move a few boxes across town. You spend more time waiting for the train to arrive and the crew to load it than it would have taken to just drive a van.

This is the "Latency Tax." For mid-sized batches between 500 and 10,000 records, the overhead of the asynchronous queue often outweighs the raw throughput of the Bulk engine.

The Mechanics of the Tax

Bulk API 2.0 is asynchronous by design. When you submit a job, you aren't processing data; you are handing Salesforce a CSV and asking for a spot in line. Your client then enters a polling loop—checking every few seconds to see if the job is done.

Salesforce must ingest the file, prepare the internal batch, find a slot in the async queue, process it, and write result logs. In a busy production environment, queueing alone can vary from thirty seconds to several minutes. For an operator triggering real-time lead routing or Slack notifications, that delay is an eternity.

Composite REST requests are synchronous. You send the data, the server processes it immediately, and the response arrives in the same connection. There is no queueing, no polling, and no waiting for a background worker. When the HTTP request finishes, the data is in the database.

The 1,000-Record Single Request

The standard REST API used to be limited to one record per call, making it useless for batching. The composite/sobjects (Collections) endpoint changed that math. You can send up to 200 records in a single subrequest.

There is a specific limit to watch: a single Composite request can hold up to 25 subrequests, but only five of those can be sObject Collections. This creates a hard ceiling for a single synchronous call: 1,000 records (5 collections × 200 records).

If your payload is 1,000 records or fewer, you can complete the entire ingestion in one synchronous round trip. By the time a Bulk API 2.0 job even transitions from "Open" to "InProgress," the REST call has already finished and triggered downstream flows.

Benchmarking the Performance

I ran a series of tests to measure "wall-clock time"—the total duration from the first byte sent to the moment the client receives the final success/failure confirmation for every record.

Payload Size Composite REST (Parallel) Bulk API 2.0 (Ingest) Latency Difference
500 Records ~1.8s ~55s 30x faster
2,500 Records ~2.5s (3 parallel calls) ~62s 24x faster
10,000 Records ~12.2s (10 parallel calls) ~95s 7x faster

The Results

  • At 500 records: Composite REST is the clear winner. The polling overhead of Bulk API makes it feel sluggish and disconnected.
  • At 2,500 records: This requires three Composite calls. Even if run sequentially, it takes roughly 6 seconds. If run in parallel via a simple worker pool or Promise.all(), it stays under 3 seconds. Bulk API remains stuck in its baseline queueing cycle.
  • At 10,000 records: Industry heuristics often suggest switching to Bulk at 2,000 records. My tests challenge this. While Bulk is more "efficient" for Salesforce's internal resources, it is still slower for the GTM engineer. Even at 10,000 records, parallelized REST calls finish in a fraction of the time it takes Bulk API to return a result log.

The Hidden Quota Trap

Critics argue that using REST consumes daily API call limits, which are tighter in smaller Salesforce editions. However, Bulk API 2.0 has its own ceiling: 15,000 ingest jobs per rolling 24-hour window.

If you have a high-frequency GTM pipeline—say, a Clearbit enrichment flow that triggers every time a lead is created—and you use Bulk API for every 100-record micro-batch, you will hit that 15,000-job limit fast. For many mid-market orgs, standard API calls are more plentiful than Bulk jobs. Using 10 REST calls to sync 10,000 records is often a better use of resources than burning a dedicated Bulk job on a mid-sized payload.

Failure Modes and Transaction Control

The choice isn't just about speed; it's about how the system breaks.

Composite REST allows for an allOrNone flag. If one record in a batch of 200 fails a validation rule, the entire batch rolls back. This is vital for maintaining integrity in complex GTM objects. You don't want a situation where a Lead is updated but the corresponding Task fails, leaving your SDRs in the dark.

Bulk API 2.0 handles errors at the row level. If record 500 fails, the other 9,500 succeed. You then have to download a "Failed Records" CSV, parse it, and orchestrate a retry. This is resilient for massive migrations but a headache for operational syncs that require atomic consistency.

When to Stick with Bulk

There are two scenarios where the Latency Tax is worth paying:

  1. Server-Side Locking: If you are updating 10,000 child records that all roll up to a single parent Account, parallel REST calls will likely trigger UNABLE_TO_LOCK_ROW errors as multiple threads fight for the parent record. Bulk API 2.0 is better at managing these internal locks.
  2. Massive Scale: Once you cross the 20,000-record threshold, the complexity of orchestrating dozens of parallel REST calls increases. At this point, the throughput of the Bulk engine begins to justify the queueing wait.

The Decision Matrix

For most GTM engineering tasks, follow these rules:

  • < 1,000 records: Always use Composite REST. It is faster, synchronous, and easier to code (no polling loop required).
  • 1,000 – 10,000 records: Use parallelized Composite REST calls if your daily API quota allows and you need "near-real-time" availability for downstream automations.
  • > 10,000 records: Transition to Bulk API 2.0, or use it for low-priority background syncs where a two-minute delay doesn't impact the business.

Stop putting small operational batches in the slow-moving async queue. If you want your revenue systems to feel snappy, use the right tool for the payload size. Chunk your data, use Composite Collections, and take the carry-on instead of checking your bags.

— C.B.