diff --git a/src/org/openstreetmap/josm/data/imagery/ImageryPatterns.java b/src/org/openstreetmap/josm/data/imagery/ImageryPatterns.java
index 30d0e99e0b..bcc6893462 100644
--- a/src/org/openstreetmap/josm/data/imagery/ImageryPatterns.java
+++ b/src/org/openstreetmap/josm/data/imagery/ImageryPatterns.java
@@ -4,12 +4,15 @@ package org.openstreetmap.josm.data.imagery;
 import static org.openstreetmap.josm.tools.I18n.marktr;
 import static org.openstreetmap.josm.tools.I18n.tr;
 
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Objects;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.openstreetmap.gui.jmapviewer.FeatureAdapter;
+
 /**
  * Patterns that can be replaced in imagery URLs.
  * @since 17578
@@ -29,16 +32,22 @@ public final class ImageryPatterns {
     static final Pattern PATTERN_HEIGHT = Pattern.compile("\\{height\\}");
     static final Pattern PATTERN_TIME   = Pattern.compile("\\{time\\}"); // Sentinel-2
     static final Pattern PATTERN_PARAM  = Pattern.compile("\\{([^}]+)\\}");
+    /**
+     * The api key pattern is used to allow us to quickly switch apikeys. This is functionally the same as the pattern
+     * in {@link org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource}.
+     */
+    static final Pattern PATTERN_API_KEY = Pattern.compile("\\{apikey}");
     // CHECKSTYLE.ON: SingleSpaceSeparator
 
     private static final Pattern[] ALL_WMS_PATTERNS = {
             PATTERN_HEADER, PATTERN_PROJ, PATTERN_WKID, PATTERN_BBOX,
             PATTERN_W, PATTERN_S, PATTERN_E, PATTERN_N,
-            PATTERN_WIDTH, PATTERN_HEIGHT, PATTERN_TIME
+            PATTERN_WIDTH, PATTERN_HEIGHT, PATTERN_TIME,
+            PATTERN_API_KEY
     };
 
     private static final Pattern[] ALL_WMTS_PATTERNS = {
-            PATTERN_HEADER
+            PATTERN_HEADER, PATTERN_API_KEY
     };
 
     private ImageryPatterns() {
@@ -74,4 +83,23 @@ public final class ImageryPatterns {
         matcher.appendTail(output);
         return output.toString();
     }
+
+    /**
+     * Handle the {@link #PATTERN_API_KEY} replacement
+     * @param id The id of the info
+     * @param url The templated url
+     * @return The templated url with {@link #PATTERN_API_KEY} replaced
+     */
+    static String handleApiKeyTemplate(final String id, final String url) {
+        if (id != null && url != null) {
+            try {
+                final String apiKey = FeatureAdapter.retrieveApiKey(id);
+                return PATTERN_API_KEY.matcher(url).replaceAll(apiKey);
+            } catch (IOException | NullPointerException e) {
+                // Match behavior in JMapViewer TemplatedTMSTileSource
+                throw new IllegalArgumentException(e);
+            }
+        }
+        return url;
+    }
 }
diff --git a/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java b/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
index e2803b993b..ba00497521 100644
--- a/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
+++ b/src/org/openstreetmap/josm/data/imagery/TemplatedWMSTileSource.java
@@ -44,7 +44,7 @@ public class TemplatedWMSTileSource extends AbstractWMSTileSource implements Tem
         this.serverProjections = new TreeSet<>(info.getServerProjections());
         this.headers.putAll(info.getCustomHttpHeaders());
         this.date = info.getDate();
-        this.baseUrl = ImageryPatterns.handleHeaderTemplate(baseUrl, headers);
+        this.baseUrl = ImageryPatterns.handleApiKeyTemplate(info.getId(), ImageryPatterns.handleHeaderTemplate(baseUrl, headers));
         initProjection();
         // Bounding box coordinates have to be switched for WMS 1.3.0 EPSG:4326.
         //
diff --git a/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java b/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java
index 5ab3a3a358..11c743e4ba 100644
--- a/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java
+++ b/src/org/openstreetmap/josm/data/imagery/WMTSTileSource.java
@@ -360,7 +360,8 @@ public class WMTSTileSource extends AbstractTMSTileSource implements TemplatedTi
         super(info);
         CheckParameterUtil.ensureThat(info.getDefaultLayers().size() < 2, "At most 1 default layer for WMTS is supported");
         this.headers.putAll(info.getCustomHttpHeaders());
-        this.baseUrl = GetCapabilitiesParseHelper.normalizeCapabilitiesUrl(ImageryPatterns.handleHeaderTemplate(info.getUrl(), headers));
+        this.baseUrl = GetCapabilitiesParseHelper.normalizeCapabilitiesUrl(
+                ImageryPatterns.handleApiKeyTemplate(info.getId(), ImageryPatterns.handleHeaderTemplate(info.getUrl(), headers)));
         WMTSCapabilities capabilities = getCapabilities(baseUrl, headers);
         this.layers = capabilities.getLayers();
         this.baseUrl = capabilities.getBaseUrl();
