diff --git a/producer/api/python/asapo_producer.pyx.in b/producer/api/python/asapo_producer.pyx.in
index 684225634a84037bf2655b48ea14f28b60e84381..5135a709d873097ee76a9f576114ffd74bc604e7 100644
--- a/producer/api/python/asapo_producer.pyx.in
+++ b/producer/api/python/asapo_producer.pyx.in
@@ -150,10 +150,13 @@ cdef class PyProducer:
             return {'client': _str(client_info)}
     def __send(self, id, exposed_path, data, user_meta=None,dataset=None,stream="default",ingest_mode = DEFAULT_INGEST_MODE,callback=None, auto_id = False):
         if user_meta!=None:
-            try:
-                json.loads(user_meta)
-            except Exception as ex:
-                raise AsapoWrongInputError("Failed to interpret user_meta as json string: " + str(ex))
+            if isinstance(user_meta, dict):
+                user_meta = json.dumps(user_meta)
+            else:
+                try:
+                    json.loads(user_meta)
+                except Exception as ex:
+                    raise AsapoWrongInputError("Failed to interpret user_meta as json string: " + str(ex))
 
         cdef MessageHeader message_header = self.create_message_header(id,exposed_path,user_meta,dataset,ingest_mode,auto_id)
         try:
@@ -276,7 +279,7 @@ cdef class PyProducer:
          :param data: data to send
          :type data: contiguous buffer like numpy or bytes array, can be None for INGEST_MODE_TRANSFER_METADATA_ONLY ingest mode
          :param user_meta: user metadata, default None
-         :type user_meta: JSON string
+         :type user_meta: JSON string or dictionary
          :param dataset: a tuple with two int values (dataset substream id, amount of dataset substreams), default None
          :type dataset: tuple
          :param ingest_mode: ingest mode flag
@@ -516,7 +519,7 @@ cdef class PyProducer:
          :param exposed_path: Path which will be exposed to consumers
          :type exposed_path: string
          :param user_meta: user metadata, default None
-         :type user_meta: JSON string
+         :type user_meta: JSON string or dictionary
          :param dataset: a tuple with two int values (dataset id, dataset size), default None
          :type dataset: tuple
          :param ingest_mode: ingest mode flag
@@ -539,10 +542,13 @@ cdef class PyProducer:
             _deprecated_api("Using the default value for the 'stream' argument is deprecated since version 24.11.0.")
             stream = "default"
         if user_meta!=None:
-            try:
-                json.loads(user_meta)
-            except Exception as ex:
-                raise AsapoWrongInputError("Failed to interpret user_meta as json string: " + str(ex))
+            if isinstance(user_meta, dict):
+                user_meta = json.dumps(user_meta)
+            else:
+                try:
+                    json.loads(user_meta)
+                except Exception as ex:
+                    raise AsapoWrongInputError("Failed to interpret user_meta as json string: " + str(ex))
         cdef MessageHeader message_header = self.create_message_header(id,exposed_path,user_meta,dataset,ingest_mode,auto_id)
         message_header.data_size = 0
         err = self.c_producer.get().SendFile(message_header, _bytes(local_path), ingest_mode, _bytes(stream),
diff --git a/tests/automatic/producer/python_api/producer_api.py b/tests/automatic/producer/python_api/producer_api.py
index 83bb9d223a51a2580e2412cd97faf4b8b97b6985..1469c1554061a48369852451655a29aab3907cba 100644
--- a/tests/automatic/producer/python_api/producer_api.py
+++ b/tests/automatic/producer/python_api/producer_api.py
@@ -221,6 +221,25 @@ producer.send(
     callback=callback,
 )
 
+# send files with user metadata in dictionary format
+producer.send_file(
+    0,
+    auto_id=True,
+    local_path="./file1",
+    exposed_path="processed/" + data_source + "/" + "file13",
+    user_meta={"test_key": "test_val"},
+    callback=callback,
+)
+
+producer.send(
+    0,
+    "processed/" + data_source + "/" + "file14",
+    b"hello",
+    auto_id=True,
+    user_meta={"test_key": "test_val"},
+    callback=callback,
+)
+
 # wait normal requests finished before sending duplicates
 
 producer.wait_requests_finished(10_000)
diff --git a/tests/automatic/pytests/test_wrong_input.py b/tests/automatic/pytests/test_wrong_input.py
index b05339da57f9c6f7c0561c803ce51329ff93b071..4485cf383537179e0e29fe1946b001ed98b59826 100644
--- a/tests/automatic/pytests/test_wrong_input.py
+++ b/tests/automatic/pytests/test_wrong_input.py
@@ -35,18 +35,7 @@ def test_wrong_send_parameters(producer):
             f"processed/file_{stream}_1.txt",
             b"foo",
             stream=stream,
-            user_meta={"ggg": 123},
-            callback=callback,
-        )
-    assert "Failed to interpret user_meta as json string" in str(err)
-
-    with pytest.raises(asapo_producer.AsapoProducerError) as err:
-        producer.send(
-            1,
-            f"processed/file_{stream}_1.txt",
-            b"foo",
-            stream=stream,
-            user_meta="test",
+            user_meta="invalid_meta",
             callback=callback,
         )
     assert "Failed to interpret user_meta as json string" in str(err)
@@ -57,7 +46,7 @@ def test_wrong_send_parameters(producer):
             f"{__file__}",
             f"processed/file_{stream}_1.txt",
             stream=stream,
-            user_meta={"ggg": 123},
+            user_meta="invalid_meta",
             callback=callback,
         )
     assert "Failed to interpret user_meta as json string" in str(err)