Note, August 2026: this pipeline was built here and now runs at MappingBitcoin, where the directory lives. It is also one of the projects we link from our projects page, which is worth saying before a post that quotes its documentation throughout. The description below follows what that documentation states, and most of it applies to any map built on the same data.
What you will end up with
An understanding of the four stages between an OpenStreetMap tag and a listing on a map, and your own query returning the raw records that feed stage one, so the pipeline stops being a black box you have to take on trust.
The end of this post is a query you can run in a browser against a public API, and a number on a live page to check it against. If the two do not match for your country, you now know where to start looking.
Before you start
- A browser. That is genuinely all the query needs.
- A city or region in mind, and ideally an expectation of roughly how many bitcoin merchants it has, so the result has something to be checked against.
- How to read a listing helps, because the failure modes below are the reasons those listings are unreliable.
Checked on 20 August 2026 against MappingBitcoin's published documentation for OSM sync, data enrichment and the API, its statistics and country pages, and the public Overpass endpoint, which returned the counts quoted below.
The tradeoff you are accepting
Every stage below trades coverage against correctness, and there is no setting where you get both. Detection that is strict enough to avoid noise misses real merchants; detection loose enough to catch them all pulls in records that are not merchants at all. The same choice reappears at every stage. When you see a directory that is unusually complete, it is usually unusually wrong somewhere, and when you see one that is unusually clean, it is usually missing things.
1. Detection
The source is OpenStreetMap, a collaborative geographic database anyone can edit. A business is marked as accepting bitcoin by tagging it, and MappingBitcoin's sync documentation lists the four tags it watches for:
currency:XBT
payment:bitcoin
payment:lightning
bitcoin
It loads the initial set through the Overpass API across nodes, ways and relations, then follows OpenStreetMap's replication feed: a job runs every minute, parses the XML diffs, tracks creations, modifications and deletions, and queues whatever changed for the next stage.
Where it goes wrong: the tags are not standardised as tightly as you would hope.
currency:XBT and payment:onchain mean subtly different things, one saying prices are
denominated in bitcoin and the other saying bitcoin is accepted as payment, and contributors
use them interchangeably. Directories also disagree about which tags count, so the same
café can be present in one and absent from another with nobody having made a mistake.
Detection has to be permissive, which means it pulls in noise.
2. Geographic enrichment
Raw OSM records are inconsistent. A venue might have coordinates and a street name but no city, or a city spelled three ways across three records, or an address in a local script with no transliteration.
So every record is enriched from its coordinates with a country, state, city and a formatted address, the coordinates themselves treated as the authoritative field since they are the one thing almost always present and almost always right. The enrichment documentation describes the city as the nearest one found through a spatial index, and says that for a venue mapped as an area rather than a point, the centre is calculated from the outline's nodes.
Where it goes wrong: nearest-city is not the same question as which city a place is in. It is confidently wrong near borders, in disputed territories, and anywhere administrative boundaries do not match how people describe where they live. A venue can end up filed under the wrong region while sitting at perfectly correct coordinates.
3. Categorisation
A directory is only useful if you can filter it: food, lodging, retail, services. OSM has
its own tagging vocabulary for this (amenity=restaurant, shop=clothes, and several
hundred others), so the first pass is a straight mapping from OSM tags to directory
categories, and the documentation puts the size of that dictionary at more than a hundred
subcategories.
That covers most records. The second pass is not inference: a venue submitted through MappingBitcoin's own form carries a category the submitter chose from a required dropdown, and that is what fills the gap when the OSM tags do not match anything in the dictionary.
Where it goes wrong: a self-declared category is a claim like any other, and the categories with the loosest edges swallow the difficulty rather than solving it. On 20 August 2026 the statistics page showed "Other" as the fourth largest category, 3,503 records out of 28,408. A place called "The Vault" could be a bar, a gym, or a co-working space, and a directory that has to guess should say it guessed.
4. Naming and deduplication
Every venue gets a URL slug generated from its name and location, and the documented collision rule is mechanical: if the slug already exists, append the location, and if that still collides, append a counter.
What is worth noticing is what is not here. There is no separate published moderation stage. Deduplication happens at the two ends instead: the submission form searches existing venues as you type the name, and OpenStreetMap itself is where duplicate nodes get merged by mappers.
Where it goes wrong: the same physical café frequently exists as several OSM nodes created by different contributors over several years, and neither end catches that reliably. A submitter who spells the name differently sails past the search, and the merge in OpenStreetMap only happens if somebody notices and cares. This is the stage where a directory's real quality is decided, and it is the least automatable.
The problem underneath all of it
Every stage above is about data hygiene. The harder problem is different: the map cannot tell you whether a listing is still true.
A tag added in 2021 by a well-meaning tourist looks exactly like a tag added last week by the
owner, unless somebody recorded a date. Some do: of the 90 bitcoin-tagged records in the
Buenos Aires bounding box below, queried on 20 August 2026, 71 carried
check_date:currency:XBT and 10 carried survey:date. But a date is a claim about when
somebody looked, not about who they were or whether they asked.
This is the problem MappingBitcoin is aimed at, using Nostr: an owner proves the listing is theirs by email, a DNS record or a file on their own website, the proof is bound to a Nostr public key, and the weight of it comes from the web of trust around that key rather than from a moderator's decision. It is early. On 20 August 2026 the statistics page reported 29 verified businesses against 28,408 merchants, so the mechanism exists and has barely been used. It does not make listings true either. It separates "somebody on the internet said so" from "the owner said so, and can prove the account is theirs."
Run the first stage yourself
There is no public endpoint for the finished dataset. MappingBitcoin's API reference documents endpoints for reviews and for verification, and lists venue search and retrieval as still to come, so the public data path is the source itself: OpenStreetMap, through Overpass.
- Open overpass-turbo.eu. No account, no key.
- Paste this and run it. It asks for everything in a bounding box carrying the tag:
[out:json][timeout:60];
nwr["currency:XBT"="yes"](-34.75,-58.60,-34.50,-58.30);
out center;
- The coordinates above cover Buenos Aires; replace them with your own bounding box. Use
nwrrather thannode: a business mapped as a building outline is a way, not a node, and asking only for nodes drops it silently. - Swap
out center;forout count;when you only want the number. Run on 20 August 2026, that query returned 90 records for Buenos Aires, all of them nodes. - To count a whole country instead, name it by its ISO code rather than drawing a box:
[out:json][timeout:150];
area["ISO3166-1"="AR"][admin_level=2]->.a;
nwr["currency:XBT"="yes"](area.a);
out count;
- Open the matching country page, in the form mappingbitcoin.com/bitcoin-shops-in-argentina, and read the number it states. That is the comparison the next section is about.
Verify it worked
- Count both sides. On 20 August 2026, Overpass returned 251 records for Argentina on
currency:XBT=yesalone, 245 nodes and 6 ways, and the country page said 249 places. - The numbers will not match exactly, and the gap is the point. Two counts within a few records of each other means the directory is reading essentially the same set you asked for. A gap of tens or hundreds means it is dropping records, or reading tags you did not ask about.
- Widen the query to the tags the directory actually watches and see what moves. Adding
payment:bitcoinandpayment:lightningto the Argentina query took the Overpass total from 251 to 252, so in Argentina those tags are almost never used alone. Somewhere else that difference will be large, and that is a fact about local mapping habits. - Pick one record present in Overpass and absent from the map, and read its tags. Nine
times out of ten the explanation is visible immediately: a missing
currency:XBT, a typo, or a node that duplicates one already listed. - Check the shape of what you counted. In the Buenos Aires sample above, 75 records
carried
payment:onchain, 72 carriedpayment:lightningand 7 carriedpayment:lightning_contactless. If your own counts for those are all identical to the total, you are probably reading a directory's normalised output rather than the raw tags.
If something goes wrong
Overpass returns nothing. Check the bounding box order. Overpass takes
(south,west,north,east), and a transposed pair silently describes an empty region of ocean.
Overpass times out. The query is too large or the timeout is too short. Public Overpass instances are a shared resource, so narrow a bounding box to a city, and give a country-wide area query a longer timeout rather than a bigger box.
The area query returns zero for a country you know has merchants. The ISO code or the admin level is wrong, and an area filter that matches nothing returns an empty set rather than an error. Check the code against the country's own OpenStreetMap relation.
Your count is wildly higher than the directory's. Check what you are counting. Exchange
offices and ATMs carry the same currency tag as shops, and 8 of the 90 Buenos Aires records
were tagged amenity=bureau_de_change rather than being places to buy anything. Most
directories separate them, and a raw Overpass count does not.
Why OpenStreetMap
It would be easier to run a private database. OSM is worth the mess for one reason: nobody owns it. The data cannot be revoked, repriced, or made conditional on an API agreement. Every tool built on it reads the same source, so effort spent tagging a café benefits MappingBitcoin, BTC Map and everything else reading the same records, rather than one company's product.
For a directory of businesses accepting money that nobody controls, a dataset nobody controls is the consistent choice.
Correction, 20 August 2026. Two factual claims in this post were wrong and have been replaced. It said the categorisation stage used a language model to infer a category from a venue's name and description; MappingBitcoin's published enrichment documentation describes no such step, and the second pass is a category the submitter picks on the form. It also described a moderation stage that deduplicated and dropped errors before publication; there is no separate published moderation stage, and the section now says where deduplication actually happens. The walkthrough previously ran against BTC Map's API as the finished output. That API is real and still works, but this post is about the pipeline behind MappingBitcoin, which has no public endpoint for its dataset, so the walkthrough now runs against OpenStreetMap through Overpass and checks the result against a published count.
