Stop paying per tile: basemap sessions on ArcGIS Location Platform
By default, every basemap tile your ArcGIS Location Platform app requests is a line on your bill. Every pan, every zoom, every basemap switch pulls more tiles — which makes the one thing your best users do most (explore the map) the one thing you can’t predict the cost of. The session usage model flips that: one billed session buys a single user unlimited basemap tiles for up to 12 hours. And since ArcGIS Maps SDK for JavaScript 5.0, turning it on is one config block.
This post walks through both basemap usage models on the Basemap Styles service, the billing mechanics that aren’t obvious until you read the fine print, the decision criteria we use when we architect an app, and the implementation in both the ArcGIS Maps SDK for JavaScript and MapLibre GL JS.
The two usage models
The ArcGIS Basemap Styles service supports two application programming patterns, and the one you implement determines what you’re billed for:
| Tile usage model | Session usage model | |
|---|---|---|
| Billed unit | Each basemap tile returned | Each session created |
| Cost shape | Scales with user interaction — open-ended | Flat per user per session — capped |
| Token | Access token (API key) on every request | Short-lived session token from /sessions/start |
| Max lifetime | n/a | 12 hours per session |
| Style access | All styles in the service | Only the style family the session was created for (arcgis or open) |
| Best for | Prototypes; focused, low-interaction maps | Interactive, long-running, high-tile-volume apps |
The tile model is what you get implicitly when you drop an API key into a map: the style request returns
tiles, and usage accrues for every tile consumed — on initial load, on style change, and on every pan
and zoom. Esri’s own guidance for keeping tile costs sane is telling: start the map at a focused
extent, cap minZoom/maxZoom, constrain
maxBounds, and disable rotation and pitch. In other words: spend less by
letting users do less. That’s fine for a store locator. It’s exactly wrong for the premium,
exploratory apps we build — where map interaction is the product.
How a basemap session works
A basemap session is a time window during which a single user of a single application uses a session token to pull unlimited basemap tiles. The mechanics, per the basemap usage documentation:
- Your app authenticates with a normal access token (API key credential) carrying the
premium:user:basemapsprivilege. - It calls the Basemap Styles service
/sessions/startendpoint, declaring astyleFamily(arcgisoropen) and a duration — up to 43,200 seconds (12 hours). - The service returns a session token. From here on, every style and tile request uses that token instead of the API key.
- When the session expires, the app starts a new session and swaps the token. One user, one app run, one (or a few) billed sessions — no matter how many thousands of tiles they consumed.
The session token is deliberately narrow: it can only reach the Basemap Styles service it was created from, and only styles in the family it was created for. It is not a general-purpose credential — geocoding, routing, and data services still use your regular access token. That separation is good security hygiene for free: the token you expose most often is the one that can do the least.
The billing fine print worth knowing
Three details from the Location Platform billing documentation that change how you design:
- A session bills when it’s created, not when tiles flow. You’re charged per session created “regardless of the number of tiles consumed during that session, even if no tiles are requested.” Don’t start a session at page load if the map is behind a tab the user may never open — start it when the map actually mounts.
- A session dies with the app run. A session token is only valid while the application is running; a refresh or restart means a new session. For a single-page app that’s usually fine. For a multi-page site where each navigation reloads the map, per-tile billing may actually be cheaper — count before you switch.
- Browser-cached tiles are never metered (under either model), and style JSON, fonts, glyphs, and sprites aren’t metered either. Also note: tiles you consume signed-in inside ArcGIS apps like Map Viewer are always metered per-tile — the session model only applies to your own applications.
Enabling sessions in the ArcGIS Maps SDK for JavaScript
Before 5.0 you had to manage the /sessions/start handshake and token swap
yourself. As of version 5.0 (February 2026) the SDK ships a first-class
config.sessions API that does it all —
including automatic token refresh at the end of each session:
import esriConfig from "@arcgis/core/config.js"; esriConfig.apiKey = import.meta.env.VITE_ARCGIS_API_KEY; // never hard-code it esriConfig.sessions = { basemap: { enabled: true, styleFamily: "arcgis", // session can only access "arcgis" styles autoRefresh: true, // new session token at end of duration duration: 43200, // seconds — 12 hours, the maximum }, };
That’s the entire integration. The SDK starts the session, routes basemap style and tile requests
through the session token, keeps your API key for everything else, and — with
autoRefresh — rolls the session over when it expires. Esri’s
Display a map with a basemap session tutorial walks the same setup
end-to-end.
Doing it by hand in MapLibre GL JS
On the open-source side there’s no config flag, but the REST handshake is small. Start the session
against /sessions/start, then build style URLs with the returned
session token:
const createBasemapSession = async (accessToken) => { const res = await fetch( "https://basemapstyles-api.arcgis.com/arcgis/rest/services/styles/v2/sessions/start?" + new URLSearchParams({ styleFamily: "arcgis", durationSeconds: 43200 }), { headers: { Authorization: `Bearer ${accessToken}` } } ); return res.json(); // { sessionToken, endTime, ... } }; const session = await createBasemapSession(accessToken); const map = new maplibregl.Map({ container: "map", style: `https://basemapstyles-api.arcgis.com/arcgis/rest/services/styles/v2/styles/arcgis/navigation?token=${session.sessionToken}`, });
Track endTime yourself and start a fresh session when it passes. One trap from
the docs: the map may cache the original token inside style resources — on refresh you may need to
replace the style entirely (map.setStyle(...) with the new token), not just the
token string.
Which model should your app use?
Our rule of thumb when we scope a Location Platform app:
- Stay on tiles while prototyping and testing, for focused single-extent maps with deliberately limited navigation, and for multi-page sites that would burn a fresh session on every page load.
- Switch to sessions for interactive single-page apps where users explore freely, for long-running operational dashboards and kiosk displays, when the app offers multiple basemap styles (style switching is free within the session’s family), and whenever you need to quote a customer a predictable per-user cost — users × sessions is an estimate you can actually stand behind; tiles consumed is not.
References
- Basemap usage — tile and session usage models (Esri Developer documentation)
- ArcGIS Maps SDK for JavaScript 5.0 release notes — basemap sessions
- config.sessions API reference — ArcGIS Maps SDK for JavaScript
- Tutorial: Display a map with a basemap session
- Basemap Styles service — sessions/start (REST reference)
- ArcGIS Location Platform — Billing (basemaps section)
Not sure what your basemap usage will cost at scale?
We architect ArcGIS Location Platform apps with the usage model, token strategy, and cost controls decided up front — so the bill at 1,000 users looks like the estimate at 10.
Book a free intro call