osmnx is how a large share of the research and analysis world touches OpenStreetMap: one line to pull a street network or every cafe in a city, straight into GeoDataFrames. Under the hood, nearly every one of those lines is an Overpass query, and by default they all go to the main public Overpass server.
That default is fine for a notebook session. It stops being fine when the work grows: long batch jobs hit the public server's per-IP rate limits, jobs on cloud machines often cannot reach the public instances at all because datacentre IP ranges are restricted, and a research pipeline that should be reproducible next year is at the mercy of a shared server's load. All three problems have the same first step: point osmnx somewhere else, whether that is an instance you run yourself or a keyed commercial one.
The setting
osmnx reads its Overpass server from ox.settings.overpass_url. The
default is https://overpass-api.de/api, and osmnx appends
/interpreter itself, so what you supply is the base, not
the full interpreter URL:
import osmnx as ox
# your own instance:
ox.settings.overpass_url = "http://localhost:12345/api"
# or a keyed instance whose key lives in the URL path:
ox.settings.overpass_url = "https://example.com/YOUR_KEY/api"
Give it a URL ending in /api/interpreter and osmnx will request
/api/interpreter/interpreter, which fails on every server. On the
older osmnx 1.x line the same setting is named
ox.settings.overpass_endpoint and takes the same value.
Two other settings we need to look at
Changing the URL may not be the whole job, because two other settings might affect your queries.
requests_timeout writes itself into your query
ox.settings.requests_timeout (default 180) is not just an HTTP
timeout. osmnx stamps it into every query it generates as the Overpass
[timeout:180] setting. The public servers accept that happily.
Keyed instances usually cap [timeout:] per plan, and a server that
rejects over-cap values rather than silently clamping them will refuse the
query. What that looks like from osmnx is unhelpfully indirect: the server's
JSON error body has no elements array, so the run dies with
KeyError: 'elements' and no mention of the real cause.
The fix is one line: set requests_timeout to your plan's timeout
cap or below.
The rate limiter polls a status endpoint
With ox.settings.overpass_rate_limit left at its default of
True, osmnx asks the server how long to pause before each query by
calling <base>/status, the public servers' per-IP slot
endpoint (covered here). A keyed
endpoint identifies you by key, not IP, and typically has no
/status. In our capture osmnx tolerated the failed probe and
carried on, but it is a wasted request before every real one, pure noise in the
provider's logs. On your own or a keyed instance, turn it off.
A worked example
The complete, verified setup is three settings and then business as usual:
import osmnx as ox
ox.settings.overpass_url = "https://example.com/YOUR_KEY/api"
ox.settings.requests_timeout = 60 # at or below your plan's [timeout:] cap
ox.settings.overpass_rate_limit = False # the key is the identity; no /status probe
cafes = ox.features_from_bbox(
(13.38, 52.51, 13.42, 52.53), # left, bottom, right, top
{"amenity": "cafe"},
)
print(len(cafes))
We ran exactly this query twice: once against the public default and once against our own keyed endpoint. Both returned the same 223 features for central Berlin, which is the point: a well-run mirror serves the same planet, so the only thing that changes is whose capacity you are using.
Worth knowing before you trust either run: it is more traffic than it looks.
For that one call, osmnx generated a union query with a recurse-down step
((._;>;)) across nodes, ways, and relations, because it fetches
full geometry to build the GeoDataFrame. Hand-written Overpass queries can be
much lighter; osmnx queries are heavier than you would write yourself, which is
exactly why batch osmnx work outgrows shared servers early.
To confirm which server answered, do not guess from the output. Misconfigure the URL on purpose once (it should fail loudly), or watch your provider's usage counter move as you run. Silent fallback does not exist in osmnx, so a successful run with your URL set means your server answered, but the thirty-second check buys certainty.
Your endpoint options, honestly
The public default is right for interactive, occasional work. That is what it is for, and nothing about a notebook session exploring one city needs to leave it. Mind the etiquette from the rate-limits post: back off when asked, and do not rotate mirrors to dodge limits.
Running your own instance gives you full control and no usage caps beyond your hardware. It is real work: a full-planet database is hundreds of gigabytes, takes days to clone, and needs its update loop watched. For a team with ops capacity and steady heavy usage, it is a fine answer.
A keyed commercial instance is the middle path: production traffic off the volunteer servers without running a database yourself. We run one, Overspan, and the quickstart carries this exact osmnx setup with the real values. For the neutral list of every public and commercial instance, the OSM wiki's instances table is the reference.
Every claim about osmnx behaviour in this post was verified against osmnx 2.1.1 on 2026-08-27 by capturing its real requests. If a newer osmnx changes any of it, mail hello@overspan.dev and we will fix the post.