diff --git a/src/metabase/api/card.clj b/src/metabase/api/card.clj
index bf7e8aa04699f7678870968a2672396db13658de..77b9f3e5862fc943805e9dd4f630bf6e923f5a7c 100644
--- a/src/metabase/api/card.clj
+++ b/src/metabase/api/card.clj
@@ -16,12 +16,14 @@
             [metabase.email.messages :as messages]
             [metabase.events :as events]
             [metabase.mbql.normalize :as mbql.normalize]
+            [metabase.mbql.util :as mbql.u]
             [metabase.models.bookmark :as bookmark :refer [CardBookmark]]
             [metabase.models.card :as card :refer [Card]]
             [metabase.models.collection :as collection :refer [Collection]]
             [metabase.models.database :refer [Database]]
             [metabase.models.interface :as mi]
             [metabase.models.moderation-review :as moderation-review]
+            [metabase.models.params.chain-filter :as chain-filter]
             [metabase.models.persisted-info :as persisted-info :refer [PersistedInfo]]
             [metabase.models.pulse :as pulse :refer [Pulse]]
             [metabase.models.query :as query]
@@ -32,6 +34,7 @@
             [metabase.models.view-log :refer [ViewLog]]
             [metabase.query-processor.async :as qp.async]
             [metabase.query-processor.card :as qp.card]
+            [metabase.query-processor.error-type :as qp.error-type]
             [metabase.query-processor.pivot :as qp.pivot]
             [metabase.query-processor.util :as qp.util]
             [metabase.related :as related]
@@ -704,6 +707,118 @@
                  :js-int-to-string?      false}))
 
 
+;;; ------------------------------------------------ Parameters -------------------------------------------------
+(def ^:const result-limit
+  "How many results to return when getting values for a parameter."
+  1000)
+
+(defn- field-clause->field-id
+  "Find the field id in a field clause if it exists.
+
+    (field-clause->field-id [:field 3 nil])
+    ;; -> 3"
+  [field-clause]
+  (mbql.u/match-one field-clause [:field (id :guard integer?) _] id))
+
+(defn- template-tag-target->field-id
+  "Given a template tag target, find the field id that it's connected to.
+  Note that a target has a field id connect to it iff it's a field filter."
+  [card {:keys [id]}]
+  (if-let [template-tags (get-in card [:dataset_query :native :template-tags])]
+    (first (for [[_ template-tag] template-tags
+                  :when (and (= (:id template-tag) id)
+                             (= (:type template-tag) :dimension))
+                  :let  [field-id (field-clause->field-id (:dimension template-tag))]
+                  :when field-id]
+             field-id))
+    (throw (ex-info (tru "Card with ID {0} does not have template tags." (:id card))
+                    {:card-id         (:id card)
+                     :template-tag-id id}))))
+
+(defn- target->field-id
+  "Find the field id that the target is connected to.
+  Target could be a `dimension`, in which target have the shape
+
+    [:dimension [:field 3 nil]]
+
+  Or it could be a `template-tag`, then it should have the shape
+
+    [:template-tag {:id \"6006ad7d-036e-83ec-4d6f-30f82b98ac21\"}]"
+  [card [target-type target-args]]
+  (case target-type
+    :dimension    (field-clause->field-id target-args)
+    :template-tag (template-tag-target->field-id card target-args)
+    nil))
+
+(defn- param-id->field-ids
+ "Get Field ID(s) associated with a parameter in a Card.
+
+    (param-id->field-ids (Card 62) \"ee876336\")
+    ;; -> #{276}"
+  [{:keys [parameter_mappings] :as card} param-id]
+  (into #{} (for [{:keys [target parameter_id]} parameter_mappings
+                  :when (= parameter_id param-id)
+                  :let [field-id (target->field-id card target)]
+                  :when field-id]
+              field-id)))
+
+(s/defn param-values
+  "Given a `param-id`, returns a of possible values that it could choose from.
+
+    ;; show me categories
+    (param-values (Card 62) \"ee876336\")
+    ;; -> (\"African\" \"American\" \"Artisan\" ...)
+
+    ;; show me categories that contains \"Ameri\"
+    (param-values (Card 62) \"ee876336\"  \"Ameri\")
+    ;; -> (\"American\")
+  "
+  ([card param-id]
+   (param-values card param-id nil))
+
+  ([card param-id query]
+   (when-not (seq (filter #(= (:id %) param-id) (:parameters card)))
+     (throw (ex-info (tru "Card does not have a parameter with the ID {0}" (pr-str param-id))
+                     {:status-code 400})))
+   (let [field-ids (param-id->field-ids card param-id)]
+     (when (empty? field-ids)
+       (throw (ex-info (tru "Parameter {0} does not have any Fields associated with it" (pr-str param-id))
+                       {:param-id    param-id
+                        :status-code 400})))
+     (try
+         (let [results (distinct (mapcat (if (seq query)
+                                           #(chain-filter/chain-filter-search % {} query :limit result-limit)
+                                           #(chain-filter/chain-filter % {} :limit result-limit))
+                                         field-ids))]
+           ;; results can come back as [v ...] *or* as [[orig remapped] ...]. Sort by remapped value if that's the case
+           (if (sequential? (first results))
+             (sort-by second results)
+             (sort results)))
+         (catch clojure.lang.ExceptionInfo e
+           (if (= (:type (u/all-ex-data e)) qp.error-type/missing-required-permissions)
+             (api/throw-403 e)
+             (throw e)))))))
+
+(api/defendpoint GET "/:id/params/:param-key/values"
+  "Fetch possible values of the parameter whose ID is `:param-id`.
+
+    ;; fetch values for Card 1 parameter 'abc' that are possible
+    GET /api/card/1/params/abc/values"
+  [id param-key]
+  (let [card (api/read-check Card id)]
+    (param-values card param-key)))
+
+(api/defendpoint GET "/:id/params/:param-key/search/:query"
+  "Fetch possible values of the parameter whose ID is `:param-id` that contain `:query`.
+
+    ;; fetch values for Card 1 parameter 'abc' that contain 'Cam'
+     GET /api/card/1/params/abc/search/Cam
+
+  Currently limited to first 1000 results."
+  [id param-key query]
+  (let [card (api/read-check Card id)]
+    (param-values card param-key query)))
+
 ;;; ----------------------------------------------- Sharing is Caring ------------------------------------------------
 
 (api/defendpoint POST "/:card-id/public_link"
@@ -766,6 +881,8 @@
                             :qp-runner qp.pivot/run-pivot-query
                             :ignore_cache ignore_cache))
 
+;;; ----------------------------------------------- Persistence ------------------------------------------------
+
 (api/defendpoint POST "/:card-id/persist"
   "Mark the model (card) as persisted. Runs the query and saves it to the database backing the card and hot swaps this
   query in place of the model's query."
diff --git a/test/metabase/api/card_test.clj b/test/metabase/api/card_test.clj
index 2f515fb4e217d584133959e3461785a6b7097903..9da4141ddbcddc4e885714e45140f2cad42bb736 100644
--- a/test/metabase/api/card_test.clj
+++ b/test/metabase/api/card_test.clj
@@ -14,6 +14,7 @@
             [metabase.models :refer [Card CardBookmark Collection Dashboard Database ModerationReview Pulse PulseCard
                                      PulseChannel PulseChannelRecipient Table Timeline TimelineEvent ViewLog]]
             [metabase.models.moderation-review :as moderation-review]
+            [metabase.models.params.chain-filter-test :as chain-filter-test]
             [metabase.models.permissions :as perms]
             [metabase.models.permissions-group :as perms-group]
             [metabase.models.revision :as revision :refer [Revision]]
@@ -1838,6 +1839,167 @@
                   (name->position (:data (mt/user-http-request :crowberto :get 200 (format "collection/%s/items" coll-id-2)
                                                                :model "card" :archived "false"))))))))
 
+;;; +----------------------------------------------------------------------------------------------------------------+
+;;; |                                           PARAMETER VALUES ENDPOINTS                                           |
+;;; +----------------------------------------------------------------------------------------------------------------+
+
+(defn- do-with-param-values-fixtures
+  [query-type card-values f]
+  {:pre [(#{:native :query} query-type)]}
+  (mt/with-temp* [Card [card]]
+    (let [card-defaults
+          (if (= query-type :query)
+            ;; notebook query with parameters are fields
+            {:database_id   (mt/id)
+             :table_id      (mt/id :venues)
+             :dataset_query (mt/mbql-query venues)
+             :parameters [{:name "Category Name"
+                           :slug "category_name"
+                           :id   "_CATEGORY_NAME_"
+                           :type "category"}
+                          {:name "Category ID"
+                           :slug "category_id"
+                           :id   "_CATEGORY_ID_"
+                           :type "category"}]
+             :parameter_mappings [{:parameter_id "_CATEGORY_NAME_"
+                                   :card_id      (:id card)
+                                   :target       [:dimension (mt/$ids venues $category_id->categories.name)]}
+                                  {:parameter_id "_CATEGORY_ID_"
+                                   :card_id      (:id card)
+                                   :target       [:dimension (mt/$ids venues $category_id)]}]}
+            ;; native query with parameters are template tags
+            {:database_id (mt/id)
+             :query_type :native
+             :dataset_query {:database (mt/id)
+                             :type     :native
+                             :native
+                             {:query         (str "SELECT * FROM VENUES WHERE {{category}} and {{category_id}};")
+                              :template-tags {"category"      {:id           "c7fcf1fa"
+                                                               :name         "category"
+                                                               :display-name "Category"
+                                                               :type         :dimension
+                                                               :dimension    [:field (mt/$ids venues $category_id->categories.name) nil]
+                                                               :widget-type  :string/=}
+                                              "category_id"   {:id           "a3cd3f3b"
+                                                               :name         "category_id"
+                                                               :display-name "Category"
+                                                               :type         :dimension
+                                                               :dimension    [:field (mt/$ids venues $category_id) nil]
+                                                               :widget-type  :number/=}}}}
+
+             :parameters [{:name "Category_name"
+                           :slug "category_name"
+                           :id   "_CATEGORY_NAME_"
+                           :type "category"}
+                          {:name "Category ID"
+                           :slug "category_id"
+                           :id   "_CATEGORY_ID_"
+                           :type "category"}]
+             :parameter_mappings [{:parameter_id "_CATEGORY_NAME_"
+                                   :card_id      (:id card)
+                                   :target       [:template-tag {:id "c7fcf1fa"}]}
+                                  {:parameter_id "_CATEGORY_ID_"
+                                   :card_id      (:id card)
+                                   :target       [:template-tag {:id "a3cd3f3b"}]}]})]
+      (db/update! Card (:id card)
+                  (merge card-defaults card-values)))
+    (f {:card       card
+        :param-ids {:category-name "_CATEGORY_NAME_"
+                    :category-id   "_CATEGORY_ID_"}})))
+
+(defmacro ^:private with-param-values-fixtures
+  "Create a query and its parameters."
+  {:style/indent 2}
+  [query-type [binding card-values] & body]
+  `(do-with-param-values-fixtures ~query-type ~card-values (fn [~binding] ~@body)))
+
+(defn- param-values-values-url [card-or-id param-id]
+  (format "card/%d/params/%s/values" (u/the-id card-or-id) (name param-id)))
+
+(defn- param-values-search-url [card-or-id param-id query]
+  (str (format "card/%d/params/%s/search/" (u/the-id card-or-id) (name param-id))
+       query))
+
+(deftest param-values-test
+  (testing "GET /api/card/:id/params/:param-id/values"
+    (doseq [query-type [:query :native]]
+      (testing (format "With %s question" (name query-type))
+        (with-param-values-fixtures query-type [{:keys [card param-ids]}]
+          (testing "Show me names of categories"
+            (is (= ["African" "American" "Artisan"]
+                   (take 3 (mt/user-http-request :rasta :get 200 (param-values-values-url
+                                                                   card
+                                                                   (:category-name param-ids))))))))
+
+        (testing "Should require perms for the Card"
+          (mt/with-non-admin-groups-no-root-collection-perms
+            (mt/with-temp Collection [collection]
+              (with-param-values-fixtures query-type [{:keys [card param-ids]} {:collection_id (:id collection)}]
+                (is (= "You don't have permissions to do that."
+                       (mt/user-http-request :rasta :get 403 (param-values-values-url
+                                                               card
+                                                               (:category-name param-ids)))))))))
+
+        (testing "should check perms for the Fields in question"
+          (mt/with-temp-copy-of-db
+            (with-param-values-fixtures query-type [{:keys [card param-ids]}]
+              (perms/revoke-data-perms! (perms-group/all-users) (mt/id))
+              (is (= "You don't have permissions to do that."
+                     (mt/user-http-request :rasta :get 403 (param-values-values-url
+                                                             card
+                                                             (:category-name param-ids))))))))))))
+
+(deftest param-values-search-test
+  (testing "GET /api/card/:id/params/:param-id/search/:query"
+    (doseq [query-type [:native :query]]
+      (testing (format "With %s question" (name query-type))
+        (with-param-values-fixtures :query [{:keys [card param-ids]}]
+          (let [url (param-values-search-url card (:category-name param-ids) "bar")]
+            (testing (str "\n" url)
+              (testing "\nShow me names of categories that include 'bar' (case-insensitive)"
+                (is (= ["Bar" "Gay Bar" "Juice Bar"]
+                       (take 3 (mt/user-http-request :rasta :get 200 url)))))))
+
+          (let [url (param-values-search-url card (:category-name param-ids) "house")]
+            (testing "\nShow me names of categories that include 'house' that have expensive venues (price = 4)"
+              (is (= ["Steakhouse"]
+                     (take 3 (mt/user-http-request :rasta :get 200 url))))))
+
+          (testing "Should require a non-empty query"
+            (doseq [query [nil
+                           ""
+                           "   "
+                           "\n"]]
+              (let [url (param-values-search-url card (:category-name param-ids) query)]
+                (is (= "API endpoint does not exist."
+                       (mt/user-http-request :rasta :get 404 url)))))))
+
+        (testing "Should require perms for the card"
+          (mt/with-non-admin-groups-no-root-collection-perms
+            (mt/with-temp Collection [collection]
+              (with-param-values-fixtures :query [{:keys [card param-ids]} {:collection_id (:id collection)}]
+                (let [url (param-values-search-url card (:category-name param-ids) "s")]
+                  (testing (str "\n url")
+                    (is (= "You don't have permissions to do that."
+                           (mt/user-http-request :rasta :get 403 url)))))))))))))
+
+(deftest param-values-human-readable-values-remapping-test
+  (testing "Get param values for Fields that have Human-Readable values\n"
+    (doseq [query-type [:native :query]]
+      (testing (format "With %s question" (name query-type))
+        (chain-filter-test/with-human-readable-values-remapping
+          (with-param-values-fixtures query-type [{:keys [card param-ids]}]
+            (testing "GET /api/card/:id/params/:param-id/values"
+              (let [url (param-values-values-url card (:category-id param-ids))]
+                (is (= [[2 "American"]
+                        [3 "Artisan"]]
+                       (take 2 (mt/user-http-request :rasta :get 200 url))))))
+
+            (testing "GET /api/card/:id/params/:param-id/search/:query"
+              (let [url (param-values-search-url card (:category-id param-ids) "house")]
+                (is (= [[67 "Steakhouse"]]
+                       (take 1 (mt/user-http-request :rasta :get 200 url))))))))))))
+
 ;;; +----------------------------------------------------------------------------------------------------------------+
 ;;; |                                            PUBLIC SHARING ENDPOINTS                                            |
 ;;; +----------------------------------------------------------------------------------------------------------------+