diff --git a/src/Plugin/AiProvider/OpenAiProvider.php b/src/Plugin/AiProvider/OpenAiProvider.php
index a1c27948d089ddbea7893610345b57883d700196..1efe125895f1f2d5dbc1825a30a459169c8e0615 100644
--- a/src/Plugin/AiProvider/OpenAiProvider.php
+++ b/src/Plugin/AiProvider/OpenAiProvider.php
@@ -633,16 +633,35 @@ class OpenAiProvider extends OpenAiBasedProviderClientBase {
    */
   public function embeddings(string|EmbeddingsInput $input, string $model_id, array $tags = []): EmbeddingsOutput {
     $this->loadClient();
-    // Normalize the input if needed.
+
+    // Determine if this is a batch request before extracting prompt.
+    $isBatch = FALSE;
+    $inputCount = 1;
+    /** @var string|array<int, string> $prompt */
+    $prompt = $input;
     if ($input instanceof EmbeddingsInput) {
-      $input = $input->getPrompt();
+      $isBatch = $input->isBatch();
+      $inputCount = $input->getPromptCount();
+      $prompt = $input->getPrompt();
     }
-    // Moderation.
-    $this->moderationEndpoints($input);
+    elseif (is_string($input)) {
+      $prompt = $input;
+    }
+
+    // Moderation - for batch inputs, check each text individually.
+    if ($isBatch && is_array($prompt)) {
+      foreach ($prompt as $text) {
+        $this->moderationEndpoints($text);
+      }
+    }
+    else {
+      $this->moderationEndpoints($prompt);
+    }
+
     // Send the request.
     $payload = [
       'model' => $model_id,
-      'input' => $input,
+      'input' => $prompt,
     ] + $this->configuration;
     try {
       $response = $this->client->embeddings()->create($payload)->toArray();
@@ -664,6 +683,44 @@ class OpenAiProvider extends OpenAiBasedProviderClientBase {
       }
     }
 
+    // Validate the response structure before processing.
+    if (!isset($response['data']) || !is_array($response['data'])) {
+      $errorMessage = $response['detail'] ?? $response['message'] ?? $response['error'] ?? 'Unknown error';
+      throw new \RuntimeException(sprintf(
+        'Embeddings request failed for %d input(s): %s',
+        $inputCount,
+        is_array($errorMessage) ? json_encode($errorMessage) : $errorMessage
+      ));
+    }
+
+    // Handle batch response - return array of embeddings.
+    if ($isBatch) {
+      $embeddings = [];
+      // OpenAI returns embeddings in 'data' array with 'index' field.
+      // Sort by index to ensure order matches input order.
+      $dataItems = $response['data'];
+      usort($dataItems, fn($a, $b) => ($a['index'] ?? 0) <=> ($b['index'] ?? 0));
+      foreach ($dataItems as $item) {
+        if (isset($item['embedding'])) {
+          $embeddings[] = $item['embedding'];
+        }
+      }
+
+      if (count($embeddings) !== $inputCount) {
+        throw new \RuntimeException(sprintf(
+          'Embeddings API returned %d embeddings for %d inputs',
+          count($embeddings),
+          $inputCount
+        ));
+      }
+
+      return new EmbeddingsOutput($embeddings, $response, ['batch' => TRUE]);
+    }
+
+    // Single embedding response.
+    if (!isset($response['data'][0]['embedding'])) {
+      throw new \RuntimeException('Embeddings response missing embedding data');
+    }
     return new EmbeddingsOutput($response['data'][0]['embedding'], $response, []);
   }
 
