Documentation menu
API

Filtering, sorting and fields

Narrow a collection, order it, and ask for less of it.

Filter syntax

filter[<field>][<op>]=<value>, in the query string, so a query is guessable, cacheable and linkable. Distinct fields are combined with AND; repeated values of the same field and operator are OR. Ranges take an open end as part of the value, so “at most 35” is a between with a null lower bound.

# At most 35 feet, as a range with an open end
filter[height_ft][between]=[null,35]

# Anything but short-term rental. Negation is first class
filter[use][nin]=short_term_rental

# The rule permits 35 feet, which is not the same question
filter[max_height_ft][permits]=35

# Only the rows a predicate could not evaluate: the audit query
filter[max_height_ft][lte]=35&filter_indeterminate=only

Operators

  • eq
  • ne
  • in
  • nin
  • lt
  • lte
  • gt
  • gte
  • between
  • prefix
  • contains
  • is_null
  • not_null
  • any
  • all
  • permits
  • forbids

Which of these a given field accepts is published at runtime, beside the field itself. An unknown field, operator or vocabulary value returns 422 naming the value and its position, so fix the request rather than reading an empty page as a real absence.

Filter outcomes

Every predicate sorts each candidate three ways, not two. Ask for districts where height is at most 35 feet and you still get back the districts with no height cap at all, marked rather than dropped.

match
The predicate was evaluated and the answer is yes. The row is returned.
no_match
The predicate was evaluated and the answer is no. The row is dropped, which is the only reason a row is ever dropped.
indeterminate
The predicate could not be evaluated, because the fact has no comparable value. The row is returned anyway, carrying its own status and citation, and counted in page.indeterminate.

The answer also carries a filter_status_indeterminate notice naming the count and the statuses involved, so a caller reading only the rows is still told. Control it with filter_indeterminate=include|exclude|only, which defaults to include. The only form is how “which of these towns is silent on height” becomes a query rather than a disclosure.

Two operators exist for the same reason. lte=35 asks whether the stated number is at most 35. permits=35 asks whether the rule admits 35 feet, which also matches every row where the standard is unlimited or cannot bind. The second is usually the question someone means.

Sorting

sort=name ascending, sort=-population descending. One signed field name does both jobs. Repeatable up to three keys, from a closed per-resource list published beside the resource; anything else is a 422 naming the parameter position.

Every sort carries a stable tiebreak on the row’s own id, even when your keys look unique, so paging over equal keys never repeats or skips a row. Rows with no comparable value sort last in both directions.

Field selection

field= takes names, named bundles and - suppression. The legal names are a closed list per resource.

field=default,-geometry
field=all,-provenance
field=control.value,control.citation

Six bundles are expensive and therefore opt-in rather than default: geometry, citation.quote, provenance, version.history, segment, coverage.detail.

Fields you cannot suppress

No parameter, bundle or toggle removes these. Expect them on every answer and write your parsing accordingly.

  • as_of
  • status
  • citation.id
  • effective_from
  • derivation
  • coverage

A CSV or a PNG cannot carry them, so those formats are labelled derived and ship the blocks beside the file as sidecars. Read the sidecars.

Walking the result set Taking it out as a file

Next

Rate limits and quota

The three refusals, and how to price a call before you make it.