Township America TypeScript SDK: PLSS Lookups in Node.js for Land Tech Applications
typescriptapidevelopers

Township America TypeScript SDK: PLSS Lookups in Node.js for Land Tech Applications

Add PLSS geocoding to a Node.js app with the Township America TypeScript SDK: single lookups, batch conversion, autocomplete, and GeoJSON polygons.

If you build land tech in the Node.js stack, at some point a legal description has to become a coordinate. A title automation tool ingests a run sheet full of township and range references. A mineral rights platform needs to draw a parcel on a map. A GIS dashboard has to place thousands of tracts. Until now, doing that in TypeScript meant either hand-rolling PLSS math against BLM survey data or shelling out to a tool built for a browser, not a server. The Township America TypeScript SDK closes that gap: it gives Node.js developers a typed client for PLSS geocoding, so a legal description goes in and coordinates plus a GeoJSON polygon come back.

This post walks through the SDK from install to a working integration: a single lookup, a batch of up to 100 descriptions in one request, an autocomplete field, and the request patterns for Express, Fastify, PostGIS, and a Mapbox or Leaflet map. The examples below show the shape of a typical integration. Check the API reference at townshipamerica.com/api for the exact method names, response fields, and your account's API key.

The gap before a native Node.js SDK

PLSS resolution is not a lookup table. A description like Township 4 North, Range 5 East, Section 12, Northeast Quarter resolves differently depending on the principal meridian, and the underlying survey geometry comes from official BLM records. Township America resolves across 30+ PLSS states and 37 principal meridians, down to the 1/256 aliquot part (about 2.5 acres). Rebuilding that in your own code is a large amount of surveying logic to own and keep current.

The REST API already exposes single lookup, batch, autocomplete, and map tile endpoints. The TypeScript SDK is a thin, typed wrapper over those endpoints, which means your editor gives you completion and type checking on the request and the response, and you write less glue code to parse JSON by hand.

Install the SDK and run a single lookup

Add the package to your project and set your API key as an environment variable so it never lands in source control:

npm install townshipamerica
import { TownshipClient } from 'townshipamerica'

const ta = new TownshipClient({
  apiKey: process.env.TOWNSHIP_AMERICA_API_KEY!,
})

const result = await ta.search('NE 12 4N 5E Indian Meridian')
console.log(result.latitude, result.longitude)
console.log(result.boundary)

Pass a legal description string that names the principal meridian. The same township and range numbers exist under more than one meridian, so the meridian identifies the survey system the parser should use.

The SDK returns a SearchResult with latitude, longitude, legalLocation, and a nullable boundary. Check the boundary before using it in a spatial query. raw preserves the API FeatureCollection when you need the original features. Because you get a real polygon back, you can drop the boundary straight into a map or a spatial query instead of drawing an approximate box around a point.

Batch convert up to 100 descriptions per request

Single lookups are fine for a search box. A pipeline that refreshes a parcel dataset needs volume. The batch endpoint accepts up to 100 descriptions per request and returns coordinates and polygon geometry for each one:

const descriptions = [
  'NE 12 4N 5E Indian Meridian',
  'NW 25 24N 1E 6th Meridian',
]

const results = await ta.batchSearch(descriptions)
for (const [index, record] of results.entries()) {
  if (!record) {
    console.warn('Could not resolve:', descriptions[index])
    continue
  }
  console.log(record.latitude, record.longitude, record.boundary)
}

Handle each record's outcome on its own. A malformed or unresolvable description in the batch should not stop the rest, so check the per-record result and log the failures for review rather than throwing on the whole request. To move more than 100 records, let batchSearch chunk larger arrays automatically, and keep concurrency within your API tier's rate limit.

If your input arrives as a spreadsheet rather than structured records, the batch conversion workflow covers the CSV path and the structured export formats.

Drive an autocomplete field

For a live search input, calling the full lookup on every keystroke is wasteful. The autocomplete endpoint is built for partial input and returns candidate matches as the user types:

async function onInput(query: string) {
  const suggestions = await ta.autocomplete(query, { limit: 5 })
  return suggestions.features // render these in the dropdown
}

Debounce the input by 150 to 250 milliseconds so you send a request after the user pauses, not on every character. When someone selects a suggestion, run the single lookup to get the coordinates and polygon for the chosen description. This keeps typing responsive and reserves the heavier call for the one description that matters.

Wire it into your stack

The SDK is a plain async client, so it fits wherever your server code already runs. Handle authentication failures, quota errors, and timeouts at the route or job boundary rather than turning them into an empty parcel. A null batch result means no match; an exception means the request itself failed.

Keep the original description beside the returned coordinates in your database. That gives an analyst a way to check the source record when a range direction, principal meridian, or aliquot string is transposed. A successful lookup confirms that a description resolves, not that it is the description intended by the deed.

  • Express or Fastify: call the SDK inside a route handler and return the coordinates and polygon as JSON. Keep the API key on the server. Never ship it to the browser, where anyone can read it.
  • PostGIS: the GeoJSON polygon goes into a geometry column with ST_GeomFromGeoJSON, after which you can run spatial queries against parcels the same way you would with any other boundary.
  • Mapbox GL or Leaflet: the polygon is already GeoJSON, so you add it as a source or layer without any conversion step.

One more thing worth knowing if your work touches Texas: the SDK resolves the Texas Abstract, Block, and Survey grid alongside PLSS, so a cross-state dataset that mixes Texas tracts with PLSS descriptions goes through the same client.

Next steps

You now have the pieces for PLSS geocoding in Node.js: a typed single lookup that returns coordinates and a survey-accurate polygon, a batch call for up to 100 descriptions at a time, an autocomplete endpoint for search inputs, and the request patterns to place all of it inside an Express, Fastify, PostGIS, or map-based app.

To go live, create an API key in your developer portal and pick the metered tier that fits your volume. The Build, Scale, and Enterprise tiers are billed per request, so a prototype and a production pipeline can start on the same footing and grow with usage. See townshipamerica.com/api for tiers and the full endpoint reference, the developer overview for where this fits in a land tech build, and the oil and gas guide for how operators use these lookups in title and permitting work. If your team also writes Python, the Python SDK covers the same endpoints, so a cross-language codebase can share one source of PLSS truth.