Relm API + MCP.

Explore

Relm Data (public records)

Query Relm Data by API or MCP

Browse the public-records library, resolve which sources cover an address, pull one source, or ask a question about an address or a whole city — from your own code or from an AI agent over MCP.

Relm TeamUpdated 10 min read
On this page

The library behind the Public Records section is exposed as eight read operations. They exist twice with the same behaviour: as REST endpoints under /v1/data/library and as tools on the Relm MCP server, so a script and an AI agent get identical records and identical citations.

Browsing the library, reading a source's catalog entry and checking which sources cover an address are free. Pulling records from a source and searching fetched records cost 2 credits per call, reading several sources in one batch costs 2 credits per source that answered, and asking a question, about an address or about a city or county, costs 20 credits per call, because each ask can fan out to a dozen government portals. A repeat call costs the same as the first. Normal API rate limits apply, and the question-answering operation has its own, tighter limit.

Before you start

You need an API key. On a paid plan, an organisation admin issues keys under Settings → Developer (see Roles). Keys are shown once; store them in a secret manager, never in a repository.

  • REST: Authorization: Bearer <your key> against https://api.relm.ai.
  • MCP: point any MCP client at https://api.relm.ai/mcp/ with the same bearer token. The server's instructions tell the agent when to use each tool, so you rarely need to prompt for them by name. Step-by-step setup for Claude, ChatGPT, Gemini and GitHub Copilot is in Connect Relm to your AI assistant.

The interactive reference, with every parameter, lives at /api-docs; the MCP guide is at /mcp.

Relm Settings, Developer, MCP page showing the server address and per-client setup tabs

The eight operations

What you wantRESTMCP tool
Browse the library by state, topic or free textGET /v1/data/librarylist_public_record_sources
Catalog entries for sources by id, with their columnsGET /v1/data/library/sourcesget_public_record_sources
Which sources cover an address (nothing fetched)GET /v1/data/library/resolveresolve_public_record_sources
Raw records from one source, by address, keywords or tableGET /v1/data/library/{source_id}query_public_record_source
Raw records from up to 10 sources in one callGET /v1/data/library/batchread_public_record_sources
Ask a question; the agent reads every matching sourceGET /v1/data/library/askask_public_records
Ask about a city, county or state with no street addressGET /v1/data/library/jurisdiction/askask_jurisdiction_records
Semantic search over records the library has already fetchedGET /v1/data/library/searchsearch_public_records

Every source_id is a stable 16-character identifier derived from the source's endpoint, so the id you see on the feature page, in a citation, or in a resolve result is the one you pass to the per-source call.

Browse

GET /v1/data/library?state=NY&topic=permits&limit=20

Returns a page of sources (name, publisher, state, topic, platform, verdict from the latest live test, whether it can be queried by address) and, alongside the page, the list of every state and topic in the whole library — so one call is enough to build a filter UI. A topic outside the library's vocabulary returns 422 with the valid names.

Catalog entries

GET /v1/data/library/sources?ids=f16b07e62fe12d3a

Catalog entries for up to 100 sources by id: name, publisher, topic, platform, landing page, columns (the names filters takes) and readable (by address, as a table, or neither). Read it before filtering a source you have not read yet. 404 names any id not in the library. It is free, because nothing is fetched from the portal.

Resolve

GET /v1/data/library/resolve?address=350+5th+Ave,+New+York,+NY+10118&topics=permits,violations

Returns the geocode and the ranked sources an ask would read for that address — each with its match level (city, county or state), topic and tier. Use it to check coverage before a longer call, or to pick a source_id. Pass lat and lon to skip geocoding when you already have coordinates.

One source

GET /v1/data/library/f16b07e62fe12d3a?address=350+5th+Ave,+New+York,+NY+10118&limit=25

Raw rows from that source near the address, newest first when the dataset has a date, with one citation per record and a records_sha256 hash over the rows. No agent and no model are involved, so this is the call to use when you want the government's data and nothing else. Because you chose the source, Relm does not check jurisdiction: asking a Boston dataset about a Manhattan address simply returns no rows.

A source can be read three ways, and the response's mode says which one ran:

  • By address (mode: address): the source has a location or address column and you gave an address, as above.

  • By keywords (mode: keywords): you gave keywords, since or open_only. This is the read for a source with no address to filter on, such as a city's notice or procurement feed:

    GET /v1/data/library/{source_id}?keywords=RFP,outreach&open_only=true

    Each keyword (up to 25) is matched as a whole word, and a keyword of several words as a phrase. Recent rows come first (the last 180 days, widened once to 3 years when that finds nothing, or from since=YYYY-MM-DD), at most 25 rows, and open_only=true keeps notices whose deadline has not passed. A keyword search carries a coverage status, where a timeout means the source could not be checked, never that it has no records.

  • As a table (mode: table): everything else, including a source with no address column when you give no keywords. You get one page of the source with its columns. Narrow it with filters, exact column values written as column=value pairs separated by ; (up to 5), and q for full-text search inside the source. Page with offset, passing the next_offset from the last answer. On a map service with several layers, layer picks one. This is how you read a town- or county-level table, such as a grand list:

    GET /v1/data/library/{source_id}?filters=town_name=Hartford;grand_list_year=2024

Keyword search and table filters are alternatives: sending both returns 422. status is hit, empty, error, unsupported (a web portal with no data API; landing_url opens it), needs_credentials or locked. 404 when the id is not in the library. Each call costs 2 credits.

Several sources at once

GET /v1/data/library/batch?ids=f16b07e62fe12d3a,2d764b3878235d21&address=350+5th+Ave,+New+York,+NY+10118

Reads up to 10 sources in one call, each exactly as the single-source call would: by address where the source has a location or address column, otherwise as a table. filters and q apply to every source. results keeps the order of ids, and a source that fails is its own item with status: error, never a failure of the whole call. Use it to compare publishers, such as every town's grand list or every county's permits. It costs 2 credits per source that answered (hit or empty).

Ask

GET /v1/data/library/ask?address=…&question=Any+open+permits+or+violations%3F

The same agent an Intel Report runs. The response keeps the raw records exactly as fetched (personal fields removed), records_sha256, one citation per record, a coverage block saying which sources were tried, returned rows, were empty, or are locked behind the Relm Data add-on, and an answer that may only cite [n] indexes present in records. A cold ask on a dense city address can take up to a minute; repeats within a topic's cache window are fast.

Ask about a city or county

GET /v1/data/library/jurisdiction/ask?question=Open+RFPs+for+leasing+office+space&state=NJ&place=Newark&keywords=lease,office+space

For questions with no street address: open RFPs and solicitations, public hearings and meetings, and other datasets a city or county publishes. Give the two-letter state and, optionally, the place (city, town or borough) and county, plus up to twelve short keywords. The agent picks up to eight of that jurisdiction's sources (statewide ones only when nothing local covers it), searches each for the keywords and answers from the records only. The response has the same shape as an address ask, with scope set to jurisdiction. An empty result means no matching record in the sources searched, not proof that none exists; a city's code book is usually not in the library.

GET /v1/data/library/search?q=sprinkler+system+violation&topic=violations

Semantic search across every public record the library has already fetched, for any address. The index holds only what a government portal published, so a hit is a fetched row with its source and query URL. It cannot see records nobody has fetched yet — use ask for a fresh read. Returns 503 service_unavailable if the search index is down.

From an agent

With the MCP server connected, a typical exchange is two tool calls: resolve to see what covers the address, then ask (or query one source). The agent receives the same records, citations and coverage fields a REST caller does, so it can quote a record and link the dataset rather than paraphrase.

resolve_public_record_sources({ "address": "350 5th Ave, New York, NY 10118", "topics": "permits,violations" })
ask_public_records({ "question": "Any open permits or violations?", "address": "350 5th Ave, New York, NY 10118" })

Zoning code text

What a city's zoning code says is a separate read: GET /v1/data/code/search and the search_zoning_code tool search the code by meaning or return one section by its number, with a link to the publisher's page. Narrow a search with special_district (one special district's chapter, named in words) or base_only (the base regulations only), not both. New York City's Zoning Resolution is loaded today; for a city whose code is not loaded yet the call says so instead of returning nothing. It costs 2 credits per call.

Historical aerial photos

GET /v1/data/historical-aerials?lat=40.7484&lon=-73.9857&start_year=1950&end_year=1990 and the get_historical_aerials tool list the historical aerial photo frames (1930s onward) that cover a point, newest first, from USGS EarthExplorer. Pass an address instead of lat and lon and it is geocoded first. Each scene carries USGS's own ids, the date it was flown, a preview image, its footprint and one citation.

This read uses your organisation's own USGS account, connected under Settings → Third-Party Services (see Portal accounts); Relm has no USGS account of its own. Until one is connected, the answer says it needs credentials and links to that page. If USGS has not given the account Machine-to-Machine (M2M) access yet, the answer says so and links to the ERS page where you request it. It costs 2 credits only when USGS was actually searched, including a search that found no frames; nothing otherwise.

Reading the result honestly

  • A record is a row as the publisher returned it, minus personal fields. Column names are the publisher's; two cities' permit datasets will not share a schema.
  • coverage.gaps lists topics for which no source covers the address. That is information about what the government publishes, not a failed call.
  • An empty attempt means the portal was reached and had nothing within range; an error attempt carries the portal's reason.
  • Locked sources appear in coverage and in citations with a record count but no rows until the Relm Data add-on is enabled for your organisation.

What's next

Was this article helpful?

Still need help?

Our team usually responds within one business day. Tell us what you're trying to do and we'll get you unstuck.

Relm Pro Help Center