lunterodevelopers
GuidesAPI Reference

Requests and pagination

Build precise searches, move through results, select translated text and interpret the values returned by the API.

Most integrations need the same foundations: construct a URL, apply the right filters, read a JSON response and request the next page. Luntero keeps those conventions consistent across its datasets, while each endpoint defines the fields and filters that make sense for that data.

Use this guide when you are turning a successful first request into a repeatable workflow. Keep the API reference nearby for the exact contract of the endpoint you are using.

Construct a request

The data API uses versioned paths under https://api.luntero.com/v1. Data operations are read-only GET requests. Put filters in query parameters and authentication in the Authorization header.

Let a URL builder handle encoding. It will preserve a search phrase or cursor without interpreting spaces, punctuation or reserved characters as part of the URL structure.

curl --fail-with-body --silent --show-error \
  --get 'https://api.luntero.com/v1/listings' \
  --data-urlencode 'min_price=1000' \
  --data-urlencode 'max_price=1800' \
  --data-urlencode 'min_size=45' \
  --data-urlencode 'limit=20' \
  -H "Authorization: Bearer $LUNTERO_API_KEY"

Omit a filter when you do not want to apply it. An empty string is not a general-purpose way to turn a filter off. Unknown query parameters, unsupported enum values and repeated parameters return 400 rather than being silently ignored.

For example, use furnishing=FURNISHED when you mean furnished homes. Do not send furnishing=furnished, or repeat furnishing twice to express a multi-select filter: that is not the current contract.

Combine the filters supported by your endpoint

Listing filters let you narrow a search by location, owner, price, size, total room count, furnishing and property type. Free-text q is supported where documented. A filter available on listings is not automatically available on articles, locations or provider directories.

IntentListing parameterMeaning
Search a placelocation_idAn ID returned by the locations API, not a city slug.
Set a budgetmin_price, max_priceMonthly advertised rent in EUR.
Set an area rangemin_size, max_sizeProperty area in square metres.
Require enough roomsmin_roomsMinimum total rooms, not bedrooms.
Select furnishingfurnishingSHELL, FURNISHED or UPHOLSTERED.
Choose a page sizelimitBetween 1 and 100; the default is 20.

Keep minimum values at or below their corresponding maximum values. If you need a property type, use the property_type value documented by the schema or returned in records; do not substitute your own display label.

For a location-driven search, resolve the place first. The rental search walkthrough shows how to request city records, let the user choose one, and use its ID for listing search.

Understand the response envelope

A paginated collection returns an array, pagination information and request metadata:

{
  "data": [],
  "pagination": {
    "limit": 20,
    "has_more": false,
    "next_cursor": null
  },
  "meta": {
    "locale": "en",
    "request_id": "example-request-id"
  }
}

This example is a successful empty search. Show an empty state and let the user adjust their filters; do not turn the absence of matches into an application error.

A detail response returns an object under data and the same meta object, without collection pagination. Some bounded collections, such as similar listings and metric discovery, also return arrays without a cursor. Check the response schema instead of assuming that every data array can be paginated.

Pagination

Collection results use stable ascending ID order. There are no page-number or offset parameters, and ID order does not imply newest-first, cheapest-first or geographical relevance.

When more results are available, the response contains pagination.has_more: true and a non-null pagination.next_cursor. Pass that cursor back on the next request with the same endpoint, filters, locale and limit.

NEXT_CURSOR='COPY_NEXT_CURSOR_FROM_THE_PREVIOUS_RESPONSE'

curl --fail-with-body --silent --show-error \
  --get 'https://api.luntero.com/v1/listings' \
  --data-urlencode 'min_price=1000' \
  --data-urlencode 'max_price=1800' \
  --data-urlencode 'min_size=45' \
  --data-urlencode 'limit=20' \
  --data-urlencode "cursor=$NEXT_CURSOR" \
  -H "Authorization: Bearer $LUNTERO_API_KEY"

The cursor can look long. Treat it as an opaque string: copy it in full, let your URL builder encode it, and leave its contents alone. It carries signed pagination state, including the position, request scope and an expiry.

Cursors expire after 24 hours. Changing a filter, selecting another language, changing page size or moving to a different endpoint also invalidates the previous cursor. In those situations, remove cursor and request the first page again.

A useful interface keeps the current filter state together with its cursor. When the user changes a filter, clear both the displayed results and the old cursor before loading the replacement search. When loading more, append results and store the new cursor returned by that request.

Pagination is not a snapshot

Listings can be published, removed or become inactive while you page through results. Stable ID ordering supports continuation, but it does not freeze the dataset. Avoid presenting a multi-page traversal as an exact historical snapshot.

Stop when has_more is false. Do not keep requesting a page with a null cursor, because omitting it starts the search from the beginning.

Language

English is the default. Use the locale parameter when you want existing translations of free-text fields:

ParameterRequested text
Omitted, or locale=enEnglish
locale=nlDutch
locale=deGerman
locale=frFrench
locale=esSpanish

If a requested translated field is missing, it falls back to English. A value that is unavailable in either language remains null. The request does not trigger translation or generate new content.

IDs, canonical slugs, brand names, enum codes, dates and numeric values keep their meaning. Location display names use the requested language. meta.locale reports the requested locale; it is not a guarantee that every text field in the result was translated.

Use the same locale across a collection request and its detail requests so the interface stays coherent. If the reader changes language midway through a paginated search, restart from the first page with that locale.

Values, units and content

Use the schema's unit, not a guess based on the size of a number. Listing price_eur is monthly euros, while internet plan fields that name cents use integer cents. Area fields name square metres. Air-quality measurements include their own component and unit.

Timestamps use ISO 8601 and date-only values use YYYY-MM-DD. A null field means that no value is available; it does not mean zero, an empty string or a negative result. For instance, an unknown price should appear as unavailable, not as a free product.

Resource content may contain stored Markdown. Render it with a Markdown renderer that suits your application and sanitizes any HTML it accepts. Treat links, descriptions and images as external content rather than trusted application code.

Data coverage

The current listing search includes active, published rental listings. Public owner profiles and editorial or product records follow their own visibility rules. A detail record can disappear between two requests, so retain a useful empty or unavailable state in your interface.

Current listing statistics and historical market observations answer different questions. The former aggregate matching active listings. The latter group advertised rental supply by first-seen month, subject to stored coverage and minimum sample sizes. Neither represents all homes or signed rental contracts in a city.

Likewise, air-quality results are stored observations, and energy-label lookup reads existing verified registrations. A missing stored label is not proof that a property has no registered label.

For the dataset-specific details, freshness fields and source attribution, read Dataset coverage in the reference section. Preserve the supplied source links and give users enough context to understand the data you display.

On this page