Relm API + MCP.

Explore
Developer platform

The Relm API

A REST API for commercial real estate: search any US property, run a multi-agent Intel Report, build 10-year pro-formas, and read your portfolios — 54 endpoints, every finding traceable to a source.

https://api.relm.ai/v1

HTTP Authorization header

  • Bearer relm_sk_…
  • 403 when scope is too narrow
  • Rate limits per org

Authenticate

Send your key on every request

Use Authorization: Bearer with a read or full scoped key from Developer settings.

Discover

Call typed REST endpoints

Base URL https://api.relm.ai/v1 — search, preview, portfolios, documents, and research runs.

Automate

Pair with MCP for agents

The same org and billing power MCP tools and the REST API. See Intel Report for what a run returns.

Base URL & authentication

All requests go to https://api.relm.ai/v1 over HTTPS and authenticate with an API key you create on the API keys page. Pass it as a bearer token:

Authorization: Bearer relm_sk_...

Each key has a scope: read (all GET endpoints) or full (adds POST/PUT/DELETE — triggering runs and edits). Keys only authenticate for organizations on a paid plan.

Quickstart

Verify your key by fetching your account:

curl https://api.relm.ai/v1/me \
  -H "Authorization: Bearer relm_sk_..."

Search a property, then start an Intel Report run and poll it — pick your language (the choice applies to every example on this page):

# 1. Search
curl "https://api.relm.ai/v1/search?q=40+Main+St" \
  -H "Authorization: Bearer relm_sk_..."

# 2. Start an Intel Report (returns { run_id })
curl -X POST https://api.relm.ai/v1/deep-research \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"portfolio_id":"...","property_id":"..."}'

# 3. Poll status
curl https://api.relm.ai/v1/deep-research/RUN_ID \
  -H "Authorization: Bearer relm_sk_..."

API reference

54 endpoints, grouped below. Full request/response schemas are in the /v1/openapi.json spec. List endpoints accept limit / offset and return a total. Path placeholders like PROP_ID are shown in UPPERCASE in the examples.

Account

Identify the key and read your organization’s wallet.

GET/v1/me

Returns the user, organization, role, and scope the API key resolves to. Use it to verify a key works and which org it acts in.

curl https://api.relm.ai/v1/me \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/credits

The org’s credit balance, plan, and whether new Intel Report / pro-forma runs can start right now.

curl https://api.relm.ai/v1/credits \
  -H "Authorization: Bearer relm_sk_..."

Search & preview

Find properties and pull instant preview data by address.

GET/v1/search

Search the org’s properties, portfolios, and uploaded documents. Returns matches with the portfolio_id / property_id you can pass to other endpoints.

Parameters

  • q query · required — Search text — an address fragment, portfolio name, or document keywords.
curl "https://api.relm.ai/v1/search?q=40+Main+St" \
  -H "Authorization: Bearer relm_sk_..."
POST/v1/properties/preview

Fetch (or serve cached) preview data — basic info, taxes, area stats — for a US address. Synchronous; 10–30s for a cold address. An address must be previewed before it can be added to a portfolio. 10 credits per call, cached or not; 402 `credit_limit_reached` when the wallet is empty.

Parameters

  • address body · required — Full street address.
  • force_refresh body — Bypass the 30-day cache and re-fetch (default false).
curl -X POST https://api.relm.ai/v1/properties/preview \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"address":"40 Main St, Brooklyn, NY 11201"}'
GET/v1/properties/preview/progress

Real-time progress steps for an in-flight preview. Poll it alongside the preview call; returns an empty list when nothing is running for the address.

Parameters

  • address query · required — The same address string passed to the preview call.
curl "https://api.relm.ai/v1/properties/preview/progress?address=40+Main+St,+Brooklyn,+NY+11201" \
  -H "Authorization: Bearer relm_sk_..."

Property data

The complete property record for an address — every field group (ownership, transactions, prices, tax, listing, building, commercial financials). Read-scope keys work. 10 credits per call, including a repeat read served from cache; 402 `credit_limit_reached` when the wallet is empty.

GET/v1/properties/records

Superset of the preview: the entire raw record set for a US address in one non-lossy payload. Use paginate=true for large multifamily towers (the provider returns one record per unit).

Parameters

  • address query · required — Full street address, e.g. "40 Main St, Brooklyn, NY 11201".
  • paginate query — Paginate up to 500 records/page for towers with per-unit records (default false).
  • refresh query — Bypass the cache and re-fetch (default false).
curl "https://api.relm.ai/v1/properties/records?address=40+Main+St,+Brooklyn,+NY+11201" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/properties/records/{section}

A single curated slice of the record. section ∈ ownership | transactions | prices | tax | listing | building | commercial. Keys the record does not carry are omitted (never invented), so each slice is a faithful subset. Mirrors the get_property_* MCP tools.

Parameters

  • section path · required — ownership, transactions, prices, tax, listing, building, or commercial.
  • address query · required — Full street address.
  • paginate query — Paginate up to 500 records/page for towers with per-unit records (default false).
  • refresh query — Bypass the cache and re-fetch (default false).
curl "https://api.relm.ai/v1/properties/records/ownership?address=40+Main+St,+Brooklyn,+NY+11201" \
  -H "Authorization: Bearer relm_sk_..."

Public records

Raw public-record reads, returned in the publisher’s own shape: NYC building records, New York rent registration, hazard and environmental screens, geography, parcels, tenant credit, and historical aerial photos (read with your own USGS login). Every call answers 200; each block carries its own status, and "timeout" or "error" means that source could not be checked — never that nothing was found. Read-scope keys work. 2 credits per call; 402 `credit_limit_reached` when the wallet is empty. Mirrored by the Public records MCP tools.

GET/v1/data/nyc

Public records for one New York City tax lot: lot facts, deeds and parties, housing and building violations, permits, building filings (facades, boilers, elevators, certificates of occupancy, safety violations), hearings, tax liens, sales, rodent inspections, energy benchmarking and more. Omit datasets for a useful default set; datasets=all returns everything but takes over a minute. Each dataset block carries its own records and count, or an error marker. fdny_bfp is a summary the city stopped updating in 2019.

Parameters

  • bbl query · required — 10-digit borough-block-lot, e.g. 1008350041.
  • datasets query — Comma-separated dataset names, or "all". Default: pluto, acris_deeds, hpd_violations, dob_violations, oath. Also: acris_parties, affordable_housing, boiler_permits, bsa_applications, dob_electrical, dob_now_boiler, dob_now_build, dob_now_co, dob_now_elevator, dob_now_facades, dob_now_safety_violations, dob_permits, dohmh_rodent, fdny_bfp, hpd_registration, ll84_benchmarking, rolling_sales, tax_liens.
curl "https://api.relm.ai/v1/data/nyc?bbl=1008350041&datasets=dob_violations,dob_permits,dob_now_co" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/dhcr

Whether a New York building is registered with the state as rent-regulated. A hit means it registered at least once since 1984, not that its units are regulated today, and there is no unit count. Takes about 15 seconds.

Parameters

  • house_number query · required — House number, e.g. 195.
  • street_name query · required — Street name without its type, e.g. STANTON.
  • county query — County, e.g. NEW YORK. Strongly recommended: street names repeat across boroughs.
curl "https://api.relm.ai/v1/data/dhcr?house_number=195&street_name=STANTON&county=NEW+YORK" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/environmental

Federally regulated facilities near a point (compliance, facility registry, hazardous waste, and toxic release records), plus state remediation sites in New York. Outside New York the state block is empty — a coverage fact, not a clean site. For the compliance block, compare count with reported_rows before trusting a total.

Parameters

  • lat query · required — Latitude (WGS84).
  • lon query · required — Longitude (WGS84).
  • radius_miles query — Search radius, up to 5 (default 0.5). Larger radii get slow and noisy.
curl "https://api.relm.ai/v1/data/environmental?lat=40.7128&lon=-74.006&radius_miles=0.5" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/hazards

Flood zone, seismic design values, wildfire (wildland–urban interface), and subsidence at a point. Each block has a status; "error" means the screen could not run, never low risk.

Parameters

  • lat query · required — Latitude (WGS84).
  • lon query · required — Longitude (WGS84).
curl "https://api.relm.ai/v1/data/hazards?lat=40.7128&lon=-74.006" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/geocode

A US street address to its matched address and coordinates. Returns success: false with a reason when there is no match — never a guessed nearby point.

Parameters

  • address query · required — Full street address, e.g. "350 5th Ave, New York, NY 10118".
curl "https://api.relm.ai/v1/data/geocode?address=350+5th+Ave,+New+York,+NY+10118" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/county

County identity and boundary metadata for a 5-digit county FIPS code.

Parameters

  • fips query · required — 5-digit county FIPS, e.g. 36061 (New York County).
curl "https://api.relm.ai/v1/data/county?fips=36061" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/parcel

Parcel attributes from the county’s own GIS service, by assessor parcel number. Coverage comes from a hand-verified registry — today St. Louis County, MO (29189). Other counties return null fields plus a county_gis_operator_fetch_required flag, meaning "not covered yet", not "no parcel".

Parameters

  • apn query · required — Assessor parcel number.
  • county_fips query · required — 5-digit county FIPS.
  • state query — Two-letter state code, e.g. MO.
curl "https://api.relm.ai/v1/data/parcel?apn=09G220112&county_fips=29189&state=MO" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/wetlands

Mapped wetland features inside a bounding box. count_only=true (the default) is cheap; geometry is not. Can take 8–20 seconds.

Parameters

  • bbox query · required — min_lon,min_lat,max_lon,max_lat in WGS84.
  • count_only query — Return only the feature count (default true).
curl "https://api.relm.ai/v1/data/wetlands?bbox=-85.7,42.1,-85.4,42.4&count_only=true" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/protected-lands

Whether a point sits inside protected land, and who manages it. protected is a real boolean only when status is "success"; on "error" it is null, because "could not check" must not read as "unencumbered". areas is a list, since designations overlap.

Parameters

  • lat query · required — Latitude (WGS84).
  • lon query · required — Longitude (WGS84).
curl "https://api.relm.ai/v1/data/protected-lands?lat=37.7456&lon=-119.5936" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/tenant-credit

Public-company filing lookup for a tenant or guarantor, by the name on the lease. A miss means "not found in public-company filings", never "not a public company" — subsidiaries, d/b/a names, and franchisees often miss.

Parameters

  • company query · required — Tenant or guarantor name, e.g. "Starbucks Corporation".
curl "https://api.relm.ai/v1/data/tenant-credit?company=Starbucks+Corporation" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/street-view

Whether street-level imagery exists at a point, and when it was captured. Metadata only — no image. A capture older than the last sale is a caveat, not evidence.

Parameters

  • lat query · required — Latitude (WGS84).
  • lon query · required — Longitude (WGS84).
curl "https://api.relm.ai/v1/data/street-view?lat=40.7484&lon=-73.9857" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/historical-aerials

Historical aerial photo frames covering a point (1930s onward), newest first, read with your organisation’s own USGS EarthExplorer login from Settings → Third-Party Services (USGS username + M2M application token). Each scene has `entity_id`, `display_id`, `acquisition_date`, `browse_url` / `thumbnail_url`, the GeoJSON `footprint`, its `center` and a `citation_index` into `citations`. Without a connected login the answer is `status: "needs_credentials"` with `connect_url`; an account USGS has not granted M2M access adds `reason: "no_m2m_access"` and `m2m_access_url`. `no_records` means USGS was searched and has no frame there. 2 credits only when USGS was searched; nothing otherwise.

Parameters

  • lat query — Latitude (WGS84). Give with lon, or give address.
  • lon query — Longitude (WGS84). Give with lat, or give address.
  • address query — US street address, geocoded when lat/lon are not given.
  • start_year query — Earliest year flown, e.g. 1950.
  • end_year query — Latest year flown, e.g. 1990.
  • limit query — Most scenes to return, 1–100. Default 20.
curl "https://api.relm.ai/v1/data/historical-aerials?lat=40.7484&lon=-73.9857&start_year=1950&end_year=1990" \
  -H "Authorization: Bearer relm_sk_..."

Relm Data library

Municipal, county and state open-data sources across the Northeast, Southwest, Southeast, Northwest, South, Great Lakes and Midwest — permits, certificates of occupancy, fire, violations, inspections, complaints and zoning — read straight from the publishing government portal. Browsing the library, reading catalog entries and resolving coverage are free; a source query or search costs 2 credits, a batch read 2 credits per source that answered, and an ask 10. The same records feed the public-records step of an Intel Report.

GET/v1/data/library

One page of the library with each source’s id, name, state, topic, platform, publisher and landing page. `states` and `topics` list every value in the whole library, not just the page, so one call is enough to build filters. Each `id` is what the single-source endpoint takes.

Parameters

  • state query — Two-letter state, e.g. NY.
  • topic query — Catalog topic, e.g. permits.
  • q query — Free text matched against source name, publisher, municipality or endpoint.
  • offset query — Rows to skip (default 0).
  • limit query — Rows per page (1–200, default 50).
curl "https://api.relm.ai/v1/data/library?state=NY&topic=permits&limit=20" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/library/resolve

The geocode and the ranked sources an ask would query for this address, each with its match level (city, county or state), topic and tier. Nothing is fetched, so this is the cheap way to check coverage before a longer call. 422 names any topic outside the library’s vocabulary.

Parameters

  • address query · required — Street address, city and state.
  • topics query — Comma-separated: permits, certificate_of_occupancy, fire, violations, inspections, complaints, zoning. Omit for all.
  • lat query — Latitude (WGS84); skips geocoding when given with lon.
  • lon query — Longitude (WGS84); skips geocoding when given with lat.
curl "https://api.relm.ai/v1/data/library/resolve?address=350+5th+Ave,+New+York,+NY+10118&topics=permits,violations" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/library/{source_id}

Raw records from one library source, exactly as the portal returned them (personal fields dropped), with one citation per record and a SHA-256 over the rows. No agent, no model. Three reads, and `mode` says which ran. `keywords`: `keywords`, `since` or `open_only` was given, so the source is searched by keyword (each term as a whole word or a phrase, recent rows first: the last 180 days, widened once to 3 years when that finds nothing; at most 25 rows, text values cut at 500 characters), with a coverage status where a timeout means not checked, never "no records". This is the read for a source with no address field, such as a city’s notice or procurement feed. `address`: the source has a location or address column and an address was given, so it returns records near it, newest first when the source has a date (the jurisdiction check is skipped, so a source for another city simply returns none). `table`: everything else, one page of the source narrowed by `filters` and `q`, paged with `offset` / `next_offset`, with the source’s `columns`. Keyword search and table filters are alternatives (422 when both are given). `read` repeats the mode (`by_keywords`, `by_address`, `table`). `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. 2 credits per call; 402 `credit_limit_reached` when the wallet is empty.

Parameters

  • source_id path · required — Source id from the library list or resolve.
  • address query — Street address, city and state. Used on a source with a location or address column; omit it to read the source as a table.
  • filters query — Table read: exact-match column filters, `column=value` pairs separated by `;` (up to 5), e.g. `town_name=Hartford; grand_list_year=2024`. Column names come from `columns` in a previous answer or in `/v1/data/library/sources`.
  • q query — Table read: full-text search inside the source (Socrata, CKAN, Carto; ArcGIS searches its text columns).
  • offset query — Table read: rows to skip (default 0); pass `next_offset` for the next page.
  • layer query — Table read: on an ArcGIS service with several layers, which layer to read (see `layers`).
  • keywords query — Comma-separated search terms, at most 25 per call, each up to 40 characters. Each is matched as a whole word; a term of several words is matched as a phrase (any spaces, hyphens or punctuation between its words). Giving them switches to keyword search.
  • since query — Keyword search only: rows dated on or after this day, YYYY-MM-DD (default: the last 180 days, widened once to 3 years when that finds nothing).
  • open_only query — Keyword search only: keep rows whose deadline is today or later, when the source has a deadline column.
  • limit query — Records to return (1–200, default 25). A keyword search returns at most 25.
  • lat query — Latitude (WGS84); skips geocoding when given with lon.
  • lon query — Longitude (WGS84); skips geocoding when given with lat.
curl "https://api.relm.ai/v1/data/library/f16b07e62fe12d3a?address=350+5th+Ave,+New+York,+NY+10118&limit=25" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/library/sources

Catalog entries for one or more 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. Free: nothing is fetched.

Parameters

  • ids query · required — Comma-separated source ids, up to 100.
curl "https://api.relm.ai/v1/data/library/sources?ids=f16b07e62fe12d3a" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/library/batch

Read up to 10 library sources in one call, each exactly as the single-source endpoint 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`; one source failing is that item’s `status: error`, never the batch’s. Use it to compare publishers, such as every town’s grand list or every county’s permits. 2 credits per source that answered (`hit` or `empty`); 402 `credit_limit_reached` when the wallet is empty.

Parameters

  • ids query · required — Comma-separated source ids, up to 10.
  • address query — Street address, city and state. Used on a source with a location or address column; omit it to read each source as a table.
  • filters query — Exact-match column filters, `column=value` pairs separated by `;` (up to 5), applied to every source in the batch. Column names come from `columns` in a previous answer or in `/v1/data/library/sources`.
  • q query — Full-text search inside each source (Socrata, CKAN, Carto; ArcGIS searches its text columns).
  • limit query — Records per source (1–200, default 25).
  • lat query — Latitude (WGS84); skips geocoding when given with lon.
  • lon query — Longitude (WGS84); skips geocoding when given with lat.
curl "https://api.relm.ai/v1/data/library/batch?ids=f16b07e62fe12d3a,2d764b3878235d21&address=350+5th+Ave,+New+York,+NY+10118" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/library/ask

Queries every matching source for the address, then answers from the records only. The envelope keeps the raw records, a hash over them, one citation per record and a coverage block saying which sources were tried, hit or locked; the answer may only cite record indexes that exist. A cold ask on a dense city address can take up to a minute; repeats inside the cache window are fast.

Parameters

  • address query · required — Street address, city and state.
  • question query · required — The question to answer from the records.
  • topics query — Comma-separated topics to limit the ask to. Omit for all.
  • lat query — Latitude (WGS84); skips geocoding when given with lon.
  • lon query — Longitude (WGS84); skips geocoding when given with lat.
curl "https://api.relm.ai/v1/data/library/ask?address=350+5th+Ave,+New+York,+NY+10118&question=Any+open+permits+or+violations%3F" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/library/jurisdiction/ask

For a question about a city, county or state with no street address: open RFPs and solicitations, public hearings and meetings, and other datasets a jurisdiction publishes. The agent picks up to 8 of that jurisdiction’s sources (statewide ones only when nothing local covers it), searches each for your keywords and answers from the records only. The envelope matches the address ask, plus `scope: "jurisdiction"` and the jurisdiction asked about. An empty result means no matching record in the sources searched, not proof that none exists. 422 for a state the library does not cover, more than 12 keywords or an unknown topic. 20 credits per call; 402 `credit_limit_reached` when the wallet is empty; 429 `rate_limit_exceeded` shares the address ask’s limit.

Parameters

  • question query · required — The question to answer from the records.
  • state query · required — Two-letter state, e.g. NJ.
  • place query — City, town or borough, e.g. Newark. Omit for a county- or state-wide question.
  • county query — County, e.g. Essex County.
  • keywords query — Comma-separated search terms, at most 12, each up to 40 characters; a term of several words is matched as a phrase. Without them the newest rows of each source are read.
  • topics query — Comma-separated topics to limit the sources to. Omit for all.
curl "https://api.relm.ai/v1/data/library/jurisdiction/ask?question=Open+RFPs+for+leasing+office+space&state=NJ&place=Newark&keywords=lease,office+space" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/data/library/search

Semantic search over 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 name and query URL. It cannot see records nobody has fetched yet — use the ask endpoint for a fresh read. 503 when the search index is unavailable.

Parameters

  • q query · required — What to look for, in plain words.
  • address query — Limit hits to records fetched for this address.
  • topic query — Limit hits to one topic.
  • top_k query — Hits to return (1–50, default 10).
curl "https://api.relm.ai/v1/data/library/search?q=sprinkler+violation&topic=violations" \
  -H "Authorization: Bearer relm_sk_..."

Zoning code

Read what a city’s zoning code says, not which district a lot is in. New York City’s Zoning Resolution today. 2 credits per call.

GET/v1/data/code/search

Search a city’s zoning code text by meaning, or read one section by its number. Each section carries its number, title, full text, last-amended date and a link to the publisher’s page to cite; a search hit also carries the matching excerpt, and one inside a special district carries `special_district` (its name). `status` is `ok` (search), `found` / `not_found` (section, or a `special_district` no district is named like) or `not_indexed`: that city’s code is not loaded in Relm yet, so it was not checked. Narrow by area with `special_district` (that district’s chapter only) or `base_only` (base regulations only). With neither, a district the query names comes first, and for a bare district token ("C6-3 FAR") the base regulations rank above special districts that merely mention it. 422 for an unsupported jurisdiction, a call with neither query nor section, or both special_district and base_only. 503 `service_unavailable` when search cannot run. 2 credits per call; 402 `credit_limit_reached` when the wallet is empty.

Parameters

  • jurisdiction query · required — Whose code to read, e.g. NYC.
  • query query — What to find, in plain words. Ignored when section is given.
  • section query — An exact section number, e.g. 23-11. Returns that section’s full text.
  • top_k query — Sections to return for a query (1–20, default 5).
  • special_district query — Search only one special district’s chapter, named in words, e.g. East Harlem Corridors. When no district has that name, status is not_found and the message names close matches.
  • base_only query — Search only the base regulations, leaving every special district’s chapter out. Not with special_district.
curl "https://api.relm.ai/v1/data/code/search?jurisdiction=NYC&query=minimum+lot+width+for+detached+houses+in+R3+districts" \
  -H "Authorization: Bearer relm_sk_..."

Portfolios

Group properties into portfolios and manage membership.

GET/v1/portfolios

Every portfolio in the org, newest first. Paginated; the response includes count, total, limit, and offset.

Parameters

  • limit query — Max to return (1–200, default 50).
  • offset query — Rows to skip (default 0).
curl "https://api.relm.ai/v1/portfolios?limit=50&offset=0" \
  -H "Authorization: Bearer relm_sk_..."
POST/v1/portfolios

Creates a new portfolio in the org and returns it (201).

Parameters

  • name body · required — Display name (1–200 chars).
curl -X POST https://api.relm.ai/v1/portfolios \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"Brooklyn Multifamily 2026"}'
GET/v1/portfolios/{portfolio_id}

One portfolio’s metadata (name, property count, created date). 404 for unknown or cross-org ids.

Parameters

  • portfolio_id path · required — Portfolio id.
curl https://api.relm.ai/v1/portfolios/PORTFOLIO_ID \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/portfolios/{portfolio_id}/properties

Summary rows (id, address, section-completion) for the properties in one portfolio. Paginated.

Parameters

  • portfolio_id path · required — Portfolio id.
  • limit query — Max to return (1–200, default 50).
  • offset query — Rows to skip (default 0).
curl "https://api.relm.ai/v1/portfolios/PORTFOLIO_ID/properties?limit=50&offset=0" \
  -H "Authorization: Bearer relm_sk_..."
POST/v1/portfolios/{portfolio_id}/properties

Adds a previously-previewed address to the portfolio and returns the new property_id. 404 if the address was never previewed.

Parameters

  • portfolio_id path · required — Target portfolio id.
  • address body · required — The exact address that was previewed.
curl -X POST https://api.relm.ai/v1/portfolios/PORTFOLIO_ID/properties \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"address":"40 Main St, Brooklyn, NY 11201"}'
GET/v1/portfolios/{portfolio_id}/properties/{prop_id}

The full, normalized property data document as it sits in that portfolio.

Parameters

  • portfolio_id path · required — Portfolio id.
  • prop_id path · required — Property id.
curl https://api.relm.ai/v1/portfolios/PORTFOLIO_ID/properties/PROP_ID \
  -H "Authorization: Bearer relm_sk_..."
DELETE/v1/portfolios/{portfolio_id}/properties/{prop_id}

Removes a property from a portfolio (org-scoped). 404 if it isn’t in that portfolio.

Parameters

  • portfolio_id path · required — Portfolio id.
  • prop_id path · required — Property id.
curl -X DELETE https://api.relm.ai/v1/portfolios/PORTFOLIO_ID/properties/PROP_ID \
  -H "Authorization: Bearer relm_sk_..."

Properties

Read properties org-wide or by id, without walking portfolios.

GET/v1/properties

Every property in the org (one row per portfolio membership), newest first. Paginated; optionally scoped to a portfolio.

Parameters

  • portfolio_id query — Restrict to a single portfolio.
  • limit query — Max to return (1–200, default 50).
  • offset query — Rows to skip (default 0).
curl "https://api.relm.ai/v1/properties?limit=50&offset=0" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/properties/{prop_id}

The full property data document by property id alone (any portfolio in the org). 404 if not in your org.

Parameters

  • prop_id path · required — Property id.
curl https://api.relm.ai/v1/properties/PROP_ID \
  -H "Authorization: Bearer relm_sk_..."

Intel Report

Kick off multi-agent research and follow it to completion.

POST/v1/deep-research

Starts (or queues) an Intel Report run. A full run (services omitted or ["all"]) researches basics, units, comps, taxes, POIs, area stats, and a summary. It is charged when it completes, by the data and AI work it delivered — about 300 credits for a typical full analysis. Returns a run_id (202); 402 `credit_limit_reached` when the org has no credits left.

Parameters

  • portfolio_id body · required — Portfolio containing the property.
  • property_id body · required — Property to research.
  • services body — Sections to run; omit for a full run. e.g. ["comps","taxes"].
  • auto_run_financials body — Start the pro-forma automatically on completion (default false).
curl -X POST https://api.relm.ai/v1/deep-research \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"portfolio_id":"PORTFOLIO_ID","property_id":"PROP_ID"}'
GET/v1/deep-research/{run_id}

Per-section statuses plus a rollup: processing | complete | error | stopped. Works for Intel Report and pro-forma runs. Status expires ~24h after completion.

Parameters

  • run_id path · required — run_id returned when the run started.
curl https://api.relm.ai/v1/deep-research/RUN_ID \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/runs/{run_id}/steps

The run’s thinking steps (the live activity trace). Pass the returned next_cursor back to receive only new steps. Steps expire ~1h after the run ends.

Parameters

  • run_id path · required — Run to inspect.
  • cursor query — Opaque cursor from a previous response.
curl "https://api.relm.ai/v1/runs/RUN_ID/steps" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/runs/{run_id}/steps/stream

The same steps as a Server-Sent Events stream. Emits a final run_complete event when the run reaches a terminal state; capped per key. Reconnect or fall back to polling on idle/timeout.

Parameters

  • run_id path · required — Run to stream.
curl -N https://api.relm.ai/v1/runs/RUN_ID/steps/stream \
  -H "Authorization: Bearer relm_sk_..."

Pro forma

Build, read, edit, and export the 10-year financial model.

POST/v1/properties/{prop_id}/proforma

Starts the financial pro-forma run (~18 line-item agents; 10–30 min). Returns a run_id (202); 402 `pro_forma_limit_reached` without an active subscription. An Intel Report should have completed first.

Parameters

  • prop_id path · required — Property to underwrite.
  • portfolio_id body · required — Portfolio containing the property.
curl -X POST https://api.relm.ai/v1/properties/PROP_ID/proforma \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"portfolio_id":"PORTFOLIO_ID"}'
GET/v1/properties/{prop_id}/proforma

The transformed pro-forma: Year-1 summary, 10-year projections, and line items. Each line item carries key / label / category / year1_value / per_unit / per_sqft / ten_year_values.

Parameters

  • prop_id path · required — Property id.
curl https://api.relm.ai/v1/properties/PROP_ID/proforma \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/properties/{prop_id}/proforma/export

Download the pro-forma as an Excel model (formulas), a formatted PDF report, or raw JSON.

Parameters

  • prop_id path · required — Property id.
  • format query — xlsx (default) | pdf | json.
curl "https://api.relm.ai/v1/properties/PROP_ID/proforma/export?format=xlsx" \
  -H "Authorization: Bearer relm_sk_..." \
  -o proforma.xlsx
PUT/v1/properties/{prop_id}/financials

Save an edited pro-forma: the full line-item array (same shape GET .../proforma returns) plus optional financing assumptions. Edits are recorded as source="api". Expenses/losses are stored as negative numbers.

Parameters

  • prop_id path · required — Property id.
  • portfolio_id body · required — Portfolio containing the property.
  • line_items body · required — Full FinancialLineItem array.
  • financing body — purchase_price, down_payment_pct, interest_rate, loan_term_years, closing_costs_pct, plus optional loan structure: interest_only_years (default 0), amortization_years (default = loan_term_years), origination_fee_pct (fraction of the loan, default 0).
curl -X PUT https://api.relm.ai/v1/properties/PROP_ID/financials \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{"portfolio_id":"PORTFOLIO_ID","line_items":[{"key":"gross_potential_income","year1_value":250000}]}'

Sections

Read (and, where allowed, write) one section of a property.

GET/v1/properties/{prop_id}/sections/{section}

One data section of a property. Readable: basic_info, taxes, comps, units, summary, area_stats, pois, market, neighborhood_news. (Financials live under the /proforma endpoints.)

Parameters

  • prop_id path · required — Property id.
  • section path · required — Section name (see list above).
curl https://api.relm.ai/v1/properties/PROP_ID/sections/taxes \
  -H "Authorization: Bearer relm_sk_..."
PUT/v1/properties/{prop_id}/sections/{section}

Save one section (schema-validated). Writable sections: basic_info, taxes, comps, units, summary, area_stats, pois. 409 if another editor holds the lock; 422 on schema failure. (market and neighborhood_news are read-only.)

Parameters

  • prop_id path · required — Property id.
  • section path · required — Writable section name.
  • (body) body · required — The section payload (shape matches what GET returns).
curl -X PUT https://api.relm.ai/v1/properties/PROP_ID/sections/summary \
  -H "Authorization: Bearer relm_sk_..." \
  -H "Content-Type: application/json" \
  -d '{ "...section fields..." }'

Documents

Attach deal documents and make them searchable.

POST/v1/properties/{prop_id}/documents

Upload one or more files (multipart/form-data field "files"). Allowed: pdf, csv, tsv, xlsx, xls, html, json, txt, md, doc, docx, and common images. 50MB per file; per-file failures are reported without failing the batch.

Parameters

  • prop_id path · required — Property to attach to.
  • files body · required — Multipart form field; repeat for multiple files.
curl -X POST https://api.relm.ai/v1/properties/PROP_ID/documents \
  -H "Authorization: Bearer relm_sk_..." \
  -F "files=@rent_roll.pdf"
POST/v1/properties/{prop_id}/documents/{document_id}/index

Start vector indexing so a document becomes searchable / citable in chat and an Intel Report (async, 202). Returns an upload_id to poll.

Parameters

  • prop_id path · required — Property id.
  • document_id path · required — Document id from the upload response.
curl -X POST https://api.relm.ai/v1/properties/PROP_ID/documents/DOCUMENT_ID/index \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/properties/{prop_id}/documents/indexing-status/{upload_id}

Status of an indexing job: pending, in_progress, failed, or complete.

Parameters

  • prop_id path · required — Property id.
  • upload_id path · required — upload_id from the index call.
curl https://api.relm.ai/v1/properties/PROP_ID/documents/indexing-status/UPLOAD_ID \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/documents/search

Semantic (vector) search across your org’s uploaded documents. Scope to one property (prop_id), one portfolio (portfolio_id), or neither to search the whole org and find similar content anywhere. Each hit returns the matched text snippet, file name, doc type, property/portfolio, and similarity distance. 2 credits per call; 402 `credit_limit_reached` when the wallet is empty.

Parameters

  • q query · required — Natural-language query to match document content.
  • portfolio_id query — Restrict to documents of one portfolio’s properties.
  • prop_id query — Restrict to one property’s documents.
  • doc_type query — Filter by document type (e.g. rent_roll, inspection).
  • top_k query — Max results (1–50, default 10).
curl "https://api.relm.ai/v1/documents/search?q=roof+replacement&portfolio_id=PORTFOLIO_ID" \
  -H "Authorization: Bearer relm_sk_..."

Chats

Read your Relm AI chats — org-shared and your private ones.

GET/v1/chats

Your chats across the org: shared "edit" chats visible to the whole org plus your own private "research" chats, newest first. Returns concise summaries (id, property/portfolio, title, type, message_count) — no message bodies. Filter by portfolio_id, prop_id, or chat_type; paginated.

Parameters

  • portfolio_id query — Restrict to chats about one portfolio.
  • prop_id query — Restrict to chats about one property.
  • chat_type query — "edit" (org-shared) or "research" (your private) only.
  • limit query — Max to return (1–200, default 50).
  • offset query — Rows to skip (default 0).
curl "https://api.relm.ai/v1/chats?chat_type=research&limit=20" \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/chats/{chat_id}

Metadata for one chat you can see (property/portfolio, title, type, message_count). 404 for unknown or not-visible chats.

Parameters

  • chat_id path · required — Chat id.
curl https://api.relm.ai/v1/chats/CHAT_ID \
  -H "Authorization: Bearer relm_sk_..."
GET/v1/chats/{chat_id}/messages

The chat transcript, paginated by message index (offset). Each message carries its text, sender type (human/agent), sender name, and timestamp. 404 if the chat isn’t visible to you.

Parameters

  • chat_id path · required — Chat id.
  • limit query — Max messages (1–500, default 100).
  • offset query — Message index to start from (default 0).
curl "https://api.relm.ai/v1/chats/CHAT_ID/messages?limit=100&offset=0" \
  -H "Authorization: Bearer relm_sk_..."

Errors & rate limits

Errors use a consistent envelope (5xx responses stay generic):

{ "detail": { "error": "insufficient_scope", "message": "..." } }

Branch on error, never on message — the wording is plan-aware and changes without notice. A billing refusal carries reason as well, which is where the subscription cases below are named.

StatusCodeWhen
401invalid_api_keyThe key is missing, malformed, revoked, expired, or not one we issued. These are deliberately indistinguishable — there is no separate revoked or expired code.
402credit_limit_reachedStarting an Intel Report, or a paid data lookup (preview, property records, public records, library query, search or ask), with no credits left on the org.
402pro_forma_limit_reachedStarting a pro-forma without an active subscription.
402insufficient_creditsThe pre-run credit gate refused the spend before any work began.
402reason: subscription_endedThe subscription is over. An ended trial is this refusal with subscription_status: trial_expired alongside it — not a code of its own.
403insufficient_scopeA read-scope key on a write verb (anything but GET / HEAD / OPTIONS).
403plan_ineligibleThe org’s plan is not eligible for API access, so no key can authenticate.
403demo_property_read_onlyA write against a demo property, which is permanently read-only.
403demo_portfolio_read_onlyA write against the demo portfolio itself — adding, renaming, or removing.
403demo_action_disabledStarting an Intel Report or a pro-forma on a demo property.
404—No such record, or it belongs to an org this key does not act in.
422—The request body or query failed schema validation.
429rate_limit_exceededOver the per-key or per-org rate limit. Honour the Retry-After header.

Over the rate limit you get 429 with a Retry-After header; retry after that many seconds. 5xx responses stay generic.

Over MCP the same refusals come back as the tool’s text result rather than as a transport error, formatted <code>: <message> — so the code survives and a failed tool call is readable by the agent. The one difference is the rate limit, which reports rate_limited there and rate_limit_exceeded over REST.

MCP server

Prefer to drive the API from an AI agent? Connect the Relm MCP server (https://api.relm.ai/mcp/) to Cursor, Claude, or any MCP client. The MCP guide has the setup config and a description of every tool.

Step-by-step guides with screenshots: Claude · ChatGPT · Gemini · Grok · GitHub Copilot · Cursor & others · Troubleshooting. The browser versions of ChatGPT and Gemini cannot connect yet; see which assistants can.

Keys are scoped and revocable, calls are rate limited and isolated to your organization, and every research finding is traceable to its source. Your data is never used to train models without your consent.

Build on Relm

Start a subscription, mint an API key, and make your first request in minutes.