diff --git a/api/v3/Job/ProcessPaymentprocessorWebhooks.php b/api/v3/Job/ProcessPaymentprocessorWebhooks.php
index 85152cb3cd8a63bf55f836bbce92ea1a19384866..414a9e250774f9fabf4c4c3f2edcd4550978e660 100644
--- a/api/v3/Job/ProcessPaymentprocessorWebhooks.php
+++ b/api/v3/Job/ProcessPaymentprocessorWebhooks.php
@@ -41,10 +41,16 @@ function civicrm_api3_job_process_paymentprocessor_webhooks($params) {
       ->addWhere('id', '=', $params['id'])
       ->execute();
   }
+  elseif (!empty($params['event_id'])) {
+    $paymentProcessorWebhooks = PaymentprocessorWebhook::get(FALSE)
+      ->addWhere('event_id', '=', $params['event_id'])
+      ->execute();
+  }
   else {
     $paymentProcessorWebhooks = PaymentprocessorWebhook::get(FALSE)
       ->addWhere('processed_date', 'IS NULL')
       ->addWhere('status', '=', 'new')
+      ->setLimit($params['queue_limit'])
       ->execute();
   }
 
@@ -82,7 +88,7 @@ function civicrm_api3_job_process_paymentprocessor_webhooks($params) {
       $results[$eventResult ? 'successes' : 'errors']++;
     }
     else {
-      // Legacy support.
+      // Legacy support. We need to keep this until Stripe 6.7 is released and is "minimum supported version".
       switch ($paymentProcessor->getPaymentProcessor()['class_name']) {
       case 'Payment_Stripe':
         try {
@@ -133,10 +139,21 @@ function _civicrm_api3_job_process_paymentprocessor_webhooks_spec(&$params) {
   $params['id']['title'] = 'ID of PaymentprocessorWebhook record (for debugging)';
   $params['id']['description'] = 'Specify an ID to FORCE processing and ignore the state of the status/processed_date fields';
   $params['id']['type'] = CRM_Utils_TYPE::T_INT;
+  $params['event_id'] = [
+    'type' => CRM_Utils_Type::T_STRING,
+    'title' => 'Event ID of PaymentprocessorWebhook record (for debugging)',
+    'description' => 'Specify an Event ID to force processing of only that event (and ignore status/processed_date fields)'
+  ];
   $params['time_limit'] = [
-      'type' => CRM_Utils_TYPE::T_INT,
-      'title' => 'Time limit (seconds)',
-      'description' => 'After each event has been processed, we stop to see whether the time limit is exceeded, and stop if so. Useful if your cron is http initiated. Default 1 hour',
-      'api.default' => 60*60,
-    ];
+    'type' => CRM_Utils_TYPE::T_INT,
+    'title' => 'Time limit (seconds)',
+    'description' => 'After each event has been processed, we stop to see whether the time limit is exceeded, and stop if so. Useful if your cron is http initiated. Default 1 hour',
+    'api.default' => 60*60,
+  ];
+  $params['queue_limit'] = [
+    'type' => CRM_Utils_Type::T_INT,
+    'title' => 'Queue limit (count)',
+    'description' => 'Maximum number of webhook events to process each time this job runs. Too many events can cause memory issues and lock the database for too long. Default 1000',
+    'api.default' => 1000,
+  ];
 }