Our trigger logic has some recursion detection built in. Essentially, we have some static variables that act as counters for trigger executions. When our set "max" gets hit, our triggers quit to avoid issues with recursive triggers and governor limits.

We found out the hard way that static variables keep their state across individual Bulk API transactions. This happens in both parallel and non-parallel mode. That means our counters keep getting incremented as triggers fire because the variables are never reset. This is strange because governor limits are reset for each individual transaction like you would expect them too.

Has anyone else experienced this? Is there any documentation around this that would explain the behavior of static variables during Bulk API transactions?

Also, within a trigger, is there a way to detect whether the current transaction is running within the Bulk API? If there were, we could come up with some sort of custom solution just for the Bulk API context.

  • Not sure how useful this is, but you could clear your trigger counter if it is older than say, 1 minute. You'd just have to add one more static field with a last modified datetime. Commented Jul 28, 2014 at 21:38
  • I just ran into the same thing yesterday. (a bit different use case -- 're-entrant flag' where 1st trigger edits a 2nd obj, and 2nd trigger edits 1st obj, and don't want to get into a trigger loop). I feel like this is new, primarily because I've written code like this in the past and have never seen this issue. We have a case w/ sfdc so I'll let you know what we learn. Commented Aug 6, 2014 at 15:31
  • we haven't heard anything yet. Anything for you, Kevin? (we were able to avoid the bulk api since that sobj/table only had 7000 records in it) Commented Aug 12, 2014 at 21:37
  • Nope, nothing on our end. Commented Aug 14, 2014 at 1:40

4 Answers 4


Has anyone else experienced this?

I have and to say it was quite annoying was an understatement. I had to call an update trigger either after it was inserted or after it's second update. I got it to work fine, but as soon as the records were inserted through DataLoader, it bombed because of this issue.

I asked a similar question and the response reflected the following:

Answered by @ScottW

From the documentation:

Use static variables to store information that is shared within the confines of the class. All instances of the same class share a single copy of the static variables. For example, all triggers that are spawned by the same transaction can communicate with each other by viewing and updating static variables in a related class. A recursive trigger might use the value of a class variable to determine when to exit the recursion.

So apparently, even though the trigger limits are being reset, it is still considering a bulk API call a single transaction. For example, a bulk API insert of 700 records is broken up into 200 records to be worked on by trigger, each batch is given of 200 records is given its own API limits. However, this is still one transaction, which means the static variables are never reset.

It is actually recommended for complex triggers that you avoid using the Bulk API altogether (this was found here):

Minimize Number of Triggers

You can use parallel mode with objects that have associated triggers if the triggers don't cause side-effects that interfere with other parallel transactions. However, salesforce.com doesn't recommend loading large batches for objects with complex triggers. Instead, you should rewrite the trigger logic as a batch Apex job that is executed after all the data has loaded.

Interestingly, recommendation eh?

Also, within a trigger, is there a way to detect whether the current transaction is running within the Bulk API?

From what I could find, no. The only way I could see to resolve this is possibly a reset for your counter; essentially toggling when a certain event should fire.

I hope that clears some things up.

  • I find it's often useful to create a Trigger "off-switch" that I can use to prevent my triggers from firing when I do bulk uploads. Since the actual "Active" checkbox can't be modified without a deployment, I use custom metadata and an if statement to do it.
    – SFDC Neuf
    Commented Feb 2, 2017 at 17:04

Is there a way to detect whether the current transaction is running within the Bulk API?


Use Url.getCurrentRequestUrl()

 * System.isBatch() //false
 * System.isFuture() //false
 * System.isQueueable() //false
 * System.isScheduled() //false
 * AsyncApiJob KeyPrefix is 750
 * AsyncApiBatch KeyPrefix is 751
 * Example of getCurrentRequestUrl()
 * http://eu6.salesforce.com/mockRequest/bulkapi/process/00D000000000000/750000000000000/751000000000000
Boolean isBulkApi() {
    return Url.getCurrentRequestUrl().getPath().contains('/bulkapi/');

The returned string allows us to infer the Apex Execution Event Type or "Quiddity"


I am also facing same issue but I break down it by using userinfo.getsessionid(). this is I am using to find the processing is of batch type or not, in case of BULK API session id is null so I have handle my code if no session no variable set to true.

Like this

trigger VtSmsDriver on SMS_Bucket__c (before insert,after insert) {
if(trigger.isbefore && trigger.isinsert && VtProcessHelper.getsmsdriverbf()){

    // some code


Or something like this

As salesforce batches records in triggers at 200 each could we not say

if (trigger.new.size() >= 200 && trigger.counter > 0) {
     trigger.counter = 0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .