The @mapbox/mapbox-sdk request object model
A JavaScript client to Mapbox services, supporting Node, browsers, and React Native
At a glance
- What is it?
- The mapbox/mapbox-sdk-js repository packages Mapbox APIs as service clients that hand back request objects rather than firing HTTP calls. That choice buys pagination helpers, progress events and a typed error, at the cost of an abstraction layer you have to learn.
- Who is it for?
- Adopt @mapbox/mapbox-sdk when your team is calling several Mapbox services from Node, the browser or React Native and wants one consistent request, pagination and error model across them. Do not adopt it for a single hand-rolled GET, or if you need retry, caching or token rotation that the documented surface does not describe.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 43 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.
Editorial analysis
Service factories, not a single global client
The core design decision in @mapbox/mapbox-sdk is that there is no one monolithic API object. Instead, every Mapbox service lives behind its own factory function that you import from a path shaped like @mapbox/mapbox-sdk/services/styles. Calling that factory hands you a service client, and the methods on that client are the surface for the corresponding Mapbox API. The result is a directory of small modules rather than one large object, which is why the repository root carries a services/ directory alongside lib/.
Two ways of wiring it up are documented. The short form passes the access token straight into the factory. The longer form creates a base client once and hands that same object to every service factory, so a single token configuration is shared across styles, tilesets and anything else you pull in. The second pattern is the one to copy if you are talking to more than two services, because it removes repeated token passing from every call site.
The token itself is a plain string in a config object. Nothing in the documented API rotates it, caches it, or scopes it per service, so a multi-tenant application has to supply the right token at the point where it builds the base client. The README also notes that the service client exposes methods that create requests, which is the key to how the rest of the library works.
Service methods return a MapiRequest, not a Promise
This is the part that changes how you write application code. Calling a service method does not perform network I/O. It returns a MapiRequest object, and you start the work by invoking its send method, which returns a Promise resolving to a MapiResponse or rejecting with a MapiError.
The gap between creating and sending is small, but it is where the useful options live. A MapiRequest exposes abort for cancelling an in-flight request, eachPage for walking a paginated endpoint, and an emitter that fires events such as downloadProgress and uploadProgress. Because a request is a first-class object, you can build it, hold it, and decide later when it goes out, and you can attach a progress listener before sending instead of after.
The install step is short, and the older-browser caveat is the first real constraint the README raises.
npm install @mapbox/mapbox-sdkIf you have to support older browsers, the README states you will need a Promise polyfill, and it points at es6-promise as one option. That is a genuine cost of the design: the entire request lifecycle is expressed through Promises, so there is no callback-only path for legacy environments.
Sharing one client across several services
A concrete first use looks like this. Build the base client once, derive two service clients from it, then create and send a request through one of them.
const mbxClient = require('@mapbox/mapbox-sdk');
const mbxStyles = require('@mapbox/mapbox-sdk/services/styles');
const mbxTilesets = require('@mapbox/mapbox-sdk/services/tilesets');
const baseClient = mbxClient({ accessToken: MY_ACCESS_TOKEN });
const stylesService = mbxStyles(baseClient);
const tilesetsService = mbxTilesets(baseClient);The service client then exposes methods that create requests, and each request is sent separately. The README shows the same pattern twice, once for a write operation and once for a read:
// Create a style.
stylesService.createStyle({..})
.send()
.then(response => {..}, error => {..});
// List tilesets.
tilesetsService.listTilesets()
.send()
.then(response => {..}, error => {..})Note the shape of the arguments. The service method takes an options object, and the request is not sent until send is called, so the options and the sending step are separable. If your code needs to build a request from user input, validate the input, then dispatch it after some other work, that separation is useful. It also means a request that you create and never send costs nothing on the network.
Pagination is a callback contract, not a page number loop
Endpoints that return collections are the reason many people pick an SDK in the first place, and this one offers two different mechanisms. On the request side, eachPage executes a callback for each page of a paginated API response, and the callback decides when the next page is requested.
tilesetsService.listTilesets().eachPage((error, response, next) => {
// Do something with the page, then call next() to send the request
// for the next page.
// You can check whether there will be a next page using
// MapiResponse#hasNextPage, if you want to do something
// different on the last page.
if (!response.hasNextPage()) {..}
});On the response side, MapiResponse exposes hasNextPage, and nextPage returns a new MapiRequest you can send. That second route gives manual control: you can stop after the first page, persist a cursor, or spread the fetch across several ticks. Which one you want depends on whether pagination is a background drain or something a user is waiting on.
One thing to notice is that the cursor state lives on the response object rather than in a URL you build yourself. You are not assembling page URLs, so the paging convention of the underlying API is handled inside the request objects. The trade-off is that you cannot see or edit the paging parameters directly, which matters if you need to drive pagination yourself.
MapiResponse and MapiError keep parsing out of your way
A resolved send gives you a MapiResponse. The parsed payload is on body, the parsed HTTP headers are on headers, and the next-page helpers described above live on the same object. You read a body like this:
// Read a response body.
stylesService.getStyle({..})
.send()
.then(resp => {
const style = resp.body;
// Do something with the style.
}, err => {..});Failures arrive as MapiError, and the same object covers both a server-side HTTP failure and a request you aborted yourself. It exposes type so you can tell the two apart, statusCode for HTTP errors, body parsed as JSON when possible, and message for a human-readable description. Aborting is a first-class operation rather than an exception to the error model:
// Abort a request.
const req = tilesetsService.listTilesets();
req.send().then(response => {..}, error => {
// Because the request is aborted, an error will be thrown that we can
// catch and handle.
});
req.abort();Progress reporting uses the same object model, since MapiRequest exposes an emitter that fires downloadProgress and uploadProgress events. You subscribe on the request, then send it:
const req = stylesService.createStyleIcon({..});
req.on('uploadProgress', event => {
// Do something with the progress event information.
});
req.send().then(response => {..}, error => {..});For upload-heavy work, this is the piece that would otherwise require a hand-rolled HTTP client with a multipart body and a progress callback.
One package, three runtimes, and a custom licence question
The README states the SDK works in Node, the browser and React Native, and the package.json backs that up with a browser field that swaps ./lib/client.js for ./lib/browser/browser-client.js. That is the whole of the runtime story: the browser variant is a different client module behind the same public API. For scripts that do not use a JS module system, the README points to pre-bundled files on unpkg.com, which are generated by the bundle script that runs rollup and then uglifies the result.
Licensing needs a note rather than a conclusion. The package.json declares a license of BSD-2-Clause, and the author field reads Mapbox. The repository carries a LICENSE file at its root that GitHub classifies as a custom licence it cannot categorise. Both statements can be true, since a repository may ship extra licence text on top of a standard SPDX identifier, but the practical advice is to read the LICENSE file yourself and treat the SPDX field as metadata rather than as the full terms. That is a licensing question for whoever owns your compliance review, not something to settle from a package field.
The top-level layout also tells you what you are dealing with: index.js as the entry point, lib/ for the core classes, services/ for the per-service modules, docs/ for the generated class and service reference, conf/ for the documentation configuration, rollup.config.js for the bundle, and a test/ directory driven by jest.
What the documented surface leaves out
Two gaps are worth knowing before you commit a team to this package.
The first is retry and rate limit handling. The README does not document any retry policy, backoff behaviour, or handling of 429 responses, and the MapiError members that are described are type, statusCode, body and message. So the behaviour you get on a throttled endpoint is whatever the underlying request does, and the work of backing off, honouring Retry-After, and deduplicating in-flight requests falls to your application. For a bulk tileset importer that is a meaningful amount of code to write yourself.
The second is caching and token lifecycle. There is no documented cache layer, no request coalescing, and no token refresh, and the MapiRequest API listed in the README covers only abort, eachPage and the event emitter. The upstream package that this one replaced, the mapbox package, is explicitly deprecated in favour of @mapbox/mapbox-sdk, which means there is no older release to fall back on for behaviour you miss.
The alternative, and it is a real one rather than a strawman, is to call the Mapbox HTTP APIs directly with any HTTP client your stack already ships. You lose the MapiRequest and MapiResponse model, the eachPage helper, the progress events and the structured error object, and you gain complete control over timeouts, retries, logging and transport. If you are making one request in a script, direct HTTP is less code. If you are making many requests across many services and want uniform error handling, the SDK earns its place.
Version numbering and how fresh the code is
The repository is not archived and the last push was on 2026-09-10, so the code is being touched. The release history, though, moves at a different pace than the commits. The listed releases are v0.16.2 on 2025-09-12, v0.16.1 on 2024-09-13 and v0.15.16 on 2024-04-23, and package.json currently carries version 0.16.3, which is ahead of the most recent tagged release in that list. The project has stayed in the 0.x line throughout, so the public API is not on a promise of stability, and the semver major version carries that warning for you.
The package.json also shows how the project is put together internally. The test script is jest, and the pretest hook runs a lint pass that fans out to eslint and a remark markdown check. The document script regenerates docs/services.md and docs/classes.md from the source with the documentation tool, which is what the README points readers to for the full MapiRequest, MapiResponse and MapiError members. There is a precommit hook that regenerates those docs and stages them, so the class reference in the repository should track the source rather than drifting behind it. The bundle script and the prepublishOnly hook mean the published package is the rollup-bundled build, not the raw source tree.
Editorial conclusion
Adopt @mapbox/mapbox-sdk when your team is calling several Mapbox services from Node, the browser or React Native and wants one consistent request, pagination and error model across them. Do not adopt it for a single hand-rolled GET, or if you need retry, caching or token rotation that the documented surface does not describe. Before committing, read docs/classes.md for the full MapiRequest, MapiResponse and MapiError members, and check the LICENSE file yourself, since the package.json declares BSD-2-Clause while the repository license is a custom file GitHub cannot classify.
Frequently asked questions
How do I install @mapbox/mapbox-sdk and create a client?
Install it with npm install @mapbox/mapbox-sdk, then import the factory for the service you need from @mapbox/mapbox-sdk/services/{service} and pass it an access token. For several services, create a base client with mbxClient({ accessToken: MY_ACCESS_TOKEN }) and pass that base client into each service factory.
How do I paginate through Mapbox API results with the SDK?
Use MapiRequest#eachPage, which calls a callback for each page and lets the callback request the next one, or send the request and use MapiResponse#hasNextPage plus MapiResponse#nextPage to fetch the following page manually. The paging convention is handled inside the request objects rather than in a URL you assemble.
Does @mapbox/mapbox-sdk work in the browser and in React Native?
The README states it works in Node, the browser and React Native, and the package.json browser field points ./lib/client.js at ./lib/browser/browser-client.js. For older browsers you will need a Promise polyfill, and for non-module scripts the README points to pre-bundled files on unpkg.com.
Is @mapbox/mapbox-sdk still being worked on?
The repository is not archived and the last push was on 2026-09-10, while the listed releases are v0.16.2 on 2025-09-12 and v0.16.1 on 2024-09-13. The project has remained on the 0.x line, so the public interface carries no stability guarantee.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/mapbox-mapbox-sdk-js)Was this useful?