diff --git a/include/triton/core/tritonserver.h b/include/triton/core/tritonserver.h
index ef5a45d6a..d9701e890 100644
--- a/include/triton/core/tritonserver.h
+++ b/include/triton/core/tritonserver.h
@@ -64,6 +64,7 @@ struct TRITONSERVER_Server;
 struct TRITONSERVER_ServerOptions;
 struct TRITONSERVER_Metric;
 struct TRITONSERVER_MetricFamily;
+struct TRITONSERVER_MetricArgs;
 
 ///
 /// TRITONSERVER API Version
@@ -91,7 +92,7 @@ struct TRITONSERVER_MetricFamily;
 ///   }
 ///
 #define TRITONSERVER_API_VERSION_MAJOR 1
-#define TRITONSERVER_API_VERSION_MINOR 33
+#define TRITONSERVER_API_VERSION_MINOR 34
 
 /// Get the TRITONBACKEND API version supported by the Triton shared
 /// library. This value can be compared against the
@@ -2615,7 +2616,8 @@ TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_ServerInferAsync(
 ///
 typedef enum TRITONSERVER_metrickind_enum {
   TRITONSERVER_METRIC_KIND_COUNTER,
-  TRITONSERVER_METRIC_KIND_GAUGE
+  TRITONSERVER_METRIC_KIND_GAUGE,
+  TRITONSERVER_METRIC_KIND_HISTOGRAM
 } TRITONSERVER_MetricKind;
 
 /// Create a new metric family object. The caller takes ownership of the
@@ -2644,6 +2646,44 @@ TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricFamilyNew(
 TRITONSERVER_DECLSPEC struct TRITONSERVER_Error*
 TRITONSERVER_MetricFamilyDelete(struct TRITONSERVER_MetricFamily* family);
 
+/// Get the TRITONSERVER_MetricKind of the metric family.
+///
+/// \param family The metric family object to query.
+/// \param kind Returns the TRITONSERVER_MetricKind of metric.
+/// \return a TRITONSERVER_Error indicating success or failure.
+TRITONSERVER_DECLSPEC struct TRITONSERVER_Error*
+TRITONSERVER_GetMetricFamilyKind(
+    struct TRITONSERVER_MetricFamily* family, TRITONSERVER_MetricKind* kind);
+
+/// Create a new metric args object. The caller takes ownership of the
+/// TRITONSERVER_MetricArgs object and must call TRITONSERVER_MetricArgsDelete
+/// to release the object.
+///
+/// \param args Returns the new metric args object.
+/// \return a TRITONSERVER_Error indicating success or failure.
+TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricArgsNew(
+    struct TRITONSERVER_MetricArgs** args);
+
+/// Set metric args with histogram metric parameter.
+///
+/// \param args The metric args object to set.
+/// \param buckets The array of bucket boundaries for the expected range of
+/// observed values.
+///
+/// \param buckets_count The number of bucket boundaries.
+/// \return a TRITONSERVER_Error indicating success or failure.
+TRITONSERVER_DECLSPEC struct TRITONSERVER_Error*
+TRITONSERVER_MetricArgsSetHistogram(
+    struct TRITONSERVER_MetricArgs* args, const double* buckets,
+    const uint64_t buckets_count);
+
+/// Delete a metric args object.
+///
+/// \param args The metric args object.
+/// \return a TRITONSERVER_Error indicating success or failure.
+TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricArgsDelete(
+    struct TRITONSERVER_MetricArgs* args);
+
 /// Create a new metric object. The caller takes ownership of the
 /// TRITONSERVER_Metric object and must call
 /// TRITONSERVER_MetricDelete to release the object. The caller is also
@@ -2661,6 +2701,28 @@ TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricNew(
     struct TRITONSERVER_MetricFamily* family,
     const struct TRITONSERVER_Parameter** labels, const uint64_t label_count);
 
+/// Create a new metric object. The caller takes ownership of the
+/// TRITONSERVER_Metric object and must call
+/// TRITONSERVER_MetricDelete to release the object. The caller is also
+/// responsible for ownership of the labels passed in.
+/// Each label can be deleted immediately after creating the metric with
+/// TRITONSERVER_ParameterDelete if not re-using the labels.
+/// Metric args can be deleted immediately after creating the metric with
+/// TRITONSERVER_MetricArgsDelete if not re-using the metric args.
+///
+/// \param metric Returns the new metric object.
+/// \param family The metric family to add this new metric to.
+/// \param labels The array of labels to associate with this new metric.
+/// \param label_count The number of labels.
+/// \param args Metric args that store additional arguments to construct
+/// particular metric types, e.g. histogram.
+/// \return a TRITONSERVER_Error indicating success or failure.
+TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricNewWithArgs(
+    struct TRITONSERVER_Metric** metric,
+    struct TRITONSERVER_MetricFamily* family,
+    const struct TRITONSERVER_Parameter** labels, const uint64_t label_count,
+    const struct TRITONSERVER_MetricArgs* args);
+
 /// Delete a metric object.
 /// All TRITONSERVER_Metric* objects should be deleted BEFORE their
 /// corresponding TRITONSERVER_MetricFamily* objects have been deleted.
@@ -2705,7 +2767,17 @@ TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricIncrement(
 TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricSet(
     struct TRITONSERVER_Metric* metric, double value);
 
-/// Get the TRITONSERVER_MetricKind of metric and its corresponding family.
+/// Sample an observation and count it to the appropriate bucket of a metric.
+/// Supports metrics of kind TRITONSERVER_METRIC_KIND_HISTOGRAM and returns
+/// TRITONSERVER_ERROR_UNSUPPORTED for unsupported TRITONSERVER_MetricKind.
+///
+/// \param metric The metric object to update.
+/// \param value The amount for metric to sample observation.
+/// \return a TRITONSERVER_Error indicating success or failure.
+TRITONSERVER_DECLSPEC struct TRITONSERVER_Error* TRITONSERVER_MetricObserve(
+    struct TRITONSERVER_Metric* metric, double value);
+
+/// Get the TRITONSERVER_MetricKind of metric of its corresponding family.
 ///
 /// \param metric The metric object to query.
 /// \param kind Returns the TRITONSERVER_MetricKind of metric.
diff --git a/src/metric_family.cc b/src/metric_family.cc
index 8132eab25..13fa23ca3 100644
--- a/src/metric_family.cc
+++ b/src/metric_family.cc
@@ -1,4 +1,5 @@
-// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights
+// reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions
@@ -54,6 +55,12 @@ MetricFamily::MetricFamily(
                                              .Help(description)
                                              .Register(*registry));
       break;
+    case TRITONSERVER_METRIC_KIND_HISTOGRAM:
+      family_ = reinterpret_cast<void*>(&prometheus::BuildHistogram()
+                                             .Name(name)
+                                             .Help(description)
+                                             .Register(*registry));
+      break;
     default:
       throw std::invalid_argument(
           "Unsupported kind passed to MetricFamily constructor.");
@@ -63,11 +70,17 @@ MetricFamily::MetricFamily(
 }
 
 void*
-MetricFamily::Add(std::map<std::string, std::string> label_map, Metric* metric)
+MetricFamily::Add(
+    std::map<std::string, std::string> label_map, Metric* metric,
+    const TritonServerMetricArgs* args)
 {
   void* prom_metric = nullptr;
   switch (kind_) {
     case TRITONSERVER_METRIC_KIND_COUNTER: {
+      if (args != nullptr) {
+        throw std::invalid_argument(
+            "Unexpected args found in counter Metric constructor.");
+      }
       auto counter_family_ptr =
           reinterpret_cast<prometheus::Family<prometheus::Counter>*>(family_);
       auto counter_ptr = &counter_family_ptr->Add(label_map);
@@ -75,12 +88,31 @@ MetricFamily::Add(std::map<std::string, std::string> label_map, Metric* metric)
       break;
     }
     case TRITONSERVER_METRIC_KIND_GAUGE: {
+      if (args != nullptr) {
+        throw std::invalid_argument(
+            "Unexpected args found in gauge Metric constructor.");
+      }
       auto gauge_family_ptr =
           reinterpret_cast<prometheus::Family<prometheus::Gauge>*>(family_);
       auto gauge_ptr = &gauge_family_ptr->Add(label_map);
       prom_metric = reinterpret_cast<void*>(gauge_ptr);
       break;
     }
+    case TRITONSERVER_METRIC_KIND_HISTOGRAM: {
+      if (args == nullptr) {
+        throw std::invalid_argument(
+            "Bucket boundaries not found in Metric args.");
+      }
+      if (args->kind() != TRITONSERVER_METRIC_KIND_HISTOGRAM) {
+        throw std::invalid_argument("Metric args not set to histogram kind.");
+      }
+      auto histogram_family_ptr =
+          reinterpret_cast<prometheus::Family<prometheus::Histogram>*>(family_);
+      auto histogram_ptr =
+          &histogram_family_ptr->Add(label_map, args->buckets());
+      prom_metric = reinterpret_cast<void*>(histogram_ptr);
+      break;
+    }
     default:
       throw std::invalid_argument(
           "Unsupported family kind passed to Metric constructor.");
@@ -134,6 +166,14 @@ MetricFamily::Remove(void* prom_metric, Metric* metric)
       gauge_family_ptr->Remove(gauge_ptr);
       break;
     }
+    case TRITONSERVER_METRIC_KIND_HISTOGRAM: {
+      auto histogram_family_ptr =
+          reinterpret_cast<prometheus::Family<prometheus::Histogram>*>(family_);
+      auto histogram_ptr =
+          reinterpret_cast<prometheus::Histogram*>(prom_metric);
+      histogram_family_ptr->Remove(histogram_ptr);
+      break;
+    }
     default:
       // Invalid kind should be caught in constructor
       LOG_ERROR << "Unsupported kind in Metric destructor.";
@@ -169,7 +209,8 @@ MetricFamily::~MetricFamily()
 //
 Metric::Metric(
     TRITONSERVER_MetricFamily* family,
-    std::vector<const InferenceParameter*> labels)
+    std::vector<const InferenceParameter*> labels,
+    const TritonServerMetricArgs* args)
 {
   family_ = reinterpret_cast<MetricFamily*>(family);
   kind_ = family_->Kind();
@@ -188,7 +229,7 @@ Metric::Metric(
         std::string(reinterpret_cast<const char*>(param->ValuePointer()));
   }
 
-  metric_ = family_->Add(label_map, this);
+  metric_ = family_->Add(label_map, this, args);
 }
 
 Metric::~Metric()
@@ -235,6 +276,11 @@ Metric::Value(double* value)
       *value = gauge_ptr->Value();
       break;
     }
+    case TRITONSERVER_METRIC_KIND_HISTOGRAM: {
+      return TRITONSERVER_ErrorNew(
+          TRITONSERVER_ERROR_UNSUPPORTED,
+          "TRITONSERVER_METRIC_KIND_HISTOGRAM does not support Value");
+    }
     default:
       return TRITONSERVER_ErrorNew(
           TRITONSERVER_ERROR_UNSUPPORTED,
@@ -279,6 +325,11 @@ Metric::Increment(double value)
       }
       break;
     }
+    case TRITONSERVER_METRIC_KIND_HISTOGRAM: {
+      return TRITONSERVER_ErrorNew(
+          TRITONSERVER_ERROR_UNSUPPORTED,
+          "TRITONSERVER_METRIC_KIND_HISTOGRAM does not support Increment");
+    }
     default:
       return TRITONSERVER_ErrorNew(
           TRITONSERVER_ERROR_UNSUPPORTED,
@@ -308,6 +359,45 @@ Metric::Set(double value)
       gauge_ptr->Set(value);
       break;
     }
+    case TRITONSERVER_METRIC_KIND_HISTOGRAM: {
+      return TRITONSERVER_ErrorNew(
+          TRITONSERVER_ERROR_UNSUPPORTED,
+          "TRITONSERVER_METRIC_KIND_HISTOGRAM does not support Set");
+    }
+    default:
+      return TRITONSERVER_ErrorNew(
+          TRITONSERVER_ERROR_UNSUPPORTED,
+          "Unsupported TRITONSERVER_MetricKind");
+  }
+
+  return nullptr;  // Success
+}
+
+TRITONSERVER_Error*
+Metric::Observe(double value)
+{
+  if (metric_ == nullptr) {
+    return TRITONSERVER_ErrorNew(
+        TRITONSERVER_ERROR_INTERNAL,
+        "Could not set metric value. Metric has been invalidated.");
+  }
+
+  switch (kind_) {
+    case TRITONSERVER_METRIC_KIND_COUNTER: {
+      return TRITONSERVER_ErrorNew(
+          TRITONSERVER_ERROR_UNSUPPORTED,
+          "TRITONSERVER_METRIC_KIND_COUNTER does not support Observe");
+    }
+    case TRITONSERVER_METRIC_KIND_GAUGE: {
+      return TRITONSERVER_ErrorNew(
+          TRITONSERVER_ERROR_UNSUPPORTED,
+          "TRITONSERVER_METRIC_KIND_GAUGE does not support Observe");
+    }
+    case TRITONSERVER_METRIC_KIND_HISTOGRAM: {
+      auto histogram_ptr = reinterpret_cast<prometheus::Histogram*>(metric_);
+      histogram_ptr->Observe(value);
+      break;
+    }
     default:
       return TRITONSERVER_ErrorNew(
           TRITONSERVER_ERROR_UNSUPPORTED,
diff --git a/src/metric_family.h b/src/metric_family.h
index b5d09d864..2ea13eeda 100644
--- a/src/metric_family.h
+++ b/src/metric_family.h
@@ -1,4 +1,5 @@
-// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights
+// reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions
@@ -27,6 +28,7 @@
 
 #ifdef TRITON_ENABLE_METRICS
 
+#include <cstring>
 #include <mutex>
 #include <set>
 #include <unordered_map>
@@ -37,6 +39,29 @@
 
 namespace triton { namespace core {
 
+//
+// TritonServerMetricArgs
+//
+// Implementation for TRITONSERVER_MetricArgs.
+//
+class TritonServerMetricArgs {
+ public:
+  TritonServerMetricArgs() = default;
+
+  void* SetHistogramArgs(const double* buckets, uint64_t bucket_count)
+  {
+    kind_ = TRITONSERVER_METRIC_KIND_HISTOGRAM;
+    buckets_ = std::vector<double>(buckets, buckets + bucket_count);
+    return nullptr;
+  }
+  TRITONSERVER_MetricKind kind() const { return kind_; }
+  const std::vector<double>& buckets() const { return buckets_; }
+
+ private:
+  TRITONSERVER_MetricKind kind_;
+  std::vector<double> buckets_;
+};
+
 //
 // Implementation for TRITONSERVER_MetricFamily.
 //
@@ -50,7 +75,9 @@ class MetricFamily {
   void* Family() const { return family_; }
   TRITONSERVER_MetricKind Kind() const { return kind_; }
 
-  void* Add(std::map<std::string, std::string> label_map, Metric* metric);
+  void* Add(
+      std::map<std::string, std::string> label_map, Metric* metric,
+      const TritonServerMetricArgs* args);
   void Remove(void* prom_metric, Metric* metric);
 
   int NumMetrics()
@@ -86,7 +113,8 @@ class Metric {
  public:
   Metric(
       TRITONSERVER_MetricFamily* family,
-      std::vector<const InferenceParameter*> labels);
+      std::vector<const InferenceParameter*> labels,
+      const TritonServerMetricArgs* args);
   ~Metric();
 
   MetricFamily* Family() const { return family_; }
@@ -95,6 +123,7 @@ class Metric {
   TRITONSERVER_Error* Value(double* value);
   TRITONSERVER_Error* Increment(double value);
   TRITONSERVER_Error* Set(double value);
+  TRITONSERVER_Error* Observe(double value);
 
   // If a MetricFamily is deleted before its dependent Metric, we want to
   // invalidate the references so we don't access invalid memory.
diff --git a/src/metrics.h b/src/metrics.h
index 2abec1cf3..6d08ad168 100644
--- a/src/metrics.h
+++ b/src/metrics.h
@@ -1,4 +1,4 @@
-// Copyright 2018-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// Copyright 2018-2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions
@@ -35,6 +35,7 @@
 
 #include "prometheus/counter.h"
 #include "prometheus/gauge.h"
+#include "prometheus/histogram.h"
 #include "prometheus/registry.h"
 #include "prometheus/serializer.h"
 #include "prometheus/summary.h"
diff --git a/src/test/metrics_api_test.cc b/src/test/metrics_api_test.cc
index 3356493a3..ea9d63a9e 100644
--- a/src/test/metrics_api_test.cc
+++ b/src/test/metrics_api_test.cc
@@ -1,4 +1,5 @@
-// Copyright (c) 2022, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+// Copyright (c) 2022-2024, NVIDIA CORPORATION & AFFILIATES. All rights
+// reserved.
 //
 // Redistribution and use in source and binary forms, with or without
 // modification, are permitted provided that the following conditions
@@ -232,6 +233,89 @@ MetricAPIHelper(TRITONSERVER_Metric* metric, TRITONSERVER_MetricKind kind)
   TRITONSERVER_ErrorDelete(err);
 }
 
+// Calculate the cumulative_counts vector based on data and buckets.
+std::vector<std::uint64_t>
+GetCumulativeCounts(
+    const std::vector<double>& data, const std::vector<double>& buckets)
+{
+  std::vector<std::uint64_t> cumulative_counts(buckets.size() + 1, 0);
+  for (double datum : data) {
+    int i = 0;
+    for (; i < buckets.size(); ++i) {
+      if (datum <= buckets[i]) {
+        cumulative_counts[i]++;
+        break;
+      }
+    }
+    if (i == buckets.size()) {
+      cumulative_counts[i]++;
+    }
+  }
+
+  for (int i = 1; i < cumulative_counts.size(); ++i) {
+    cumulative_counts[i] += cumulative_counts[i - 1];
+  }
+  return cumulative_counts;
+}
+
+void
+HistogramAPIHelper(
+    TRITONSERVER_Server* server, TRITONSERVER_Metric* metric,
+    std::vector<double> buckets, std::string metric_name,
+    std::string labels_str)
+{
+  // Observe data
+  std::vector<double> data{0.05, 1.5, 6.0};
+  double sum = 0.0;
+  for (auto datum : data) {
+    FAIL_TEST_IF_ERR(
+        TRITONSERVER_MetricObserve(metric, datum), "observe metric value");
+    sum += datum;
+  }
+  std::vector<std::uint64_t> cumulative_counts =
+      GetCumulativeCounts(data, buckets);
+
+  // Collect formatted output
+  std::string metrics_str;
+  GetMetrics(server, &metrics_str);
+
+  // All strings in this function are generated from ostringstream to make sure
+  // there is no trailing zeros. For example, std::to_string(7.55) is "7.550000"
+  // which is inconsistent with prometheus text_serializer output "7.55".
+
+  // custom_histogram_example_count{example1="histogram_label1",example2="histogram_label2"}
+  // 3
+  std::ostringstream count_ss;
+  count_ss << metric_name << "_count{" << labels_str << "} " << data.size();
+  ASSERT_EQ(NumMetricMatches(server, count_ss.str()), 1);
+
+  // custom_histogram_example_sum{example1="histogram_label1",example2="histogram_label2"}
+  // 7.55
+  std::ostringstream sum_ss;
+  sum_ss << metric_name << "_sum{" << labels_str << "} " << sum;
+  ASSERT_EQ(NumMetricMatches(server, sum_ss.str()), 1);
+
+  // custom_histogram_example_bucket{example1="histogram_label1",example2="histogram_label2"
+  std::ostringstream bucket_partial_ss;
+  bucket_partial_ss << metric_name << "_bucket{" << labels_str;
+  ASSERT_EQ(
+      NumMetricMatches(server, bucket_partial_ss.str()),
+      cumulative_counts.size());
+  for (uint64_t i = 0; i < cumulative_counts.size(); ++i) {
+    // custom_histogram_example_bucket{example1="histogram_label1",example2="histogram_label2",le="0.1"}
+    // 1
+    std::ostringstream le_ss;
+    if (i < buckets.size()) {
+      le_ss << "\"" << buckets[i] << "\"";
+    } else {
+      le_ss << "\"+Inf\"";
+    }
+    std::ostringstream bucket_ss;
+    bucket_ss << metric_name << "_bucket{" << labels_str
+              << ",le=" << le_ss.str() << "} " << cumulative_counts[i];
+    ASSERT_EQ(NumMetricMatches(server, bucket_ss.str()), 1);
+  }
+}
 
 // Test Fixture
 class MetricsApiTest : public ::testing::Test {
@@ -364,6 +448,123 @@ TEST_F(MetricsApiTest, TestGaugeEndToEnd)
   ASSERT_EQ(NumMetricMatches(server_, description), 0);
 }
 
+// Test end-to-end flow of Generic Metrics API for Histogram metric
+TEST_F(MetricsApiTest, TestHistogramEndToEnd)
+{
+  // Create metric family
+  TRITONSERVER_MetricFamily* family;
+  TRITONSERVER_MetricKind kind = TRITONSERVER_METRIC_KIND_HISTOGRAM;
+  const char* name = "custom_histogram_example";
+  const char* description =
+      "this is an example histogram metric added via API.";
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricFamilyNew(&family, kind, name, description),
+      "Creating new metric family");
+
+  // Create metric
+  TRITONSERVER_Metric* metric;
+  // Create labels
+  std::vector<const TRITONSERVER_Parameter*> labels;
+  labels.emplace_back(TRITONSERVER_ParameterNew(
+      "example1", TRITONSERVER_PARAMETER_STRING, "histogram_label1"));
+  labels.emplace_back(TRITONSERVER_ParameterNew(
+      "example2", TRITONSERVER_PARAMETER_STRING, "histogram_label2"));
+  // Create metric args
+  std::vector<double> buckets = {0.1, 1.0, 2.5, 5.0, 10.0};
+  TRITONSERVER_MetricArgs* args;
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricArgsNew(&args), "Creating new metric args");
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricArgsSetHistogram(args, buckets.data(), buckets.size()),
+      "setting histogram metric args");
+
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricNewWithArgs(
+          &metric, family, labels.data(), labels.size(), args),
+      "Creating new metric");
+  for (const auto label : labels) {
+    TRITONSERVER_ParameterDelete(const_cast<TRITONSERVER_Parameter*>(label));
+  }
+  FAIL_TEST_IF_ERR(TRITONSERVER_MetricArgsDelete(args), "delete metric args");
+
+  // Run through metric APIs and assert correctness
+  std::string labels_str =
+      "example1=\"histogram_label1\",example2=\"histogram_label2\"";
+  HistogramAPIHelper(server_, metric, buckets, name, labels_str);
+
+  // Assert custom metric is reported and found in output
+  ASSERT_EQ(NumMetricMatches(server_, description), 1);
+
+  // Cleanup
+  FAIL_TEST_IF_ERR(TRITONSERVER_MetricDelete(metric), "delete metric");
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricFamilyDelete(family), "delete metric family");
+
+  // Assert custom metric/family is unregistered and no longer in output
+  ASSERT_EQ(NumMetricMatches(server_, description), 0);
+}
+
+// Test create a histogram metric without creating metric arguments
+TEST_F(MetricsApiTest, TestHistogramNoMetricArgs)
+{
+  // Create metric family
+  TRITONSERVER_MetricFamily* family;
+  TRITONSERVER_MetricKind kind = TRITONSERVER_METRIC_KIND_HISTOGRAM;
+  const char* name = "no_metric_args";
+  const char* description = "no metric args description";
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricFamilyNew(&family, kind, name, description),
+      "Creating new metric family");
+
+  // MetricArgs not created
+  TRITONSERVER_MetricArgs* args = nullptr;
+  // Create metric
+  std::vector<const TRITONSERVER_Parameter*> labels;
+  TRITONSERVER_Metric* metric = nullptr;
+  auto err = TRITONSERVER_MetricNewWithArgs(
+      &metric, family, labels.data(), labels.size(), args);
+  EXPECT_THAT(
+      TRITONSERVER_ErrorMessage(err),
+      HasSubstr("Bucket boundaries not found in Metric args"));
+
+  // Cleanup
+  FAIL_TEST_IF_ERR(TRITONSERVER_MetricArgsDelete(args), "delete metric args");
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricFamilyDelete(family), "delete metric family");
+}
+
+// Test create a histogram metric without setting metric arguments
+TEST_F(MetricsApiTest, TestHistogramMetricArgsNotset)
+{
+  // Create metric family
+  TRITONSERVER_MetricFamily* family;
+  TRITONSERVER_MetricKind kind = TRITONSERVER_METRIC_KIND_HISTOGRAM;
+  const char* name = "metric_args_not_set";
+  const char* description = "metric args not set description";
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricFamilyNew(&family, kind, name, description),
+      "Creating new metric family");
+
+  // Create metric args object without setting it
+  TRITONSERVER_MetricArgs* args;
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricArgsNew(&args), "Creating new metric args");
+
+  // Create metric
+  std::vector<const TRITONSERVER_Parameter*> labels;
+  TRITONSERVER_Metric* metric = nullptr;
+  auto err = TRITONSERVER_MetricNewWithArgs(
+      &metric, family, labels.data(), labels.size(), args);
+  EXPECT_THAT(
+      TRITONSERVER_ErrorMessage(err),
+      HasSubstr("Metric args not set to histogram kind"));
+
+  // Cleanup
+  FAIL_TEST_IF_ERR(TRITONSERVER_MetricArgsDelete(args), "delete metric args");
+  FAIL_TEST_IF_ERR(
+      TRITONSERVER_MetricFamilyDelete(family), "delete metric family");
+}
+
 // Test that a duplicate metric family can't be added
 // with a conflicting type/kind
 TEST_F(MetricsApiTest, TestDupeMetricFamilyDiffKind)
diff --git a/src/tritonserver.cc b/src/tritonserver.cc
index 82642d5dc..67343a730 100644
--- a/src/tritonserver.cc
+++ b/src/tritonserver.cc
@@ -3365,6 +3365,61 @@ TRITONSERVER_MetricFamilyDelete(TRITONSERVER_MetricFamily* family)
 #endif  // TRITON_ENABLE_METRICS
 }
 
+TRITONSERVER_Error*
+TRITONSERVER_GetMetricFamilyKind(
+    TRITONSERVER_MetricFamily* family, TRITONSERVER_MetricKind* kind)
+{
+#ifdef TRITON_ENABLE_METRICS
+  *kind = reinterpret_cast<tc::MetricFamily*>(family)->Kind();
+  return nullptr;  // Success
+#else
+  return TRITONSERVER_ErrorNew(
+      TRITONSERVER_ERROR_UNSUPPORTED, "metrics not supported");
+#endif  // TRITON_ENABLE_METRICS
+}
+
+TRITONSERVER_Error*
+TRITONSERVER_MetricArgsNew(TRITONSERVER_MetricArgs** args)
+{
+#ifdef TRITON_ENABLE_METRICS
+  tc::TritonServerMetricArgs* largs = new tc::TritonServerMetricArgs();
+  *args = reinterpret_cast<TRITONSERVER_MetricArgs*>(largs);
+  return nullptr;  // Success
+#else
+  *args = nullptr;
+  return TRITONSERVER_ErrorNew(
+      TRITONSERVER_ERROR_UNSUPPORTED, "metrics not supported");
+#endif  // TRITON_ENABLE_METRICS
+}
+
+TRITONSERVER_Error*
+TRITONSERVER_MetricArgsSetHistogram(
+    TRITONSERVER_MetricArgs* args, const double* buckets,
+    const uint64_t buckets_count)
+{
+#ifdef TRITON_ENABLE_METRICS
+  auto largs = reinterpret_cast<tc::TritonServerMetricArgs*>(args);
+  largs->SetHistogramArgs(buckets, buckets_count);
+  return nullptr;  // Success
+#else
+  return TRITONSERVER_ErrorNew(
+      TRITONSERVER_ERROR_UNSUPPORTED, "metrics not supported");
+#endif  // TRITON_ENABLE_METRICS
+}
+
+TRITONSERVER_Error*
+TRITONSERVER_MetricArgsDelete(TRITONSERVER_MetricArgs* args)
+{
+#ifdef TRITON_ENABLE_METRICS
+  auto largs = reinterpret_cast<tc::TritonServerMetricArgs*>(args);
+  delete largs;
+  return nullptr;  // success
+#else
+  return TRITONSERVER_ErrorNew(
+      TRITONSERVER_ERROR_UNSUPPORTED, "metrics not supported");
+#endif  // TRITON_ENABLE_METRICS
+}
+
 //
 // TRITONSERVER_Metric
 //
@@ -3373,6 +3428,24 @@ TRITONSERVER_MetricNew(
     TRITONSERVER_Metric** metric, TRITONSERVER_MetricFamily* family,
     const TRITONSERVER_Parameter** labels, const uint64_t label_count)
 {
+#ifdef TRITON_ENABLE_METRICS
+  return TRITONSERVER_MetricNewWithArgs(
+      metric, family, labels, label_count, nullptr);
+#else
+  return TRITONSERVER_ErrorNew(
+      TRITONSERVER_ERROR_UNSUPPORTED, "metrics not supported");
+#endif  // TRITON_ENABLE_METRICS
+}
+
+//
+// TRITONSERVER_MetricGeneric
+//
+TRITONSERVER_Error*
+TRITONSERVER_MetricNewWithArgs(
+    TRITONSERVER_Metric** metric, TRITONSERVER_MetricFamily* family,
+    const TRITONSERVER_Parameter** labels, const uint64_t label_count,
+    const TRITONSERVER_MetricArgs* args)
+{
 #ifdef TRITON_ENABLE_METRICS
   std::vector<const tc::InferenceParameter*> labels_vec;
   for (size_t i = 0; i < label_count; i++) {
@@ -3381,8 +3454,9 @@ TRITONSERVER_MetricNew(
   }
 
   try {
-    *metric = reinterpret_cast<TRITONSERVER_Metric*>(
-        new tc::Metric(family, labels_vec));
+    *metric = reinterpret_cast<TRITONSERVER_Metric*>(new tc::Metric(
+        family, labels_vec,
+        reinterpret_cast<const tc::TritonServerMetricArgs*>(args)));
   }
   catch (std::invalid_argument const& ex) {
     // Catch invalid kinds passed to constructor
@@ -3450,6 +3524,17 @@ TRITONSERVER_MetricSet(TRITONSERVER_Metric* metric, double value)
 #endif  // TRITON_ENABLE_METRICS
 }
 
+TRITONSERVER_Error*
+TRITONSERVER_MetricObserve(TRITONSERVER_Metric* metric, double value)
+{
+#ifdef TRITON_ENABLE_METRICS
+  return reinterpret_cast<tc::Metric*>(metric)->Observe(value);
+#else
+  return TRITONSERVER_ErrorNew(
+      TRITONSERVER_ERROR_UNSUPPORTED, "metrics not supported");
+#endif  // TRITON_ENABLE_METRICS
+}
+
 TRITONSERVER_Error*
 TRITONSERVER_GetMetricKind(
     TRITONSERVER_Metric* metric, TRITONSERVER_MetricKind* kind)
diff --git a/src/tritonserver_stub.cc b/src/tritonserver_stub.cc
index cd1e03e15..5cdb65c3c 100644
--- a/src/tritonserver_stub.cc
+++ b/src/tritonserver_stub.cc
@@ -1080,6 +1080,11 @@ TRITONSERVER_MetricNew()
 {
 }
 
+TRITONAPI_DECLSPEC void
+TRITONSERVER_MetricNewWithArgs()
+{
+}
+
 TRITONAPI_DECLSPEC void
 TRITONSERVER_MetricDelete()
 {
@@ -1100,6 +1105,11 @@ TRITONSERVER_MetricSet()
 {
 }
 
+TRITONAPI_DECLSPEC void
+TRITONSERVER_MetricObserve()
+{
+}
+
 TRITONAPI_DECLSPEC void
 TRITONSERVER_GetMetricKind()
 {