diff --git a/bids/layout/layout.py b/bids/layout/layout.py
index c7624631..4407b03a 100644
--- a/bids/layout/layout.py
+++ b/bids/layout/layout.py
@@ -546,6 +546,37 @@ class BIDSLayout(Layout):
                     fieldmap_set.append(cur_fieldmap)
         return fieldmap_set
 
+    def get_tr(self, derivatives=False, **selectors):
+        """ Returns the scanning repetition time (TR) for one or more runs.
+
+        Args:
+            derivatives (bool): If True, also checks derivatives images.
+            selectors: Optional keywords used to constrain the selected runs.
+                Can be any arguments valid for a .get call (e.g., BIDS entities
+                or JSON sidecar keys).
+        
+        Returns: A single float.
+
+        Notes: Raises an exception if more than one unique TR is found.
+        """
+        # Constrain search to functional images
+        selectors['suffix'] = 'bold'
+        selectors['datatype'] = 'func'
+        images = self.get(extensions=['.nii', '.nii.gz'], derivatives=derivatives,
+                          **selectors)
+        if not images:
+            raise ValueError("No functional images that match criteria found.")
+        
+        all_trs = set()
+        for img in images:
+            md = self.get_metadata(img.path, suffix='bold', full_search=True)
+            all_trs.add(round(float(md['RepetitionTime']), 5))
+ 
+        if len(all_trs) > 1:
+            raise ValueError("Unique TR cannot be found given selectors {!r}"
+                             .format(selectors))
+        return all_trs.pop()
+
     def get_collections(self, level, types=None, variables=None, merge=False,
                         sampling_rate=None, skip_empty=False, **kwargs):
         """Return one or more variable Collections in the BIDS project.

Add Analysis.get_tr() API
Currently, querying the TR is somewhat convoluted (see https://github.com/bids-standard/pybids/issues/124#issuecomment-439104593). A simple high-level API could be:

```Python
class Analysis:
    def get_tr(self, **selectors):
        all_trs = some_query(**selectors)
        unique_trs = filter_by_allclose(all_trs)
        if len(unique_trs) > 1:
            raise ValueError("Unique TR cannot be found given selectors {!r}".format(selectors))
        return unique_trs[0]
```

For single dataset analyses, `.get_tr()` should provide a valid value, making this useful for Jupyter-notebook style exploration, but I would expect application programmers to provide as many entities as possible to reduce risk of exceptions.
diff --git a/bids/layout/tests/test_layout.py b/bids/layout/tests/test_layout.py
index f6ccc122..c667021d 100644
--- a/bids/layout/tests/test_layout.py
+++ b/bids/layout/tests/test_layout.py
@@ -264,3 +264,19 @@ def test_derivative_getters():
     with pytest.raises(AttributeError):
         bare_layout.get_spaces()
     assert set(full_layout.get_spaces()) == {'MNI152NLin2009cAsym', 'T1w'}
+
+
+def test_get_tr(layout_7t_trt):
+    # Bad subject, should fail
+    with pytest.raises(ValueError) as exc:
+        layout_7t_trt.get_tr(subject="zzz")
+        assert exc.value.message.startswith("No functional images")
+    # There are multiple tasks with different TRs, so this should fail
+    with pytest.raises(ValueError) as exc:
+        layout_7t_trt.get_tr(subject=['01', '02'])
+        assert exc.value.message.startswith("Unique TR")
+    # This should work
+    tr = layout_7t_trt.get_tr(subject=['01', '02'], acquisition="fullbrain")
+    assert tr == 3.0
+    tr = layout_7t_trt.get_tr(subject=['01', '02'], acquisition="prefrontal")
+    assert tr == 4.0
\ No newline at end of file

pip install -e .[analysis]
parse_log_pytest
requirements.txt
pytest pytest-xdist
3.6
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs==22.2.0
certifi==2021.5.30
docopt==0.6.2
execnet==1.9.0
grabbit==0.2.5
importlib-metadata==4.8.3
iniconfig==1.1.1
nibabel==3.2.2
num2words==0.5.14
numpy==1.19.5
packaging==21.3
pandas==1.1.5
patsy==1.0.1
pluggy==1.0.0
py==1.11.0
-e git+https://github.com/bids-standard/pybids.git@36853b0bf222d3174ae7d9d1d9c7a7ecf5049cdd#egg=pybids
pyparsing==3.1.4
pytest==7.0.1
pytest-xdist==3.0.2
python-dateutil==2.9.0.post0
pytz==2025.2
scipy==1.5.4
six==1.17.0
tomli==1.2.3
typing_extensions==4.1.1
zipp==3.6.0

name: pybids
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=21.2.2=py36h06a4308_0
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - attrs==22.2.0
      - docopt==0.6.2
      - execnet==1.9.0
      - grabbit==0.2.5
      - importlib-metadata==4.8.3
      - iniconfig==1.1.1
      - nibabel==3.2.2
      - num2words==0.5.14
      - numpy==1.19.5
      - packaging==21.3
      - pandas==1.1.5
      - patsy==1.0.1
      - pluggy==1.0.0
      - py==1.11.0
      - pyparsing==3.1.4
      - pytest==7.0.1
      - pytest-xdist==3.0.2
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - scipy==1.5.4
      - six==1.17.0
      - tomli==1.2.3
      - typing-extensions==4.1.1
      - zipp==3.6.0
prefix: /opt/conda/envs/pybids

bids/layout/tests/test_layout.py::test_get_tr
bids/layout/tests/test_layout.py::test_layout_init bids/layout/tests/test_layout.py::test_layout_repr bids/layout/tests/test_layout.py::test_load_description bids/layout/tests/test_layout.py::test_get_metadata bids/layout/tests/test_layout.py::test_get_metadata2 bids/layout/tests/test_layout.py::test_get_metadata3 bids/layout/tests/test_layout.py::test_get_metadata4 bids/layout/tests/test_layout.py::test_get_metadata_meg bids/layout/tests/test_layout.py::test_get_metadata5 bids/layout/tests/test_layout.py::test_get_metadata_via_bidsfile bids/layout/tests/test_layout.py::test_get_bvals_bvecs bids/layout/tests/test_layout.py::test_get_subjects bids/layout/tests/test_layout.py::test_get_fieldmap bids/layout/tests/test_layout.py::test_get_fieldmap2 bids/layout/tests/test_layout.py::test_bids_json bids/layout/tests/test_layout.py::test_include bids/layout/tests/test_layout.py::test_layout_with_derivs bids/layout/tests/test_layout.py::test_layout_with_multi_derivs bids/layout/tests/test_layout.py::test_query_derivatives bids/layout/tests/test_layout.py::test_get_bidsfile_image_prop bids/layout/tests/test_layout.py::test_restricted_words_in_path bids/layout/tests/test_layout.py::test_derivative_getters
bids/layout/layout.py
bids/layout/layout.py