Map SDK Overview

Get Started

Jumpstart your mapmaking experience, bringing geospatial analytics and data science to your browser.



API Explorer

Get Started

Jump into the Map SDK on your environment of choice.




Map SDK Guides

The Map SDK has many endpoints you can leverage to programmatically manipulate and embed maps.



Data

Data

Add, update, and remove datasets via the Map SDK.

Filters

Filters

Apply filters across your dataset. Filters can also be used to explore temporal data through animation

Layers

Layers

Use layers to visualize geospatial data. Multiple layers can be added to a map, visualizing any number of datasets (or different aspects of the same dataset).

Maps

Maps

Discover how to configure the map — the foundation for your Studio projects.

Annotations

Annotations

Add labels with text and pointers to your maps.




Reference Documentation

Looking for the exact details behind a class or function? Our reference documentation contains detailed tables defining each parameter in class definitions and function methods.



Reference Documentation

Reference Pages

Find more detail in the reference documentation.




Product Support



API Status

Release Notes

Discover the most recent improvements, including the addition of new functionality and other important changes.

API Status

FAQs

Get the answers to our most common questions regarding usage of the Developer Console and related topics.





${value.text.secondary}
`; } async function selectItem({ target }) { if (target.tagName === "BUTTON") { const link = target.dataset.object; const addressDetail = await fetchAddressDetails(link); const { location = {} } = addressDetail; const { address = "", country = "", postcode = "", locality = "", region = "", } = location; addressInput.value = address; address2Input.value = ""; countryInput.value = country; postcodeInput.value = postcode; cityInput.value = locality; regionInput.value = region; // generate new session token after a complete search sessionToken = generateRandomSessionToken(); address2Input && address2Input.focus(); dropDownField.style.display = "none"; } } async function fetchAddressDetails(link) { try { const results = await fetch(`https://api.foursquare.com${link}`, { method: "get", headers: new Headers({ Accept: "application/json", Authorization: fsqAPIToken, }), }); const data = await results.json(); return data; } catch (err) { logError(err); } } function highlightedNameElement(textObject) { if (!textObject) return ""; const { primary, highlight } = textObject; if (highlight && highlight.length) { let beginning = 0; let hightligtedWords = ""; for (let i = 0; i " + primary.substr(start, length) + ""; beginning = start + length; } hightligtedWords += primary.substr(beginning); return hightligtedWords; } return primary; } function debounce(func, timeout = 300) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); }; } } localAddressAutoFillJs(); } if (state && state.params && state.params.slug === "local-search-map") { function loadLocalMapSearchJs() { mapboxgl.accessToken = "pk.eyJ1IjoiZm91cnNxdWFyZSIsImEiOiJjbDNqNXdrN20wN3JtM2JvMWFqZGxoaGljIn0.uSxJ2t7E96TrBFsn3cXT_g"; const fsqAPIToken = "fsq3bgqdcpLAJFkodk8gisc2F+NenA7gK/zI97A9nKQAXIw="; let userLat = 40.7128; let userLng = -74.006; let sessionToken = generateRandomSessionToken(); const inputField = document.getElementById("explorer-search"); const dropDownField = document.getElementById("explorer-dropdown"); const ulField = document.getElementById("explorer-suggestions"); const errorField = document.getElementById("explorer-error"); const notFoundField = document.getElementById("explorer-not-found"); const onChangeAutoComplete = debounce(changeAutoComplete); inputField.addEventListener("input", onChangeAutoComplete); ulField.addEventListener("click", selectItem); function success(pos) { const { latitude, longitude } = pos.coords; userLat = latitude; userLng = longitude; flyToLocation(userLat, userLng); } function logError(err) { console.warn(`ERROR(${err.code}): ${err.message}`); } navigator.geolocation.getCurrentPosition(success, logError, { enableHighAccuracy: true, timeout: 5000, maximumAge: 0, }); const map = new mapboxgl.Map({ container: "map", style: "mapbox://styles/mapbox/light-v10", center: [userLng, userLat], zoom: 12, }); map.addControl(new mapboxgl.GeolocateControl()); map.addControl(new mapboxgl.NavigationControl()); let currentMarker; /* Generate a random string with 32 characters. Session Token is a user-generated token to identify a session for billing purposes. Learn more about session tokens. https://docs.foursquare.com/reference/session-tokens */ function generateRandomSessionToken(length = 32) { let result = ""; const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; for (let i = 0; i { addItem(value); }); } else { notFoundField.innerHTML = `Foursquare can't find ${inputSearch}. Make sure your search is spelled correctly. Don't see the place you're looking for?.`; notFoundField.style.display = "block"; } } catch (err) { errorField.style.display = "block"; logError(err); } finally { isFetching = false; dropDownField.style.display = "block"; } } else { dropDownField.style.display = "none"; } } async function autoComplete(query) { const { lng, lat } = map.getCenter(); userLat = lat; userLng = lng; try { const searchParams = new URLSearchParams({ query, types: "place", ll: `${userLat},${userLng}`, radius: 50000, session_token: sessionToken, }).toString(); const searchResults = await fetch( `https://api.foursquare.com/v3/autocomplete?${searchParams}`, { method: "get", headers: new Headers({ Accept: "application/json", Authorization: fsqAPIToken, }), } ); const data = await searchResults.json(); return data.results; } catch (error) { throw error; } } function addItem(value) { const placeDetail = value[value.type]; if (!placeDetail || !placeDetail.geocodes || !placeDetail.geocodes.main) return; const { latitude, longitude } = placeDetail.geocodes.main; const fsqId = placeDetail.fsq_id; const dataObject = JSON.stringify({ latitude, longitude, fsqId }); ulField.innerHTML += `
  • ${highlightedNameElement(value.text)}
    ${value.text.secondary}
  • `; } async function selectItem({ target }) { if (target.tagName === "LI") { const valueObject = JSON.parse(target.dataset.object); const { latitude, longitude, fsqId } = valueObject; const placeDetail = await fetchPlacesDetails(fsqId); addMarkerAndPopup(latitude, longitude, placeDetail); flyToLocation(latitude, longitude); // generate new session token after a complete search sessionToken = generateRandomSessionToken(); const name = target.dataset.name; inputField.value = target.children[0].textContent; dropDownField.style.display = "none"; } } async function fetchPlacesDetails(fsqId) { try { const searchParams = new URLSearchParams({ fields: "fsq_id,name,geocodes,location,photos,rating", session_token: sessionToken, }).toString(); const results = await fetch( `https://api.foursquare.com/v3/places/${fsqId}?${searchParams}`, { method: "get", headers: new Headers({ Accept: "application/json", Authorization: fsqAPIToken, }), } ); const data = await results.json(); return data; } catch (err) { logError(err); } } function createPopup(placeDetail) { const { location = {}, name = "", photos = [], rating } = placeDetail; let photoUrl = "https://files.readme.io/c163d6e-placeholder.svg"; if (photos.length && photos[0]) { photoUrl = `${photos[0].prefix}56${photos[0].suffix}`; } const popupHTML = `
    photo of ${name}
    ${name}
    ${location.address}
    ${ rating ? `
    ${rating}
    ` : `
    ` }
    `; const markerHeight = 35; const markerRadius = 14; const linearOffset = 8; const verticalOffset = 8; const popupOffsets = { top: [0, verticalOffset], "top-left": [0, verticalOffset], "top-right": [0, verticalOffset], bottom: [0, -(markerHeight + verticalOffset)], "bottom-left": [ 0, (markerHeight + verticalOffset - markerRadius + linearOffset) * -1, ], "bottom-right": [ 0, (markerHeight + verticalOffset - markerRadius + linearOffset) * -1, ], left: [ markerRadius + linearOffset, (markerHeight - markerRadius) * -1, ], right: [ -(markerRadius + linearOffset), (markerHeight - markerRadius) * -1, ], }; return new mapboxgl.Popup({ offset: popupOffsets, closeButton: false, }).setHTML(popupHTML); } function addMarkerAndPopup(lat, lng, placeDetail) { if (currentMarker) currentMarker.remove(); currentMarker = new mapboxgl.Marker({ color: "#3333FF", }) .setLngLat([lng, lat]) .setPopup(createPopup(placeDetail)) .addTo(map); currentMarker.togglePopup(); } function flyToLocation(lat, lng) { map.flyTo({ center: [lng, lat], }); } function highlightedNameElement(textObject) { if (!textObject) return ""; const { primary, highlight } = textObject; if (highlight && highlight.length) { let beginning = 0; let hightligtedWords = ""; for (let i = 0; i " + primary.substr(start, length) + ""; beginning = start + length; } hightligtedWords += primary.substr(beginning); return hightligtedWords; } return primary; } function debounce(func, timeout = 300) { let timer; return (...args) => { clearTimeout(timer); timer = setTimeout(() => { func.apply(this, args); }, timeout); }; } } loadLocalMapSearchJs(); } }); $(window).on("pageLoad", function (e, state) { const openSearchPopup = () => { const headerSearch = document.getElementById("hub-search-results"); if (headerSearch) { headerSearch.classList.add("hub-search-results-active"); document .getElementsByClassName("Input Input_md SearchBox-InputUQZAW9QXMe-c")[0] ?.focus(); } }; const triggerSearchButton = document.getElementsByClassName( "landing-page-trigger-search-button" )[0]; if (triggerSearchButton) { triggerSearchButton.addEventListener("click", openSearchPopup); } }); // START: Add Segment Tracking to Try It button // TODO: Verify this runs on route changes, so button gets instrumented on every page $(window).on("load", function (e, state) { var tryItBtn = document.querySelector(".rm-TryIt"); // in case the button has not yet renedered if (tryItBtn === undefined) return; tryItBtn.addEventListener("click", () => { console.log("Try It button Clicked (project)"); analytics.track("Try It button Clicked"); }); }); // END: Add Segment Tracking to Try It button // CUSTOM LAUNCH TOP NAV CODE - README document.addEventListener('DOMContentLoaded', () => { setTimeout(() => { document.querySelectorAll('.Header-leftADQdGVqx1wqU a[href]').forEach(a => { a.addEventListener('click', e => { e.preventDefault(); window.location.href = a.href; }); }); }, 100); }); // CUSTOM LAUNCH SIDEBAR CODE (FINAL project: inject Places first, then Users under Welcome) // Runs after a short delay to avoid flicker or unwanted auto-expansion. document.addEventListener("DOMContentLoaded", function () { // Delay entire injection by 1 second setTimeout(() => { (async function() { // Normalize path (strip trailing slash) const path = window.location.pathname.replace(/\/$/, ''); // Only act on the FINAL project paths if (!path.startsWith('/developer/reference/')) { return; } // Pages to pull from: Places API overview and Users API overview // **Places first**, then Users const toInject = [ { otherPageUrl: '/fsq-developers-places/reference/places-api-overview', targetSectionText: 'Places API' }, { otherPageUrl: '/fsq-developers-users/reference/users-api-overview', targetSectionText: 'Users API' } ]; // If versioned (e.g. /v2023-05-01/ in URL), insert version segment into otherPageUrl const versionMatch = window.location.pathname.match(/\/(v\d{4}-\d{2}-\d{2})\//); function applyVersion(url) { if (versionMatch) { const version = versionMatch[1]; return url.replace('/reference/', `/${version}/reference/`); } return url; } // Fetch & cache a single section's HTML from another page async function fetchSectionHtml(otherPageUrl, targetSectionText) { const versionedUrl = applyVersion(otherPageUrl); const cacheKey = `crossSection:${versionedUrl}:${targetSectionText}`; const cached = sessionStorage.getItem(cacheKey); if (cached) { return cached; } try { const resp = await fetch(versionedUrl, { credentials: 'same-origin' }); if (!resp.ok) { console.warn('[CrossInject] Fetch failed', resp.status, versionedUrl); return null; } const htmlText = await resp.text(); // Parse to DOM const parser = new DOMParser(); const doc = parser.parseFromString(htmlText, 'text/html'); // Find the sidebar section const sidebarSelector = '.rm-Sidebar'; const otherSidebar = doc.querySelector(sidebarSelector); if (!otherSidebar) { console.warn('[CrossInject] Sidebar not found for', versionedUrl); return null; } // Locate the
    whose

    includes targetSectionText let foundSection = null; otherSidebar.querySelectorAll('section').forEach(sec => { if (foundSection) return; const h2 = sec.querySelector('h2'); if (h2 && h2.textContent.trim().includes(targetSectionText)) { foundSection = sec; } }); if (!foundSection) { console.warn('[CrossInject] Section not found:', targetSectionText, 'in', versionedUrl); return null; } const sectionHtml = foundSection.outerHTML; try { sessionStorage.setItem(cacheKey, sectionHtml); } catch (_) {} return sectionHtml; } catch (err) { console.error('[CrossInject] Error fetching/parsing:', err); return null; } } // Wait until the current page's sidebar is present function whenSidebarReady(fn) { const interval = setInterval(() => { const sidebarNav = document.querySelector('.rm-Sidebar'); if (sidebarNav) { clearInterval(interval); fn(sidebarNav); } }, 100); // Stop polling after 5s setTimeout(() => clearInterval(interval), 5000); } // Collapse helper for a section element function collapseSection(sec) { const h2 = sec.querySelector('h2'); const ul = sec.querySelector('ul'); if (h2) { h2.classList.remove('section-expanded'); h2.classList.add('section-collapsed'); h2.setAttribute('aria-expanded', 'false'); } if (ul) { ul.classList.add('section-collapsed'); ul.classList.remove('section-list-expanded'); } } // Expand helper function expandSection(sec) { const h2 = sec.querySelector('h2'); const ul = sec.querySelector('ul'); if (h2) { h2.classList.remove('section-collapsed'); h2.classList.add('section-expanded'); h2.setAttribute('aria-expanded', 'true'); } if (ul) { ul.classList.remove('section-collapsed'); ul.classList.add('section-list-expanded'); } } // Fetch all needed sections in parallel const results = await Promise.all( toInject.map(item => fetchSectionHtml(item.otherPageUrl, item.targetSectionText) .then(html => ({ ...item, sectionHtml: html })) ) ); const validSections = results.filter(r => r.sectionHtml); if (validSections.length === 0) { return; } // Once our sidebar is ready, inject under "Welcome" whenSidebarReady(sidebarNav => { const contentDiv = sidebarNav.querySelector('.hub-sidebar-content'); if (!contentDiv) { console.warn('[CrossInject] .hub-sidebar-content not found; abort insertion.'); return; } // Find "Welcome" section const welcomeSection = Array.from(contentDiv.querySelectorAll('section')).find(sec => { const h2 = sec.querySelector('h2'); return h2 && h2.textContent.trim() === 'Welcome'; }); let insertAfter = welcomeSection; validSections.forEach(({ targetSectionText, sectionHtml }) => { const wrapper = document.createElement('div'); wrapper.innerHTML = sectionHtml; const originalSectionNode = wrapper.firstElementChild; if (!originalSectionNode) { console.warn('[CrossInject] No section node for', targetSectionText); return; } const sectionClone = originalSectionNode.cloneNode(true); // Adapt heading & toggle as needed: const injectedH2 = sectionClone.querySelector('h2'); const injectedUL = sectionClone.querySelector('ul'); if (injectedH2 && injectedUL) { // Copy classes from an existing template

    if present const templateH2 = sidebarNav.querySelector('.hub-sidebar-content section h2'); if (templateH2) { injectedH2.className = templateH2.className; } else { injectedH2.classList.add('Sidebar-headingTRQyOa2pk0gh', 'rm-Sidebar-heading'); } injectedH2.setAttribute('tabindex', '0'); // Remove existing chevrons, clone from template or fallback Array.from(injectedH2.querySelectorAll('.icon-chevron')).forEach(el => el.remove()); if (templateH2) { templateH2.querySelectorAll('.icon-chevron').forEach(iconSpan => { injectedH2.appendChild(iconSpan.cloneNode(true)); }); } else { const fallbackSpan = document.createElement('span'); fallbackSpan.className = 'icon-chevron'; injectedH2.appendChild(fallbackSpan); } // Initially collapsed collapseSection(sectionClone); // Toggle handler const toggleFn = () => { const isCollapsed = injectedUL.classList.contains('section-collapsed'); if (isCollapsed) { expandSection(sectionClone); } else { collapseSection(sectionClone); } }; injectedH2.style.cursor = 'pointer'; injectedH2.addEventListener('click', toggleFn); injectedH2.addEventListener('keydown', e => { if (e.key === 'Enter' || e.key === ' ' || e.key === 'Spacebar') { e.preventDefault(); toggleFn(); } }); } // Nested toggles sectionClone.querySelectorAll('a.Sidebar-link_parent').forEach(aParent => { let btn = aParent.querySelector('button'); if (!btn && aParent.nextElementSibling && aParent.nextElementSibling.tagName === 'BUTTON') { btn = aParent.nextElementSibling; } let subUl = null; if (btn) { const maybeUl = btn.parentElement.nextElementSibling; if (maybeUl && maybeUl.classList.contains('subpages')) subUl = maybeUl; } if (!subUl) { const parentLi = aParent.closest('li'); if (parentLi) { const maybe = parentLi.querySelector('ul.subpages'); if (maybe) subUl = maybe; } } if (btn && subUl) { // collapsed by default btn.setAttribute('aria-expanded', 'false'); subUl.classList.add('section-collapsed'); subUl.classList.remove('section-list-expanded'); btn.style.cursor = 'pointer'; btn.addEventListener('click', e => { e.preventDefault(); const expanded = btn.getAttribute('aria-expanded') === 'true'; if (expanded) { btn.setAttribute('aria-expanded', 'false'); subUl.classList.add('section-collapsed'); subUl.classList.remove('section-list-expanded'); } else { btn.setAttribute('aria-expanded', 'true'); subUl.classList.remove('section-collapsed'); subUl.classList.add('section-list-expanded'); } }); } }); // Insert right after Welcome (Places first, then Users) if (insertAfter && insertAfter.parentElement === contentDiv) { insertAfter.insertAdjacentElement('afterend', sectionClone); insertAfter = sectionClone; } else { contentDiv.appendChild(sectionClone); } console.log(`[CrossInject] Injected "${targetSectionText}" after Welcome.`); }); // After injecting both, auto-expand whichever matches current path function normalizeHref(href) { try { const url = new URL(href, window.location.origin); return url.pathname.replace(/\/$/, ''); } catch { return href.replace(/\/$/, ''); } } const normPath = window.location.pathname.replace(/\/$/, ''); // Clear any previous active marks: contentDiv.querySelectorAll('a[aria-current]').forEach(a => { a.removeAttribute('aria-current'); a.classList.remove('active'); }); // Find matches among injected links: const allLinks = Array.from(contentDiv.querySelectorAll('a')); allLinks.forEach(a => { const href = a.getAttribute('href'); if (!href) return; if (normalizeHref(href) === normPath) { // mark active a.setAttribute('aria-current', 'page'); a.classList.add('active'); // expand its parent section let sec = a.closest('section'); if (sec) expandSection(sec); // expand ancestors let anc = a.parentElement; while (anc && anc !== contentDiv) { if (anc.tagName === 'UL' && anc.classList.contains('subpages')) { anc.classList.remove('section-collapsed'); anc.classList.add('section-list-expanded'); const parentLi = anc.closest('li'); if (parentLi) { const btn = parentLi.querySelector('button[aria-expanded]'); if (btn) btn.setAttribute('aria-expanded', 'true'); } } if (anc.tagName === 'SECTION') { expandSection(anc); } anc = anc.parentElement; } } }); // If none matched exactly, all remain collapsed. }); })(); }, 600); });