Skip to content

Add Mapbox Geocoding v6 lookup#1709

Open
takatea wants to merge 2 commits into
alexreisner:masterfrom
takatea:add-mapbox-v6-lookup
Open

Add Mapbox Geocoding v6 lookup#1709
takatea wants to merge 2 commits into
alexreisner:masterfrom
takatea:add-mapbox-v6-lookup

Conversation

@takatea

@takatea takatea commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Closes #1708

Summary

Adds opt-in support for the Mapbox Geocoding v6 API to the existing :mapbox lookup:

Geocoder.configure(mapbox: {version: 6})

v5 remains the default, so existing users are unaffected. Since the public result interface is identical between the two versions, this switches versions via configuration within the one lookup rather than registering a separate :mapbox_v6 lookup.

Design references

The design follows existing patterns in this gem:

  • Version switch via a lookup option: modeled on the Mapquest lookup's :open option, which switches between the licensed and open endpoint families within one lookup (lib/geocoder/lookups/mapquest.rb#L19, introduced in Changes to the Mapquest Service; includes support for the premium service #315).
  • Separate result class per response format: the v6 response is parsed by its own Geocoder::Result::MapboxV6 class, both because the response shapes are genuinely different and to keep the v5 code easy to delete later. When Mapbox eventually sunsets v5, removing it amounts to dropping the else branches and results/mapbox.rb — similar to how the HERE lookup was migrated to API v7 in place (Update HERE lookup to use Geocoder and Search API #1575, released in 1.8.0 with a CHANGELOG note).
  • #precision accessor: the v6 result exposes properties.coordinates.accuracy (rooftop / parcel / interpolated / ...) as #precision, following Geocoder::Result::Google#precision which returns the semantically equivalent location_type.

API differences handled

v5 (default) v6 (version: 6)
Forward GET /geocoding/v5/{dataset}/{query}.json GET /search/geocode/v6/forward?q={query}
Reverse same endpoint, {lon},{lat}.json in path GET /search/geocode/v6/reverse?longitude=&latitude=
Permanent storage separate dataset (mapbox.places-permanent) permanent=true request param
Address string place_name properties.full_address
Context flat array matched by id prefix structured properties.context object
State code properties.short_code (US-NY, needs splitting) context.region.region_code (NY)
Country code properties.short_code context.country.country_code

Changes

  • lib/geocoder/lookups/mapbox.rbversion: 6 branches for endpoint and params; v5 paths untouched. Same InvalidApiKey handling and semicolon truncation (Exception thrown when using mapbox and query contains semicolons #1299) apply to both versions
  • lib/geocoder/results/mapbox_v6.rb — parses the structured properties.context object; adds a #precision accessor for the v6 accuracy value
  • tests + fixtures following the existing mapbox_* patterns (test_helper.rb picks the fixture prefix by configured version)
  • README_API_GUIDE.md — documents the version option under the existing Mapbox section

Verified with live API

Ran the same queries through v5 and v6 with a real access token to confirm the result interfaces line up.

### query: "1600 Pennsylvania Avenue NW, Washington, DC"
v5 (Geocoder::Result::Mapbox):
  coordinates  [38.897684, -77.036574]
  address      "1600 Pennsylvania Avenue Northwest, Washington, District of Columbia 20500, United States"
  street       nil
  city         "Washington"
  state        "District of Columbia"
  state_code   "DC"
  postal_code  "20500"
  country      "United States"
  country_code "US"
  neighborhood "National Mall"
v6 (Geocoder::Result::MapboxV6):
  coordinates  [38.897684, -77.036574]
  address      "1600 Pennsylvania Avenue Northwest, Washington, District of Columbia 20500, United States"
  street       "1600 Pennsylvania Avenue Northwest"
  city         "Washington"
  state        "District of Columbia"
  state_code   "DC"
  postal_code  "20500"
  country      "United States"
  country_code "US"
  neighborhood "National Mall"
  precision    "rooftop"

### query: [40.750755, -73.993710125]  (reverse)
v5 (Geocoder::Result::Mapbox):
  coordinates  [40.750711, -73.993849]
  address      "445 Fashion Ave, New York, New York 10121, United States"
  street       nil
  city         "New York"
  state        "New York"
  state_code   "NY"
  postal_code  "10121"
  country      "United States"
  country_code "US"
  neighborhood "Chelsea"
v6 (Geocoder::Result::MapboxV6):
  coordinates  [40.750711, -73.993849]
  address      "445 Fashion Ave, New York, New York 10121, United States"
  street       "445 Fashion Ave"
  city         "New York"
  state        "New York"
  state_code   "NY"
  postal_code  "10121"
  country      "United States"
  country_code "US"
  neighborhood "Chelsea"
  precision    "rooftop"

Notes from live verification:

  • The two versions return identical values for every shared field; v6 additionally fills street (v5's street reads properties.address, which the live v5 API no longer populates for these queries) and exposes precision.
  • A POI query ("Madison Square Garden, New York, NY") resolves to a neighborhood on both versions — POI data has been removed from v5 as well, so this is not a v6 regression.
  • An invalid token returns Not Authorized - Invalid Token on v6 too, so the existing Geocoder::InvalidApiKey handling works unchanged.

Tests

$ bundle exec rake  # 613 tests, 3643 assertions, 0 failures, 0 errors

@takatea takatea marked this pull request as draft June 5, 2026 04:49
@takatea takatea changed the title Add Mapbox Geocoding v6 lookup (:mapbox_v6) [WIP] Add Mapbox Geocoding v6 lookup Jun 5, 2026
The :mapbox lookup uses the Geocoding v5 API. Mapbox has released the
Geocoding v6 API with different endpoints and a new structured response
format. Following the precedent of the Mapquest :open option, this adds
an opt-in version option to the existing lookup:

  Geocoder.configure(mapbox: {version: 6})

v5 remains the default so existing users are unaffected. The v6
response format is parsed by a separate result class
(Geocoder::Result::MapboxV6), which keeps the v5 code easy to remove
once Mapbox sunsets the old API.

Closes alexreisner#1708
@takatea takatea force-pushed the add-mapbox-v6-lookup branch from 148828b to 2fd3ec6 Compare June 5, 2026 04:58
@takatea takatea changed the title [WIP] Add Mapbox Geocoding v6 lookup Add Mapbox Geocoding v6 API support to :mapbox lookup Jun 5, 2026
@takatea takatea changed the title Add Mapbox Geocoding v6 API support to :mapbox lookup Add Mapbox Geocoding v6 lookup Jun 5, 2026
@takatea takatea marked this pull request as ready for review June 5, 2026 05:13
Exposes the v6 response's properties.coordinates.accuracy value
(rooftop / parcel / point / interpolated / approximate / intersection),
following the precedent of Geocoder::Result::Google#precision which
returns the semantically equivalent location_type. Returns nil for
administrative features (country, region, etc.) which carry no
accuracy value.
@alexreisner

Copy link
Copy Markdown
Owner

Thanks for this great PR! The code looks good with one exception. APIs change all the time, and one goal of Geocoder is to save users from having to think about those changes, while allowing them to use the latest/recommended API versions. So I think the default behavior for people using the :mapbox lookup should be for them to be silently upgraded to v6, assuming there's nothing that could break their apps. If there is any reason people might want to stick with v5, then we can have a separate v5 lookup (eg, mapbox_v5), but generally I only do that when there are compelling reasons to stick with an old version of the API long term. I haven't dug too far into the v6 changes--is there any reason to continue supporting v5 long-term, in your mind?

@takatea

takatea commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

@alexreisner Thank you, this makes a lot of sense to me! I agree with the philosophy that :mapbox users should be upgraded to v6 transparently.

To answer your question directly: I do not see a compelling reason to keep v5 supported long-term. In the live comparison, v6 returned identical values for every shared field, and it additionally fills street and exposes precision, so the result interface is effectively a superset of v5. I could not find any data that v5 returns but v6 does not. I am therefore happy to make v6 the default and remove v5 in place (following the HERE v7 approach from #1575), along with a CHANGELOG note.

There is one migration detail that I would like your call on, since it is the only thing I can see that might silently break existing apps:

Permanent storage. In v5, this is configured through the dataset:

Geocoder.configure(mapbox: { dataset: "mapbox.places-permanent" })

In v6, the dataset concept is gone and a request param is used instead, which already works today through Geocoder's generic params option:

Geocoder.search(address, params: { permanent: true })

The concern is that anyone currently relying on dataset: "mapbox.places-permanent" would, after a silent upgrade, start making non-permanent requests without noticing, which has billing/ToS implications for them.

How do you think we should handle this? Personally, I lean toward simply documenting it with a CHANGELOG / README migration note. That said, if you would prefer to emit a temporary deprecation instead, I am happy to map a permanent dataset setting to permanent: true for one release with a warning, and then drop it later. I can implement it either way, so I would appreciate your opinion before I update the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Mapbox: support the new Geocoding v6 API

2 participants