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
}
| Field | Type | Description |
|---|---|---|
data | array | Query result rows |
next | string | Path + querystring for the next page, or "" if no more |
total_count | integer | Total 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:
| Header | Value |
|---|---|
X-Total-Count | Same as total_count |
X-Offset | Current offset |
X-Limit | Current limit |
X-Next | Same 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
dataarray (nonext/total_countenvelope). - The
limitandoffsetquery 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" }
]
}
| Status | Meaning |
|---|---|
200 | Success |
400 | Validation error (invalid or missing parameters) |
401 | Missing/invalid authentication |
403 | Authenticated but forbidden |
404 | No endpoint matches the URL |
429 | Rate limit exceeded |
500 | Query 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-paginationat 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=arrowor?format=csvover JSON.
Next Steps
- Parameters — configure request parameters
- Validation — surface clear 400 responses
- Write Operations —
rows_affected/RETURNING - Caching — reduce latency on large queries