PlotFinder API

Plot & coordinate lookup for Botswana and South Africa

Search by GPS coordinates to find nearby registered plots, or search by plot number to get coordinates. Built for logistics, delivery, real estate and location-based businesses operating in these countries.

Overview

The PlotFinder API answers two questions about land in Botswana and South Africa: what plots are near this point? and where is this plot number? It is served from our own database of registered parcels, so responses are fast and do not depend on a third party being online at request time. See Field differences by country for how the two are told apart in responses.

Use case How the API helps
Logistics & delivery Turn a customer's plot number into coordinates a driver can navigate to, or confirm which plot a driver is currently parked on.
Real estate Place listings accurately on a map and find comparable plots within a radius.
Field services & utilities Identify the plot at a technician's current GPS position.
Insurance & finance Verify that a stated plot number resolves to a real, located property.

Base URL

All endpoints are versioned under /api/v1.

Base URL
https://api.starlightsoftware.co.bw/api/v1

Responses are always JSON. A successful lookup that simply finds nothing is still 200 with an empty plots array — finding no plots is a valid answer, not an error.

Getting Access

API access is issued under a commercial agreement. There is no public signup — every key is created by hand once an agreement is in place, so that usage limits and terms match what was actually agreed.

To request access, email info@starlightsoftware.co.bw with your business name and intended use case. Telling us your expected request volume up front lets us set an appropriate rate limit from day one.

Request API Access

Your key is shown once. When we issue it, the key is displayed a single time and is not recoverable afterwards — we store only a cryptographic hash of it. Save it straight into your secret manager. If it is ever lost or exposed, contact us and we will revoke it and issue a replacement.

Authentication

Every request to /api/v1/plots/* must carry your key in an X-API-KEY header. There is no OAuth flow and no token exchange step — the key alone authenticates the request.

cURL
curl -H "X-API-KEY: YOUR_API_KEY_HERE" \
  "https://api.starlightsoftware.co.bw/api/v1/plots/reverse?latitude=-24.66252002&longitude=25.90249236"
JavaScript (fetch)
const res = await fetch(
  "https://api.starlightsoftware.co.bw/api/v1/plots/reverse?latitude=-24.66252002&longitude=25.90249236",
  { headers: { "X-API-KEY": process.env.PLOTFINDER_API_KEY } }
);
const data = await res.json();

Keep your key server-side. Never ship it in a mobile app, a browser bundle, or a public repository — anything shipped to a user's device can be extracted from it. Call the PlotFinder API from your own backend and let your app talk to that.

Countries

The same endpoints serve every country we cover. They share a response shape; almost nothing else about them is the same. Read whichever one you are buying — and if you are buying more than one, read each, because code that assumes one will misread another without erroring.

Botswana — plots

26 towns and cities, every plot carrying its district. The identifier is a plot number.

Plot numbers are not unique. The same number recurs across towns, and sometimes twice within one town at locations kilometres apart. Every lookup therefore returns an array, and what you can rely on is plot_number plus city plus the coordinates — never the number alone.

Botswana records carry a district and no province. This is the dataset the coverage list at /api/v1/cities returns by default.

South Africa — erven

Erven by township, across all nine provinces. The identifier is an erf number, and it travels in the same plot_number field — parcel_identifier_type tells you which you are holding.

An erf number on its own identifies nothing. Erf numbers restart at 1 in every township, so erf 1 exists thousands of times over. Always qualify a South African lookup with the township (city) and check the province on the way back — township names themselves repeat across provinces.

South African records carry a province and no district. Erf portions are addressed as erf/portion: erf 123 portion 4 is 123/4.

Coverage is thousands of townships rather than 26 towns, which is why /api/v1/cities does not include them unless you ask: use ?country=SOUTH_AFRICA, or ?country=ALL for everything your key covers.

What your key covers

Keys are issued per country, and optionally per town. Omitting country searches everything your key covers — so a Botswana key that omits it gets Botswana, not both. Asking for a country or a city outside your key returns 403 rather than an empty result, so a missing entitlement never looks like missing data. A key may also carry a term, after which it answers 401 ApiKeyExpired naming the date.

The field-by-field comparison is under Field differences by country.

Endpoints

Three read-only endpoints. All return the same response envelope: a plots array and a message.

GET /api/v1/plots/search-given-coordinates

Returns registered plots within a radius of a coordinate, nearest first. Each result includes distance_meters from the point you supplied.

Parameters

Name Type Description
latitude float Required Between −90 and 90.
longitude float Required Between −180 and 180.
radius integer Optional Search radius in whole metres, 1–50000. Defaults to 500.
limit integer Optional Maximum plots to return, 1–500. Defaults to 100.
country string Optional BOTSWANA or SOUTH_AFRICA, case-insensitive. Omitted, the search covers both.

Botswana

Request
curl -H "X-API-KEY: YOUR_API_KEY_HERE" \
  "https://api.starlightsoftware.co.bw/api/v1/plots/search-given-coordinates?latitude=-24.66252002&longitude=25.90249236&radius=200&country=BOTSWANA"
200 Response
{
  "plots": [
    {
      "id": 1,
      "plot_number": "2",
      "latitude": -24.66252,
      "longitude": 25.9024924,
      "city": "GABORONE",
      "country": "BOTSWANA",
      "parcel_identifier_type": "PLOT_NUMBER",
      "district": "SOUTH-EAST DISTRICT",
      "distance_meters": 0
    },
    {
      "id": 12346,
      "plot_number": "13962",
      "latitude": -24.6617906,
      "longitude": 25.9015311,
      "city": "GABORONE",
      "country": "BOTSWANA",
      "parcel_identifier_type": "PLOT_NUMBER",
      "district": "SOUTH-EAST DISTRICT",
      "distance_meters": 126.5
    }
  ],
  "message": "Plots fetched successfully"
}
213962200 m
Registered plotYour coordinate200 m radius2 plots within 200 m of your coordinate, nearest first

South Africa

Request
curl -H "X-API-KEY: YOUR_API_KEY_HERE" \
  "https://api.starlightsoftware.co.bw/api/v1/plots/search-given-coordinates?latitude=-28.732384&longitude=24.7621112&radius=200&country=SOUTH_AFRICA&limit=3"
200 Response
{
  "plots": [
    {
      "id": 595404,
      "plot_number": "11862",
      "latitude": -28.732384,
      "longitude": 24.7621112,
      "city": "KIMBERLEY",
      "country": "SOUTH_AFRICA",
      "parcel_identifier_type": "ERF_NUMBER",
      "province": "NORTHERN CAPE",
      "distance_meters": 0
    },
    {
      "id": 630355,
      "plot_number": "7361",
      "latitude": -28.7320475,
      "longitude": 24.7613427,
      "city": "KIMBERLEY",
      "country": "SOUTH_AFRICA",
      "parcel_identifier_type": "ERF_NUMBER",
      "province": "NORTHERN CAPE",
      "distance_meters": 83.8
    },
    {
      "id": 630076,
      "plot_number": "6937",
      "latitude": -28.7321936,
      "longitude": 24.7629819,
      "city": "KIMBERLEY",
      "country": "SOUTH_AFRICA",
      "parcel_identifier_type": "ERF_NUMBER",
      "province": "NORTHERN CAPE",
      "distance_meters": 87.6
    }
  ],
  "message": "Plots fetched successfully"
}
1186273616937200 m
Registered plotYour coordinate200 m radius3 plots within 200 m of your coordinate, nearest first

Townships are dense: a 200 m radius in a South African suburb can cover dozens of erven, so limit matters more here than it does in Botswana.

GET /api/v1/plots/{plot_number}

Returns the coordinates of a plot by its number. No distance_meters is included, because no reference point was supplied.

This always returns an array. A plot number is not unique in Botswana: the same number occurs in several towns, and can even occur more than once within one town at different locations. South African erf numbers repeat far more — they restart at 1 in every township. Pass city and/or country to narrow the result, and design your integration to handle more than one match.

Parameters

Name Type Description
plot_number string Required Path segment. The plot number to look up.
city string Optional Query parameter. Narrows results to one city. Case-insensitive — kasane and KASANE both work.
limit integer Optional Maximum plots to return, 1–500. Defaults to 100.
country string Optional BOTSWANA or SOUTH_AFRICA, case-insensitive. Omitted, both are searched.

Botswana

Request
curl -H "X-API-KEY: YOUR_API_KEY_HERE" \
  "https://api.starlightsoftware.co.bw/api/v1/plots/73?city=kasane&country=BOTSWANA"
200 Response
{
  "plots": [
    {
      "id": 160572,
      "plot_number": "73",
      "latitude": -17.7997276,
      "longitude": 25.1532716,
      "city": "KASANE",
      "country": "BOTSWANA",
      "parcel_identifier_type": "PLOT_NUMBER",
      "district": "CHOBE DISTRICT"
    }
  ],
  "message": "Plots fetched successfully"
}
735 m
Registered plotPlot 73, Kasane — the registered position, with coordinates

South Africa

Request
curl -H "X-API-KEY: YOUR_API_KEY_HERE" \
  "https://api.starlightsoftware.co.bw/api/v1/plots/11862?city=kimberley&country=SOUTH_AFRICA"
200 Response
{
  "plots": [
    {
      "id": 595404,
      "plot_number": "11862",
      "latitude": -28.732384,
      "longitude": 24.7621112,
      "city": "KIMBERLEY",
      "country": "SOUTH_AFRICA",
      "parcel_identifier_type": "ERF_NUMBER",
      "province": "NORTHERN CAPE"
    }
  ],
  "message": "Plots fetched successfully"
}
118625 m
Registered plotPlot 11862, Kimberley — the registered position, with coordinates

Dropping city here would return every erf 11862 in the country. The township is not optional in practice, even though the parameter is.

200 Response — no match
{
  "plots": [],
  "message": "No plots found"
}
GET /api/v1/plots/reverse

Returns the single nearest plot to a coordinate — the "what plot am I standing on?" lookup. Useful for drivers confirming a delivery address and for field staff logging a visit.

Parameters

Name Type Description
latitude float Required Between −90 and 90.
longitude float Required Between −180 and 180.
country string Optional BOTSWANA or SOUTH_AFRICA, case-insensitive. Omitted, both are searched.

The search widens progressively until it finds something, so a point right on a plot is answered immediately. If nothing is found within roughly 100 km, an empty array is returned.

Botswana

Request
curl -H "X-API-KEY: YOUR_API_KEY_HERE" \
  "https://api.starlightsoftware.co.bw/api/v1/plots/reverse?latitude=-24.66252002&longitude=25.90249236&country=BOTSWANA"
200 Response
{
  "plots": [
    {
      "id": 1,
      "plot_number": "2",
      "latitude": -24.66252,
      "longitude": 25.9024924,
      "city": "GABORONE",
      "country": "BOTSWANA",
      "parcel_identifier_type": "PLOT_NUMBER",
      "district": "SOUTH-EAST DISTRICT",
      "distance_meters": 0
    }
  ],
  "message": "Plots fetched successfully"
}
25 m
Registered plotYour coordinateThe nearest registered plot to your coordinate — 0 m away

South Africa

Request
curl -H "X-API-KEY: YOUR_API_KEY_HERE" \
  "https://api.starlightsoftware.co.bw/api/v1/plots/reverse?latitude=-28.732384&longitude=24.7621112&country=SOUTH_AFRICA"
200 Response
{
  "plots": [
    {
      "id": 595404,
      "plot_number": "11862",
      "latitude": -28.732384,
      "longitude": 24.7621112,
      "city": "KIMBERLEY",
      "country": "SOUTH_AFRICA",
      "parcel_identifier_type": "ERF_NUMBER",
      "province": "NORTHERN CAPE",
      "distance_meters": 0
    }
  ],
  "message": "Plots fetched successfully"
}
118625 m
Registered plotYour coordinateThe nearest registered plot to your coordinate — 0 m away

Without country this lookup searches both, and near the border the nearest parcel may be in the other one — which is correct, and rarely what an application wants.

Field differences by country

Every plot object names the country it belongs to, so an integration written for one keeps working when another is added. South African records travel with the erf number in the same plot_number field, and parcel_identifier_type says what the value is.

Field Botswana South Africa
country BOTSWANA SOUTH_AFRICA
parcel_identifier_type PLOT_NUMBER ERF_NUMBER
province Not returned Province name, e.g. NORTHERN CAPE
city Town Township / allotment area
district District Not returned

Rate Limits

Each key has its own per-minute allowance, agreed with you when the key is issued. Every response carries standard rate-limit headers, so you can pace your client rather than waiting to be rejected.

Response headers
RateLimit-Policy: 60;w=60
RateLimit: limit=60, remaining=59, reset=60

remaining is the number of requests left in the current window; reset is the seconds until it refills. Exceeding the limit returns 429. If you consistently need more, contact us — the limit is a contract setting, not a technical ceiling.

Errors

Errors return a consistent JSON body:

Error shape
{
  "error": "ValidationError",
  "message": "Invalid query parameters",
  "details": [{ "field": "latitude", "message": "latitude is required" }],
  "requestId": "78be60d3-8000-4601-9fda-7d5e6838388d"
}

Every response includes a requestId, also returned as the X-Request-Id header. Quote it when contacting support — it matches our server logs and turns a vague report into a two-minute diagnosis.

Status codes

Status Meaning What to do
200 Success. Note that an empty plots array is still a 200. Check whether plots is empty before reading [0].
400 Invalid parameters — missing, non-numeric, or out of range. Every invalid field is listed in details, not just the first. Fix the parameters listed in details; do not retry unchanged.
401 Key missing, malformed, unrecognised, or suspended. See the codes below. Check the header is being sent. If suspended, contact us.
404 No such route. This means the URL is wrong — a plot that does not exist returns 200 with an empty array, not a 404. Check the path and API version.
429 Rate limit exceeded for your key. Back off until the reset window elapses, then retry.
500 Something failed on our side. Retry with backoff. If it persists, send us the requestId.

Authentication error codes

error Status Meaning
ApiKeyMissing 401 No X-API-KEY header was sent.
ApiKeyInvalid 401 The key is malformed or not recognised.
ApiKeySuspended 401 The key was revoked. Contact us to reinstate it.
ApiKeyExpired 401 The key reached the end of its term — a trial, or a period that has run out. The message names the date. Contact us to extend it; the same key can be re-dated, so nothing in your integration has to change.
CountryNotPermitted 403 The key asked for a country it does not cover — see Botswana & South Africa.
CityNotPermitted 403 The key named a city outside the towns it covers. Keys sold specific towns are refused others rather than answered with an empty list.
ValidationError 400 One or more parameters were invalid.
RateLimitExceeded 429 Per-minute allowance exhausted.

Questions?

Email info@starlightsoftware.co.bw. If you are reporting a problem with a specific request, include the requestId from the response.

Request API Access