Skip to main content

Response Format

flAPI returns JSON by default and also supports CSV and Apache Arrow IPC stream via content negotiation. Pagination is controlled per-endpoint with the with-pagination flag.

There is no response: block in endpoint YAML — formats are negotiated at request time and pagination is configured by a single top-level boolean.

Default JSON Response

With pagination enabled (the default), the response is:

{
"data": [
{ "id": 1, "name": "Customer A" },
{ "id": 2, "name": "Customer B" }
],
"next": "/customers/?offset=100&limit=100",
"total_count": 1532
}
FieldTypeDescription
dataarrayQuery result rows
nextstringPath + querystring for the next page, or "" if no more
total_countintegerTotal rows matching the query (across all pages)

With pagination disabled the body is the bare data array (no envelope).

In addition to the body, flAPI always sets the following response headers when pagination is on:

HeaderValue
X-Total-CountSame as total_count
X-OffsetCurrent offset
X-LimitCurrent limit
X-NextSame as next

Disabling Pagination

url-path: /customers/lookup/
method: GET
template-source: lookup.sql
connection: [customers-parquet]

with-pagination: false # body is just the rows, no envelope

When with-pagination: false:

  • The body is the raw data array (no next/total_count envelope).
  • The limit and offset query parameters are still accepted but no envelope/headers are added.

When with-pagination: true (default), the client can send ?limit=N&offset=M:

curl 'http://localhost:8080/customers/?limit=20&offset=40'

Content Negotiation

Clients request a format with either the Accept header or the format query parameter. The query parameter wins when both are sent.

JSON (Default)

Content-Type: application/json

curl http://localhost:8080/customers/
curl -H 'Accept: application/json' http://localhost:8080/customers/

CSV

Content-Type: text/csv

curl 'http://localhost:8080/customers/?format=csv'
curl -H 'Accept: text/csv' http://localhost:8080/customers/

Apache Arrow IPC Stream

Content-Type: application/vnd.apache.arrow.stream

curl 'http://localhost:8080/customers/?format=arrow' -o customers.arrow
curl -H 'Accept: application/vnd.apache.arrow.stream' \
http://localhost:8080/customers/ -o customers.arrow

Arrow responses use Arrow's native framing; flAPI disables HTTP compression for them (Arrow handles compression internally via LZ4/ZSTD) and sets Content-Length explicitly.

Error Responses

Validation failures return HTTP 400 with a JSON body listing each invalid field:

{
"errors": [
{ "field": "segment", "message": "Invalid enum value" },
{ "field": "id", "message": "Integer is less than the minimum allowed value" }
]
}
StatusMeaning
200Success
400Validation error (invalid or missing parameters)
401Missing/invalid authentication
403Authenticated but forbidden
404No endpoint matches the URL
429Rate limit exceeded
500Query execution or server error

Write Endpoints

For POST/PUT/PATCH/DELETE endpoints the response shape is different — see Write Operations:

{
"rows_affected": 1,
"data": [
{ "id": 42, "name": "New Widget", "price": 29.99 }
]
}

data is only present when the endpoint has operation.returns-data: true and the SQL template uses a RETURNING clause.

Column Formatting

DuckDB drives the output. Dates and timestamps come back as ISO 8601 strings; numerics keep their precision. Reshape values in SQL when you need a different form:

SELECT
STRFTIME(created_at, '%Y-%m-%d') AS date,
ROUND(price, 2) AS price,
COALESCE(optional_field, 'N/A') AS optional_field
FROM products

Best Practices

  • Leave with-pagination at the default unless you have a specific reason — it keeps responses bounded and surfaces total counts.
  • Validate every parameter so 400s come back with clear field-level messages.
  • For bulk exports, prefer ?format=arrow or ?format=csv over JSON.

Next Steps

🍪 Cookie Settings