> **Draft: Addrexy is not live yet.** It will run at https://addrexy.com as a tenant of the white-label developer portal. In version 1 the portal and its documentation are those of the existing white-label tenant at https://intodq2.devapicloud.com; the API and the documentation are the same. **TBD** marks something not yet decided. **Not documented** marks something the white-label documentation does not say.

# Build: call the APIs

Base URL: `https://api.devapicloud.com/v1`. All calls are `POST` with `Content-Type: application/json`, Basic authentication, and an `X-Session-Token`. See [/agent/start](https://addrexy.com/agent/start). The full contract is [/openapi.json](https://addrexy.com/openapi.json).

In this page, "**API documentation says**" means the white-label API's own documentation. "**Addrexy guidance**" is our advice for software. The API itself returns only the fields shown.

| Call | Endpoint | Operation id | Planned MCP tool |
|---|---|---|---|
| Suggest addresses as someone types | `POST /autocomplete/suggest` | `suggestAddresses` | `autocomplete_address` |
| Verify one address | `POST /address/verify` | `verifyAddress` | `verify_address` |
| Address to latitude and longitude | `POST /address/geocode` | `geocodeAddress` | `geocode_address` |
| Latitude and longitude to address | `POST /address/reverse-geocode` | `reverseGeocode` | `reverse_geocode` |

**MCP (planned, not built).** A thin pass-through MCP server is planned. It will forward your key and secret to the endpoints above, create the `X-Session-Token` for you, and return the API's JSON unchanged. It adds no fields and no state. Its address is **TBD**. Until it exists, call the REST API.

**If your caller is a browser.** The key and secret must stay on a server, so a browser cannot call the API directly. Put a small proxy on your server that holds the credentials and forwards each request. The proxy sends the `X-Session-Token` (create it when the field gets focus and pass it from the browser to the proxy) and should rate-limit and authenticate its own callers, or anyone can spend your credits.

## Autocomplete

Call it as someone types. Debounce at 200 ms, start at 3 characters, and reuse one `X-Session-Token` for the whole search session.

Request:

```json
{
  "query": { "addressInput": "20 W 34th", "countryCode": "USA" },
  "settings": { "candidateLimit": 5, "includeDetails": true, "language": "en_US" }
}
```

| Field | Required | Notes |
|---|---|---|
| `query.addressInput` | yes | What has been typed. A plain string, 3 to 200 characters. |
| `query.countryCode` | yes | ISO 3166-1 alpha-3, for example `USA`. Case-insensitive (`usa` is accepted). |
| `settings.candidateLimit` | no | 1 to 50. Default 5. |
| `settings.includeDetails` | no | `true` returns `addressComponents` and `suggestedAddressLines`. They are `null` otherwise. Use `true` when you fill a form. Default `false`. |
| `settings.language` | no | A locale code in the format the API's examples use, such as `en_US` or `fr_FR`. Defaults to the country's locale. |
| `settings.searchType` | no | `ADDRESS` (default) or `PLACES` for named places such as landmarks. |
| `settings.originCoordinates` | no | `[longitude, latitude]`. Ranks nearby results first. |
| `settings.searchRadius` | no | `{ "value": 5000, "unit": "METER" }`. Needs `originCoordinates`, or you get `INVALID_PARAMETER`. |
| `settings.searchScope` | no | Filter or bias results by `city` and `postcode`. `type` is `filter` or `bias`. `exclude` is only valid with `filter`. |

Response:

```json
{
  "requestId": "req_03kz2p8qxy",
  "result": {
    "status": "OK",
    "message": "",
    "meta": { "language": "en_US", "candidateLimit": 5, "countryCode": "USA", "enrichmentFields": { "requested": [], "granted": [] } },
    "suggestions": [
      {
        "displayText": "20 W 34th St, New York, NY 10118, United States",
        "suggestedAddressLines": ["20 W 34th St", "New York, NY 10118", "United States"],
        "postalDeliverable": true,
        "addressComponents": { "houseNumber": "20", "thoroughfare": "W 34th St", "city": "New York", "district": "New York County", "region": "New York", "neighborhood": "", "postcode": "10118", "postcodeExtension": "", "unit": "", "unitType": "", "countryCode": "USA" },
        "enrichment": null,
        "places": null
      }
    ]
  }
}
```

- The request id is the top-level `requestId`. (Verify and geocode put it in `result.meta.requestId`.) Every call also returns `X-Request-Id` as a response header.
- An empty `suggestions` array means no matches. It is not an error.
- `postalDeliverable` is `true`, `false`, or `null` (unknown for this address or country).
- `enrichment` is always `null` today and `meta.enrichmentFields` is always empty: both are documented as future scope. There is no request field for them.
- On any `200`, check `result.status`. The API documentation defines `OK` and `ERROR`; if it is `ERROR`, read `result.message`.
- **Autocomplete does not verify.** A suggestion is not a verified address. Call verify on the one the user chose.

## Verify

One address in, one answer out. Synchronous. **Addrexy guidance:** with the Option 1 data behind Addrexy, verify checks that an address *could* exist. It does not confirm that it does. Treat a high `matchScore` and `deliverable: true` as plausible, not proven. Check [country coverage](https://addrexy.com/agent/coverage) first.

Request:

```json
{
  "query": { "address": "20 W 34th St, New York NY 10118", "countryCode": "USA" },
  "settings": { "language": "en_US", "matchLevel": "STANDARD" }
}
```

| Field | Required | Notes |
|---|---|---|
| `query.address` | yes | The whole address as one string, up to 500 characters. |
| `query.countryCode` | yes | ISO 3166-1 alpha-3. Accepted in any case. |
| `query.referenceId` | no | Your own id for this record, up to 100 characters. It comes back in `result.meta.referenceId`. |
| `query.houseNumber` (20), `thoroughfare` (150), `placeName` (100), `unitType` (10), `unit` (20), `suburb` (100), `city` (100), `district` (100), `postcode` (20), `region` (100) | no | Structured parts, with their maximum lengths. When you have them, send them as well as `address`: they take precedence over the same part parsed from `address`. |
| `settings.language` | no | A locale code such as `en_US`. |
| `settings.matchLevel` | no | `EXACT` (tight match, fewest candidates, fewest false positives), `STANDARD` (default), or `RELAXED` (loose match: finds more but returns more multiple matches and is slower). |
| `settings.includeComponentDetail` | no | Default `true`. If `false`, `componentMatches` is empty and only `matchType` is returned. **Then you cannot apply the rules below that read component values (scores 2 and 4).** Set `false` only if you branch on `matchScore` alone. |
| `settings.returnParsedInput` | no | Default `false`. Returns how your input was tokenised. Use it only to diagnose a score of 0. |

Response:

```json
{
  "result": {
    "status": "OK",
    "message": "",
    "meta": { "requestId": "req_03kz2p8qxy", "referenceId": null, "language": "en_US", "countryCode": "USA" },
    "matchScore": 4,
    "deliverable": true,
    "addressType": "COMMERCIAL",
    "address": {
      "formatted": "20 W 34th St, New York, NY 10118",
      "addressLines": ["20 W 34th St", "New York, NY 10118", "United States"],
      "components": { "houseNumber": "20", "thoroughfare": "W 34th St", "placeName": "", "unitType": "", "unit": "", "city": "New York", "suburb": "", "district": "New York County", "postcode": "10118", "postcodeExt": "", "region": "New York", "adminAreaCode": "US-NY", "countryCode": "USA" },
      "nationalId": null
    },
    "matchDetails": {
      "matchType": "ADDRESS",
      "componentMatches": {
        "houseNumber": { "value": 4, "meaning": "Verified without changes" },
        "thoroughfare": { "value": 4, "meaning": "Verified without changes" },
        "city": { "value": 4, "meaning": "Verified without changes" },
        "district": { "value": 6, "meaning": "Appended using reference data" },
        "postcode": { "value": 4, "meaning": "Verified without changes" },
        "region": { "value": 3, "meaning": "Verified and corrected" },
        "countryCode": { "value": 4, "meaning": "Verified without changes" }
      }
    },
    "parsed": null
  }
}
```

This example has a `matchScore` of 4: the address matched, and `region` was corrected (`NY` became `New York`) and `district` was filled in.

| Field | Meaning |
|---|---|
| `result.matchScore` | Integer 0 to 5. **The signal to branch on.** A score of 0 is still HTTP `200`. |
| `result.deliverable` | `true`: confirmed as a postal delivery point. `false`: cannot be confirmed deliverable. |
| `result.addressType` | `RESIDENTIAL`, `COMMERCIAL`, `INDUSTRIAL`, `PO_BOX`, `UNKNOWN`, or `null` where classification data is unavailable. |
| `result.address` | The standardised address. **Use this, not your input.** It may be corrected. Components that do not apply are `""`, never `null`. |
| `result.matchDetails` | How the score was reached. See below. |

What `address`, `deliverable` and `addressType` contain at `matchScore` 0 is **not documented**. Do not rely on them at that score.

There is no confidence percentage. Do not turn `matchScore` into one.

## What to do for each `matchScore`

Use the middle column when a person is present in your application (for example a customer at a checkout). Use the right column when no person is available while your task runs.

| Score | API documentation says (a person is present) | Addrexy guidance (no person available) |
|---|---|---|
| 5 | Proceed. Use `result.address` downstream. No user interaction needed. | Proceed. Store `result.address` and the request id. |
| 4 | Proceed. Minor normalisation; silent acceptance is fine. | Proceed. Log the components with `value` 3 (corrected). |
| 3 | Review. Show the user the corrected address and ask for confirmation. | Do not treat it as verified. Queue it for human review. Do not wait for an answer. |
| 2 | Flag to the user. Show which components were not found. Allow manual override with confirmation. | Do not use it. Queue it for human review, with the components whose `value` is 0. |
| 1 | Prompt for re-entry. Do not use for delivery without manual verification. | Reject. Ask the source for a fuller address. |
| 0 | Block submission and prompt for re-entry. Never block silently. | Reject and report that no match was found. Do not retry with guessed variations. |

"Queue for human review" means: put the record, its request id and the reason in a list a person will look at. You choose where it lives. The service has no review queue.

## Reading `matchDetails`

`matchType` is the deepest level the match reached. The score is how confident the match is; the type is how deep that confidence goes. The ranges below are the API documentation's "typical" ranges, not guarantees.

| `matchType` | Meaning | Typical `matchScore` |
|---|---|---|
| `ADDRESS` | Confirmed to building or door level. Usable for delivery. | 4 to 5 |
| `STREET` | The street exists, but the delivery point could not be resolved. | 2 to 3 |
| `LOCALITY` | Confirmed only to city or suburb. The street could not be verified. | 1 to 2 |
| `POSTAL_CODE` | Confirmed only to the postcode area. | 0 to 1 |

Each entry in `componentMatches` has a `value`. There are four, and no others:

| `value` | Meaning |
|---|---|
| 0 | Not found or empty. |
| 3 | Verified and corrected: the API changed what you sent. `result.address` holds the right value. |
| 4 | Verified without changes: an exact match. |
| 6 | Appended: you did not send it; it was filled in from reference data. Reliable, but not yours. |

Which components appear in `componentMatches` is not documented beyond the example. The API does not return your original value, so compare against your input to see what changed.

## From a suggestion to a verified address

After the user picks a suggestion, call verify. Send the suggestion's `displayText` as `query.address` and `query.countryCode`, and add the structured parts from `addressComponents` where they map. The names differ between the two APIs:

| Autocomplete `addressComponents` | Verify `query` |
|---|---|
| `houseNumber`, `thoroughfare`, `city`, `district`, `region`, `postcode`, `unit`, `unitType` | the same names |
| `neighborhood` | `suburb` (Addrexy mapping, not documented) |
| `postcodeExtension` | include it in `postcode` (verify returns it as `postcodeExt`) |

Store `result.address` from verify, not the suggestion. If you also need coordinates, pass the verified `result.address.components` (plus `countryCode`) into forward geocode.

## Forward geocode

Address in, coordinates out. Synchronous. The `query` fields are the same as verify, so you can pass the verified `address.components` straight in. For best results run verify first.

Request:

```json
{
  "query": {
    "address": "20 W 34th St, New York NY 10118",
    "houseNumber": "20",
    "thoroughfare": "W 34th St",
    "city": "New York",
    "region": "NY",
    "countryCode": "USA"
  },
  "settings": { "language": "en_US", "matchLevel": "STANDARD" }
}
```

| Field | Required | Notes |
|---|---|---|
| `query.address`, `query.countryCode` | yes | As for verify. |
| `query.referenceId` and the structured parts (`houseNumber`, `thoroughfare`, `placeName`, `unitType`, `unit`, `suburb`, `city`, `district`, `postcode`, `region`) | no | As for verify. Structured parts take precedence over the same part parsed from `address`. |
| `settings.matchLevel` | no | `EXACT`, `STANDARD` (default) or `RELAXED` (more candidates; useful for ambiguous input). |
| `settings.searchType` | no | How deep to resolve. `ADDRESS` (default): a delivery point. `POSTAL`: a postcode centroid. `GEOGRAPHIC`: a city or region centroid. |
| `settings.maxCandidates` | no | 1 to 10. Default 1. |
| `settings.geohashPrecision` | no | 1 to 12. Default 9. Length of `coordinates.geohash`. |
| `settings.h3Resolution` | no | 0 to 15. Default 9. Resolution of `coordinates.h3Index`. |
| `settings.language` | no | A locale code such as `en_US`. |

Response shape (the documentation gives **no sample response**, so none is shown here): `result.status`, `result.meta` (`requestId`, `referenceId`, `language`, `countryCode`) and `result.candidates`, an array ordered by `matchScore`, highest first. An empty array means no match. It is HTTP `200`, not an error. Each candidate has:

| Field | Meaning |
|---|---|
| `matchScore` | 0 to 5, the same scale as verify. |
| `matchPrecision` | 0 to 100. A finer score for your own accept or reject threshold. |
| `address` | The standardised address (`formatted`, `addressLines`, `components`), the same structure as verify. Use this, not your input. |
| `coordinates.latitude`, `coordinates.longitude` | Decimal degrees, WGS 84 (EPSG:4326). No other coordinate system is offered. |
| `coordinates.geohash`, `coordinates.h3Index` | The same point as a geohash and an H3 cell. |
| `coordinates.locationType` | How precise the point is. See below. |
| `coordinates.locationDescription` | Where the point sits: `POSTAL_BUILDING`, `BUILDING`, `FRONT_DOOR`, `DRIVEWAY`, `PARCEL`, `POINT_OF_INTEREST`, `STREET_BLOCK` or `POSTAL_CODE`. |
| `matchDetails` | `matchType` (`ADDRESS`, `STREET`, `LOCALITY`, `POSTAL_CODE`) and `componentMatches`, as for verify. |

`locationType` values: `ADDRESS_POINT`, `INTERSECTION`, `ADDRESS_INTERPOLATED`, `NEAR_BY_ADDRESS`, `STREET`, `POSTAL`, `POSTAL_CENTROID`, `TOWN_CENTROID`, `CITY`, `COUNTRY`, `NO_LOCATION`, `POI`. The first is the most precise. `POSTAL_CENTROID`, `TOWN_CENTROID`, `CITY` and `COUNTRY` are the middle of an area, not an address.

**Read three things together:** `matchScore`, `matchDetails.matchType` and `coordinates.locationType`. The documentation warns that a high score at `POSTAL_CODE` depth is not building-level accuracy.

### What to do for each geocode `matchScore`

The middle column is the API documentation's guidance. The right column is Addrexy guidance for software with no person available.

| Score | `matchPrecision` | API documentation says | Addrexy guidance (no person available) |
|---|---|---|---|
| 5 | 85 to 100 | Reliable for navigation and last-mile delivery. | Use the coordinates. Still check `locationType` is a point, not a centroid. |
| 4 | 65 to 84 | Suitable for most operational use. | Use the coordinates for operations. Check `locationType`. |
| 3 | 45 to 64 | Usable for general mapping; review before critical workflows. | Use for display only. Do not route or dispatch on it. Queue for review. |
| 2 | 25 to 44 | Street or locality level. Do not use for delivery without manual verification. | Do not use. Queue for human review. |
| 1 | 1 to 24 | A postcode or region centroid. Flag for manual review. | Do not use as a location. Report it. |
| 0 | 0 | No match. Discard the candidate. Do not use the coordinates. | Discard. Do not retry with guessed variations. |

## Reverse geocode

Coordinates in, address out. Synchronous.

Request:

```json
{
  "query": { "latitude": 40.7484, "longitude": -73.9857, "countryCode": "USA" },
  "settings": { "searchRadius": { "value": 150, "unit": "METER" }, "maxCandidates": 1, "language": "en_US" }
}
```

| Field | Required | Notes |
|---|---|---|
| `query.latitude` | yes | Decimal degrees, WGS 84, from -90 to 90. |
| `query.longitude` | yes | Decimal degrees, WGS 84, from -180 to 180. |
| `query.countryCode` | no | ISO 3166-1 alpha-3. Recommended: it constrains the search and helps near borders. If omitted, the country is resolved from the coordinates. |
| `query.referenceId` | no | Your own id, up to 100 characters. It comes back in `result.meta.referenceId`. |
| `settings.searchRadius` | no | `{ "value": 150, "unit": "METER" }` is the default. Units: `METER`, `KILOMETER`, `FOOT`, `MILE`. Needs both `value` and `unit`. |
| `settings.maxCandidates` | no | 1 to 5. Default 1. |
| `settings.language` | no | A locale code such as `en_US`. |

The response uses the same envelope as forward geocode: `result.status`, `result.meta` and `result.candidates` (the documentation gives **no sample response**). Each candidate has `address` (the same structure as above) and `coordinates`, which has everything forward geocode returns plus:

| Field | Meaning |
|---|---|
| `coordinates.designation` | Where the matched point sits relative to the address footprint: `ROOFTOP`, `CENTROID`, `RANGE_INTERPOLATED`, `GEOMETRIC_CENTER` or `APPROXIMATE`. `ROOFTOP` is the most precise. |
| `coordinates.distanceMeters` | Metres between your point and the matched address point. |

`coordinates.latitude` and `longitude` are the **matched address point**, which may differ slightly from the point you sent. Decide your own tolerance for `distanceMeters` and reject candidates beyond it. The documentation does not say whether reverse geocode returns `matchScore`, `matchPrecision` or `matchDetails`, so do not rely on them.

## Where the API documentation contradicts itself

These are the places an agent could be misled. Follow the right-hand column.

| The documentation says | What to do |
|---|---|
| "`requestId` is in the response header, not the body." | Its own examples show `requestId` in the autocomplete body and `result.meta.requestId` in the verify body. Read the `X-Request-Id` header, which is always sent; use the body value as a fallback. |
| The verify field table lists `result.candidates[].matchPrecision`, and a `places` object. | Neither appears in the verify example. They look copied from other APIs. Do not expect them from verify. |
| The error envelope says "See Part 5 for the full catalogue". | The catalogue is in the Error Reference (Part 7). See [/agent/operate](https://addrexy.com/agent/operate). |
| The autocomplete path was `/address/suggest` in the June 2026 documentation. | The August 2026 documentation says `/autocomplete/suggest`. Use that. If it returns `404`, try the old path and report it. |

## Rules while building

- Keep the key and secret on the server.
- Do not retry a `matchScore` of 0 with guessed variations. Ask for a better input.
- Never treat a centroid (`POSTAL_CENTROID`, `TOWN_CENTROID`, `CITY`, `COUNTRY`, or `designation` `CENTROID` or `APPROXIMATE`) as an address.
- Log the request id of every call.
- Leave `returnParsedInput` off except when diagnosing a score of 0.
- No test keys, test addresses or sandbox are documented. Do not assume one exists.

## Next

Errors, retries, limits: [/agent/operate](https://addrexy.com/agent/operate).