Metadata-Version: 2.5
Name: locio
Version: 0.1.0
Summary: Australian address autocomplete, validation and geocoding from G-NAF
Project-URL: Homepage, https://locio.com.au
Project-URL: Documentation, https://locio.com.au/docs/
Project-URL: Source, https://github.com/locio-au/locio-python
Project-URL: Changelog, https://github.com/locio-au/locio-python/blob/main/CHANGELOG.md
Author: Locio
License: MIT License
        
        Copyright (c) 2026 Locio
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: address autocomplete,address geocoding,address validation,address validation service,australia,australian address api,g-naf,geocoding,gnaf,postcode
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: GIS
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown

# locio

**Australian address validation, address autocomplete and address geocoding**
in Python, from G-NAF, the national address register.

One call gives you a stable G-NAF id, a coordinate, an ABS mesh block and the
address split into fields. Comparable services bill validate, geocode and
meshblock separately; here they are fields of one response.

No dependencies. Python 3.9+.

```sh
pip install locio
```

## Address validation

`resolve` takes an address however you hold it and tells you whether it is
real, where it is, and what it is made of.

```python
from locio import Locio

locio = Locio("lc_live_...")

result = locio.resolve("1 george st sydenham nsw 2044")

if result.matched:
    a = result.address
    print(a.formatted)               # 1 George Street, Sydenham NSW 2044
    print(a.address_detail_pid)      # store this, not the text
    print(a.lat, a.lng)              # geocoded
    print(a.mesh_block)              # ABS mesh block
    print(a.components.postcode)     # parsed
```

`matched` is `False` for an address that is not in G-NAF. That is an ordinary
answer, not an error, and it is what a validation call is asking. `Resolution`
is falsy when nothing matched, so this reads the way you want:

```python
if not locio.resolve(typed):
    ...  # ask the customer to check it
```

## Cleaning a spreadsheet

The commonest thing this library gets asked to do. `to_dict()` flattens a
record to one level so it goes straight into a `DictWriter`.

```python
import csv
from locio import Locio

locio = Locio("lc_live_...")

with open("customers.csv") as f, open("clean.csv", "w", newline="") as out:
    rows = list(csv.DictReader(f))
    writer = None

    for row in rows:
        result = locio.resolve(row["address"])
        clean = result.address.to_dict() if result.matched else {}
        record = {**row, **clean, "matched": result.matched}

        if writer is None:
            writer = csv.DictWriter(out, fieldnames=list(record))
            writer.writeheader()
        writer.writerow(record)
```

`resolve_many` does the same sequentially, which is deliberate: the quota is
per key, and firing a thousand requests at once is how a free tier is spent in
a second.

## Address autocomplete

```python
for a in locio.search("104/119 turner", limit=8):
    print(a.formatted, a.address_detail_pid)
```

For a browser autocomplete use [`@locio-au/react`](https://www.npmjs.com/package/@locio-au/react)
or [`@locio-au/vue`](https://www.npmjs.com/package/@locio-au/vue) with a **public**
key. This library takes a secret key, and a secret key must never reach a page.

## Correcting a typo

```python
if not (result := locio.resolve(typed)):
    for near in locio.similar(typed, limit=5):
        print(near.formatted)
```

Three units, because it scores rows by similarity rather than seeking an
index. Call it once on an address that failed to resolve, never per keystroke.

## Reading an id back

```python
from locio import NotFound

try:
    a = locio.get("GAVIC425624910")
except NotFound:
    ...  # G-NAF retires ids between releases; search for it again
```

## Two ids, and which to store

A record can carry two pids and they mean different things:

| Attribute | Means |
|---|---|
| `address_detail_pid` | **This address.** The one to store. |
| `gnaf.primary_pid` | The **parcel** it sits on, when this row is a unit. |

`address.is_unit` reports which you have. Storing the primary pid stores the
building rather than the door, and nothing about the value itself says so.

## Errors

```python
from locio import LocioError, NotFound, AuthError

try:
    locio.search("90 bay road")
except AuthError as err:
    print(err.status, err.title, err.detail)
```

The API writes refusals for a person to read and they are carried through,
because the detail is the part that says what to do.

## Keys and safety

Secret keys (`lc_live_...`) belong on a server. Get one at
[locio.com.au/account/api](https://locio.com.au/account/api).

The client refuses a plaintext `http://` base URL to any host but loopback: a
bearer key sent in the clear is a key given away.

## What it costs

`search`, `resolve` and `get` are one unit each, `similar` is three. See
[locio.com.au/pricing](https://locio.com.au/pricing/).

## Licence

MIT. Address data is G-NAF, published by Geoscape Australia under CC BY 4.0;
attribution belongs wherever you show it.
