Python SDK for automapa.pl map services API (geocoding, routing, roads)
Project description
Automapa Map Services Python SDK
European Geocoding, Routing & Address Autocomplete
Python SDK for the Automapa Map Services REST API. Simplifies integration with geocoding, routing, and address autocomplete services for Europe.
Getting started with your .env file
You will receive a .env file from us containing your API credentials. Place it in the root directory of this repository:
AUTOMAPA_API_KEY=your-key
AUTOMAPA_API_PASSWORD=your-password
Once the .env file is in place, you can test all endpoints against the live API using Docker — no local Python installation required:
# Run the full test suite (unit + integration) inside Docker
make docker-test
# Run only integration tests (live API calls)
make docker-test-integration
# Run only unit tests (no API calls)
make docker-test-unit
# Run all checks (unit, integration, lint, type checks)
make docker-check
Zero dependencies. The SDK has no runtime dependencies — it uses only Python's built-in
urllib. It is compatible with Python 3.11 and above.
1. Overview
The SDK covers:
- Geocoding — convert an address to coordinates and back
- Routing — calculate routes, distance matrices, and point-order optimization
- Address autocomplete — suggestions as the user types
- Google Geocoding API compatibility — optional
googleresponse format
For the full list of endpoints and parameters, see the Automapa REST API documentation.
2. Installation
pip install automapa-map-services
Or with uv:
uv add automapa-map-services
Requirements: Python 3.11+, no external runtime dependencies.
3. Client configuration
from automapa_map_services import ApiClient, Config
client = ApiClient(Config(
key="YOUR_API_KEY",
password="YOUR_PASSWORD",
))
4. Session handling
A session is opened automatically (lazy) on the first call that requires authorisation. To open it manually:
client.session().generate_session(key="YOUR_API_KEY", pass_="YOUR_PASSWORD")
5. Available services
| Class | Methods |
|---|---|
Geocoder |
geocode(), geocodemulti(), revgeocode(), revgeocodemulti() |
Autocomplete |
search(), search_address() |
Road |
get_segment_info(), speed_check(), speed_check_multi() |
RoadPermit |
add(), overwrite(), remove(), list() |
Routes |
matrix(), optimize(), optimize_queue(), optimize_queue_result(), route() |
Speed |
speed_report(), speed_report_result() |
All methods also accept camelCase aliases (searchAddress(), getSegmentInfo(), etc.) for consistency with the PHP SDK.
Geocoder
# Address → coordinates
response = client.geocoder().geocode({
"address": {"city": "Warsaw", "street": "Domaniewska", "house": "37"},
"maxResults": 1,
})
results = response.data() # list of results, each with x, y, city, street, ...
# Reverse geocoding — coordinates → address
response = client.geocoder().revgeocode({
"point": [21.0073642, 52.18288],
"params": {},
})
address = response.data() # city, street, house, pcode, ...
Autocomplete
Address and POI autocomplete — returns suggestions as the user types.
search() — search by phrase
response = client.autocomplete().search({
"query": "Warsaw Domaniewska 37",
# "types": ["place"], # optional: "place", "poi", or both
})
items = response.data()["items"]
for item in items:
print(item["name"]) # "Domaniewska 37, 02-672 Warsaw"
print(item["type"]) # "place"
print(item["subtype"]) # "building"
print(item["coords"]["x"]) # longitude (lng)
print(item["coords"]["y"]) # latitude (lat)
Each items entry contains: type, subtype, name, province, district, community,
place, quarter, street, number, postal_code, coords.{x,y}.
search_address() — step-by-step address search
Useful for multi-field address forms (city → street → number → postcode).
response = client.autocomplete().search_address({
"query": {
"city": "Warsaw",
"street": "Dom", # partial street name
"number": "",
"zipcode": "02-672",
},
"source": "street", # field being searched: "place", "street", "number", "zipcode"
"selected": ["city", "zipcode"], # fields already confirmed by the user
# "limit": 10, # optional: max results (default 10)
})
items = response.data()["items"]
# [{"city": "Warsaw", "street": "Domaniewska", "zipcode": "02-672", "count": 1}]
Each entry may contain: city, street, number, zipcode, count, and optionally coords.{x,y}.
Note on Autocomplete coordinates: Unlike
Geocoder, coordinates are nested:coords.x= longitude,coords.y= latitude.
Routes
Route calculation, distance matrix, and point-order optimisation.
route() — calculate a route
response = client.routes().route({
"points": [
{"x": 21.0073642, "y": 52.2297}, # Warsaw (x=lng, y=lat)
{"x": 18.6282, "y": 54.3520}, # Gdańsk
],
"route": {
"type": "short", # "quick" | "short" | "optimal"
"traffic": True, # True = take live traffic into account
},
"object": {"type": "car"},
})
data = response.data()
print(round(data["length"] / 1000, 1), "km")
import time; print(time.strftime("%H:%M:%S", time.gmtime(data["eta"]))) # estimated travel time
print(data["mapurl"]) # link to map
# data["desc"] — turn-by-turn manoeuvre list
# data["tolls"] — toll charges
matrix() — distance / time matrix
response = client.routes().matrix({
"points": [
{"x": 21.0073642, "y": 52.2297},
{"x": 18.6282, "y": 54.3520},
{"x": 16.9252, "y": 52.4064},
],
"type": 1, # 1 = time (duration in minutes), 2 = distance (length in metres)
})
for row in response.data():
print(f"Segment {row['points']}: {round(row['length'] / 1000)} km, {row['duration']} min")
optimize() — synchronous optimisation (max 10 points)
response = client.routes().optimize({
"points": [{"x": 21.0, "y": 52.2}, {"x": 18.6, "y": 54.4}],
"type": 1, # 1 = time, 2 = distance
"fixedEnd": False, # True = last point is the destination; False = return to start
"object": {"type": "car"},
})
data = response.data()
data["order"] # optimal index sequence, e.g. "0,3,2,1"
data["sections"] # segment details: x, y, eta, dist
optimize_queue() + optimize_queue_result() — asynchronous optimisation (max 50 points)
import time
# Step 1: submit — each point requires an "id" field
response = client.routes().optimize_queue({
"points": [{"id": "Warsaw", "x": 21.0, "y": 52.2}],
"optimizeBy": "time", # "time" or "distance"
"object": {"type": "car"},
})
request_id = response.data()["requestId"]
# Step 2: poll for the result
while True:
time.sleep(2)
result = client.routes().optimize_queue_result({"requestId": request_id})
data = result.data()
if data["resultReady"] is True:
break
data["optimizedSequence"] # list of point IDs in the optimal order
6. Examples
# Copy configuration
cp .env.example .env
# Fill in AUTOMAPA_API_KEY and AUTOMAPA_API_PASSWORD in .env
# Routes
python examples/routes_route.py # point-to-point route
python examples/routes_matrix.py # distance/time matrix
python examples/routes_optimize.py # sync optimisation (max 10 points)
python examples/routes_optimize_async.py # async optimisation (max 50 points)
# Geocoder
python examples/geocoder_geocode.py # single address → coordinates
python examples/geocoder_geocodemulti.py # batch address geocoding
python examples/geocoder_revgeocode.py # coordinates → single address
python examples/geocoder_revgeocodemulti.py # coordinates → multiple addresses
# Autocomplete
python examples/autocomplete_search.py # search places and POI by phrase
python examples/autocomplete_search_address.py # search address objects
# Road
python examples/road_segment_info.py # road segment metadata
python examples/road_speed_check.py # speed limit for a single point
python examples/road_speed_check_multi.py # speed limits for multiple points
# Road permits
python examples/road_permit_list.py # list permits
python examples/road_permit_add.py # create a new permit
python examples/road_permit_overwrite.py <id> # replace an existing permit
python examples/road_permit_remove.py <id> # delete a permit
# Speed reports
python examples/speed_report.py # submit async speed report
python examples/speed_report_result.py <id> # poll for report results
Or simply run make examples to run all examples.
7. Passing additional parameters
The SDK does not restrict unknown parameters — all fields are forwarded to the API as-is:
client.geocoder().geocode({
"address": {"city": "Warsaw"},
"myCustomParam": "value", # passed through without modification
})
8. Low-level call() and request()
# Via service + method name (preferred)
client.call("Geocoder", "newMethod", {"param": "value"})
# Or via full URL path (fallback)
client.request("POST", "/v3/Geocoder/newMethod", {"param": "value"})
9. Response formats: native vs google
# Native format (default)
response = client.geocoder().geocode({"address": {"city": "Kraków"}})
data = response.data() # API data
raw = response.raw() # full raw response (always available)
# Google format
response = client.geocoder().geocode({
"address": {"city": "Kraków"},
"format": "google",
})
10. Google format field limitations
| Google field | Value |
|---|---|
geometry.viewport |
always null (not provided by the API) |
place_id |
always null (not provided by the API) |
Note: The Automapa API uses
x= longitude andy= latitude — the opposite of Google's convention.
11. Error handling
| Situation | Exception class |
|---|---|
| Invalid API key / password | AuthenticationException |
| Expired session | SessionException |
| Parameter validation failure | ValidationException |
| Resource not found | NotFoundException |
| Rate limit exceeded | RateLimitException |
| Network / urllib error | NetworkException |
| Server error (5xx) | ServerException |
| Unexpected response | UnknownResponseException |
from automapa_map_services.exceptions import AuthenticationException, AutomapaException
try:
response = client.geocoder().geocode({"address": {"city": "Warsaw"}})
except AuthenticationException as e:
print("Authentication error:", e)
except AutomapaException as e:
print("API error:", e)
License
MIT © 2026 Automapa sp. z o.o.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file automapa_map_services-0.1.0.tar.gz.
File metadata
- Download URL: automapa_map_services-0.1.0.tar.gz
- Upload date:
- Size: 22.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ac382fd7dab497fb2ec0a3862842bb40e0d7c39aec191c0b87bd45cb5690bdd4
|
|
| MD5 |
f12e427205eefcd2934f182aa4f8fe0a
|
|
| BLAKE2b-256 |
c8c7eef8439bbf43d8f3ff1f1fe98a666423b4542df8a7c2ceab92ec5ec2d501
|
File details
Details for the file automapa_map_services-0.1.0-py3-none-any.whl.
File metadata
- Download URL: automapa_map_services-0.1.0-py3-none-any.whl
- Upload date:
- Size: 27.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.8 {"installer":{"name":"uv","version":"0.11.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Pop!_OS","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7df1f373ead71f8e2e064f125bb2457d9f1475d011e26e92d2e6af7d1c239d6d
|
|
| MD5 |
32463afd7f784e1e083cdb88a716426e
|
|
| BLAKE2b-256 |
e93b39bf26fb88e792da9e859eef8d62dced454458837a6107435c8644f30e31
|