diff --git a/rmw/include/rmw/get_topic_endpoint_info.h b/rmw/include/rmw/get_topic_endpoint_info.h
index 8e873b87..449b0b80 100644
--- a/rmw/include/rmw/get_topic_endpoint_info.h
+++ b/rmw/include/rmw/get_topic_endpoint_info.h
@@ -186,6 +186,166 @@ rmw_get_subscriptions_info_by_topic(
bool no_mangle,
rmw_topic_endpoint_info_array_t * subscriptions_info);
+/// Retrieve endpoint information for each known client of a given service.
+/**
+ * This function returns an array of endpoint information for each client
+ * of a given service, as discovered so far by the given node.
+ * Endpoint information includes the client's node name and namespace,
+ * the associated topic type, the client's gid, and the client QoS profile.
+ * Names of non-existent topics are allowed, in which case an empty array will be returned.
+ *
+ * Depending on the RMW in use, discovery may be asynchronous. Therefore, creating a client
+ * and then calling this API may not show the newly created client immediately.
+ *
+ * \par QoS that are correctly read
+ * Not all QoS may be read correctly, \sa rmw_get_publishers_info_by_topic() for more details.
+ *
+ *
+ * Attribute | Adherence
+ * ------------------ | -------------
+ * Allocates Memory | Yes
+ * Thread-Safe | Yes
+ * Uses Atomics | Maybe [1]
+ * Lock-Free | Maybe [1]
+ * [1] rmw implementation defined, check the implementation documentation
+ *
+ * \par Runtime behavior
+ * To query the ROS graph is a synchronous operation.
+ * It is also non-blocking, but it is not guaranteed to be lock-free.
+ * Generally speaking, implementations may synchronize access to internal resources using
+ * locks but are not allowed to wait for events with no guaranteed time bound (barring
+ * the effects of starvation due to OS scheduling).
+ *
+ * \par Thread-safety
+ * Nodes are thread-safe objects, and so are all operations on them except for finalization.
+ * Therefore, it is safe to query the ROS graph using the same node concurrently.
+ * However, when querying service names and types:
+ * - Access to the array of topic endpoint information is not synchronized.
+ * It is not safe to read or write `clients_info`
+ * while rmw_get_clients_info_by_service() uses it.
+ * - Access to C-style string arguments is read-only but it is not synchronized.
+ * Concurrent `service_name` reads are safe, but concurrent reads and writes are not.
+ * - The default allocators are thread-safe objects, but any custom `allocator` may not be.
+ * Check your allocator documentation for further reference.
+ *
+ * \pre Given `node` must be a valid node handle, as returned by rmw_create_node().
+ * \pre Given `clients_info` must be a zero-initialized array of endpoints' information,
+ * as returned by rmw_get_zero_initialized_topic_endpoint_info_array().
+ *
+ * \param[in] node Node to query the ROS graph.
+ * \param[in] allocator Allocator to be used when populating the `clients_info` array.
+ * \param[in] service_name Name of the service for client lookup, often a fully qualified
+ * topic name but not necessarily (see rmw_create_client()).
+ * \param[in] no_mangle Whether to mangle the topic name before client lookup or not.
+ * \param[out] clients_info Array of client information, populated on success,
+ * left unchanged on failure.
+ * If populated, it is up to the caller to finalize this array later on,
+ * using rmw_topic_endpoint_info_array_fini().
+ * QoS Profiles in the info array will use RMW_DURATION_INFINITE for infinite durations,
+ * avoiding exposing any implementation-specific values.
+ * \return `RMW_RET_OK` if the query was successful, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `node` is NULL, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `allocator` is not valid,
+ * by rcutils_allocator_is_valid() definition, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `service_name` is NULL, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `clients_info` is NULL, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `clients_info` is not a
+ * zero-initialized array, or
+ * \return `RMW_RET_INCORRECT_RMW_IMPLEMENTATION` if the `node` implementation
+ * identifier does not match this implementation, or
+ * \return `RMW_RET_BAD_ALLOC` if memory allocation fails, or
+ * \return `RMW_RET_ERROR` if an unspecified error occurs.
+ */
+RMW_PUBLIC
+RMW_WARN_UNUSED
+rmw_ret_t
+rmw_get_clients_info_by_service(
+ const rmw_node_t * node,
+ rcutils_allocator_t * allocator,
+ const char * service_name,
+ bool no_mangle,
+ rmw_topic_endpoint_info_array_t * clients_info);
+
+/// Retrieve endpoint information for each known server of a given service.
+/**
+ * This function returns an array of endpoint information for each server
+ * of a given service, as discovered so far by the given node.
+ * Endpoint information includes the server's node name and namespace,
+ * the associated topic type, the server's gid, and the server QoS profile.
+ * Names of non-existent topics are allowed, in which case an empty array will be returned.
+ *
+ * Depending on the RMW in use, discovery may be asynchronous. Therefore, creating a server
+ * and then calling this API may not show the newly created server immediately.
+ *
+ * \par QoS that are correctly read
+ * Not all QoS may be read correctly, \sa rmw_get_publishers_info_by_topic() for more details.
+ *
+ *
+ * Attribute | Adherence
+ * ------------------ | -------------
+ * Allocates Memory | Yes
+ * Thread-Safe | Yes
+ * Uses Atomics | Maybe [1]
+ * Lock-Free | Maybe [1]
+ * [1] rmw implementation defined, check the implementation documentation
+ *
+ * \par Runtime behavior
+ * To query the ROS graph is a synchronous operation.
+ * It is also non-blocking, but it is not guaranteed to be lock-free.
+ * Generally speaking, implementations may synchronize access to internal resources using
+ * locks but are not allowed to wait for events with no guaranteed time bound (barring
+ * the effects of starvation due to OS scheduling).
+ *
+ * \par Thread-safety
+ * Nodes are thread-safe objects, and so are all operations on them except for finalization.
+ * Therefore, it is safe to query the ROS graph using the same node concurrently.
+ * However, when querying topic names and types:
+ * - Access to the array of topic endpoint information is not synchronized.
+ * It is not safe to read or write `servers_info`
+ * while rmw_get_servers_info_by_service() uses it.
+ * - Access to C-style string arguments is read-only but it is not synchronized.
+ * Concurrent `service_name` reads are safe, but concurrent reads and writes are not.
+ * - The default allocators are thread-safe objects, but any custom `allocator` may not be.
+ * Check your allocator documentation for further reference.
+ *
+ * \pre Given `node` must be a valid node handle, as returned by rmw_create_node().
+ * \pre Given `servers_info` must be a zero-initialized array of endpoints' information,
+ * as returned by rmw_get_zero_initialized_topic_endpoint_info_array().
+ *
+ * \param[in] node Node to query the ROS graph.
+ * \param[in] allocator Allocator to be used when populating the `servers_info` array.
+ * \param[in] service_name Name of the service for server lookup, often a fully qualified
+ * topic name but not necessarily (see rmw_create_service()).
+ * \param[in] no_mangle Whether to mangle the topic name before server lookup or not.
+ * \param[out] servers_info Array of server information, populated on success,
+ * left unchanged on failure.
+ * If populated, it is up to the caller to finalize this array later on,
+ * using rmw_topic_endpoint_info_array_fini().
+ * QoS Profiles in the info array will use RMW_DURATION_INFINITE for infinite durations,
+ * avoiding exposing any implementation-specific values.
+ * \return `RMW_RET_OK` if the query was successful, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `node` is NULL, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `allocator` is not valid,
+ * by rcutils_allocator_is_valid() definition, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `service_name` is NULL, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `servers_info` is NULL, or
+ * \return `RMW_RET_INVALID_ARGUMENT` if `servers_info` is not a
+ * zero-initialized array, or
+ * \return `RMW_RET_INCORRECT_RMW_IMPLEMENTATION` if the `node` implementation
+ * identifier does not match this implementation, or
+ * \return `RMW_RET_BAD_ALLOC` if memory allocation fails, or
+ * \return `RMW_RET_ERROR` if an unspecified error occurs.
+ */
+RMW_PUBLIC
+RMW_WARN_UNUSED
+rmw_ret_t
+rmw_get_servers_info_by_service(
+ const rmw_node_t * node,
+ rcutils_allocator_t * allocator,
+ const char * service_name,
+ bool no_mangle,
+ rmw_topic_endpoint_info_array_t * servers_info);
+
#ifdef __cplusplus
}
#endif