Metadata-Version: 2.1
Name: osrmroute
Version: 1.1.0
Summary: Official Python SDK for the OSRMRoute Maps, Routing & Geocoding API
Author: MAMMADOFF LLC
License: MIT
Project-URL: Homepage, https://osrmroute.com
Project-URL: Documentation, https://osrmroute.com/api-reference
Project-URL: Repository, https://github.com/Mammadoff/OSRM
Keywords: maps,routing,geocoding,osrm,isochrone,matrix,directions,vrp,route-optimization,openstreetmap,api
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Typing :: Typed
Requires-Python: >=3.8
Description-Content-Type: text/markdown

# osrmroute

Official Python SDK for the [OSRMRoute](https://osrmroute.com) Maps, Routing & Geocoding API.

- **Zero dependencies** — standard library only, so it never conflicts with your existing HTTP stack
- **Type-hinted** — full annotations for every method
- **Routing and geospatial APIs** — routing, matrix, isochrones, geocoding, VRP, elevation, geofencing…
- **`(lat, lon)` for point helpers** — these helpers convert to OSRM `lon,lat`; raw VRP arrays are the exception described below
- **Automatic retries** with exponential backoff, honouring `Retry-After`

```bash
python3 -m pip install ./osrmroute-python-1.1.0
```

Download and extract the source archive from [OSRMRoute](https://osrmroute.com/downloads/osrmroute-python-1.1.0.zip), then run the command above from its parent directory. This package is not published on PyPI.

Requires Python 3.8+.

## Quick start

```python
from osrmroute import OSRMRoute

maps = OSRMRoute(api_key="your-key", auth_mode="bearer", retries=0)

r = maps.route([(40.4093, 49.8671), (40.3777, 49.8920)])
route = r["routes"][0]
print(f"{route['distance'] / 1000:.1f} km, {round(route['duration'] / 60)} min")
```

Get a free key at [osrmroute.com](https://osrmroute.com) — no card required.

## Retry policy

The source client retains its existing default of two automatic retries. Set
`retries=0` for paid calls, as shown above: a network timeout can leave the
execution outcome unknown. Use `X-Request-Id` to investigate and use the async
job API with a durable `Idempotency-Key` for deduplicated submissions.

## Configuration

```python
maps = OSRMRoute(
    api_key="your-key",                 # required
    base_url="https://osrmroute.com",   # self-hosted / staging
    timeout=30.0,                        # seconds per request
    retries=0,                           # do not replay a paid request with an unknown outcome
    auth_mode="query",                  # or "bearer"
    headers={},                          # extra headers
)
```

## Routing

```python
maps.route([a, b, c], profile="car", geometries="geojson", steps=True)
maps.matrix([a, b, c])                        # {"durations": ..., "distances": ...}
maps.directions([a, b], alternatives=True, lang="az")
maps.trip([a, b, c])                          # travelling-salesman order
maps.match(gps_trace)                         # snap a noisy trace to roads
maps.nearest(a, number=3)
maps.snap([a, b])
maps.isochrone(a, time_limit=900)             # 15-min reachability polygon
```

Profiles: `car` (global), `bike`, `foot`. Aliases `driving` / `cycling` / `walking` also work.

## Geocoding

```python
maps.geocode("Nizami küçəsi, Bakı")           # -> {"hits": [...]}
maps.reverse((40.4093, 49.8671))              # coordinates -> address
maps.autocomplete("Berl", limit=5)            # -> {"suggestions": [...]}
maps.geocode_batch(["Baku", "Tbilisi", "Yerevan"])
maps.places((40.4093, 49.8671), radius=1000, category="restaurant")
```

Bias results toward a location with `near=(lat, lon)`, restrict with `bbox` or `city`.

## Fleet optimisation

```python
plan = maps.optimize(
    vehicles=[{"id": 1, "start": [49.8671, 40.4093], "end": [49.8671, 40.4093]}],
    services=[{"id": 1, "location": [49.8920, 40.3777]}],
)

maps.cluster(customers)   # group nearby stops into delivery zones
```

> Note: VRP `vehicles` / `services` take raw `[lon, lat]` (upstream VROOM format).

## Geo utilities

```python
maps.timezone((40.4093, 49.8671))
maps.elevation([(40.4093, 49.8671), (40.3777, 49.8920)])
maps.elevation_profile(points=route_points)
maps.boundary((40.4093, 49.8671), polygon=True)
maps.geofence(fences, points)
maps.solar((40.4093, 49.8671), date="2026-07-18")
maps.geometry("distance", from_=[40.4093, 49.8671], to=[40.3777, 49.8920])
maps.convert(point=(40.4093, 49.8671))
maps.country("AZ")
```

## Error handling

Every non-2xx response raises `OSRMRouteError`:

```python
from osrmroute import OSRMRoute, OSRMRouteError

try:
    maps.route([a, b])
except OSRMRouteError as err:
    if err.is_rate_limited:      # 429 — plan limit reached
        ...
    if err.is_out_of_coverage:   # 422 — outside routable coverage
        ...
    print(err.status, err.code, err.message)
```

GET requests and job creation with an explicit stable idempotency key retry transient failures. Other POSTs and cancellation are sent once; a lost response may have an unknown outcome. Never create a fresh job key merely because a response was lost. Retries are limited to 0–5.

## Coverage

Availability depends on coordinates, profile and the current dataset. Missing coverage remains explicit; no driving fallback is used for unsupported profiles. Reverse results distinguish detailed addresses from nearby place fallback. Isochrone geometry may be absent when sampling is insufficient. See [service status](https://osrmroute.com/status) and source metadata; neither implies every road or address is covered.

## Version 1.1.0 platform methods

Unknown routing profiles now fail before transport instead of becoming driving.
VRP accepts `shipments` and a top-level profile; raw VRP location arrays remain
`[lon, lat]`, unlike point helpers. Constraints are verified; global optimality
is not promised. Check `unassigned` and result quality.

```python
queued = maps.create_job('matrix', {
    'from_points': [[40.4093, 49.8671]], 'to_points': [[40.3777, 49.8920]], 'profile': 'car',
}, idempotency_key='delivery-batch-20261015-001')
maps.list_jobs()
status = maps.get_job(queued['job']['id'], result=True)
maps.cancel_job(queued['job']['id'])  # only a queued job can be cancelled
sources = maps.source_metadata()  # requires info scope
fixture = sandbox_maps.sandbox('geocode', {'q': 'Baku'})  # separate sandbox key
```

Job states include `unknown` and `expired`; these methods never automatically
resubmit an unknown operation. Result retrieval is explicit. Sandbox results are
deterministic fixtures with zero billing, never navigation data. Preserve your
idempotency key across process restarts for the same operation/payload.

## License

MIT © MAMMADOFF LLC
