Generate SQL: POST /v1/ask
Receives a natural language question and returns a SELECT validated by the SQL Guard, with the values in params. Each call makes 1 to 2 calls to the LLM of your tenant (the second one is an automatic correction when the first SQL is rejected).
Request
Headers:
Authorization: Bearer tsk_1a2b3c4d_...
Content-Type: application/json
Accept-Language: en (optional)
Body:
{
"question": "How many orders were placed yesterday?",
"end_user": "5f0c3a9e7b1d4e2f8a6c0b9d3e7f1a2c4b5d6e7f8091a2b3c4d5e6f708192a3b",
"max_rows": 100,
"locale": "en"
}
| Field | Type | Required | Description |
|---|---|---|---|
question | string | yes | The question, up to 2,000 characters. |
end_user | string | yes* | Opaque identifier of the person who asked, 16 to 128 characters (A-Z, a-z, 0-9, _, -). See End-user identifier. *Required for most customers. |
max_rows | integer | no | Maximum rows, ≥ 1. Default 100, ceiling 1,000. The API applies a LIMIT to the SQL; aggregations that return a single row are left without LIMIT. |
locale | string | no | Language of message and explanation: pt-BR, en or es. Variants such as en-US or es-419 fall back to the primary language (en, es). Precedence: this field → Accept-Language header → the tenant's default language (set in the panel) → pt-BR. |
Unknown fields are rejected with 422 invalid_request. The whole body can be up to 96 KiB.
Response 200
{
"sql": "SELECT count(*) AS total FROM public.pedidos WHERE criado_em >= %(start)s AND criado_em < %(end)s",
"params": { "start": "2026-10-22", "end": "2026-10-23" },
"dialect": "postgres",
"tables": ["public.pedidos"],
"explanation": "Counts the orders created yesterday.",
"locale": "en",
"request_id": "req_0f3c9a7d2b6e4f1a8c5d0e9b7a6f3c21"
}
| Field | Type | Description |
|---|---|---|
sql | string | A single SELECT, in your schema's dialect, with %(name)s placeholders. |
params | object | Values of the placeholders: string, number, boolean or null. Can be {}. |
dialect | string | postgres, mysql or firebird. |
tables | list of strings | Tables used by the query. |
explanation | string or null | Short explanation of what the query does, in the resolved language. |
locale | string | Language used in the response. |
request_id | string | req_ + 32 hexadecimal characters. Use it to send feedback and to fetch the answer from the history. |
The same request_id comes in the X-Request-ID header of every response, including error responses.
About the returned SQL
- Table and column names are the ones in your database, exactly as they appear in the registered DDL.
- User text is never concatenated into the SQL: values such as names, dates and search terms arrive in
params. - Every
%that is not a placeholder comes out escaped as%%(for exampleLIKE 'A%%'), in the style of Python drivers. That is why you should always passparamsto the driver, even when empty. See Run the SQL. - Relative dates ("yesterday", "last month") are resolved at the moment of the question, with today's date sent to the LLM.
Common errors
| HTTP | code | What to do |
|---|---|---|
| 400 | end_user_required | Send end_user. |
| 401 | invalid_api_key | Fix the key. |
| 409 | schema_not_configured, llm_not_configured | The administrator needs to finish the configuration in the panel. |
| 422 | no_answer | The schema does not allow an answer; ask the user to rephrase. |
| 422 | sql_rejected | The generated SQL did not pass the Guard, even after the automatic correction. |
| 429 | rate_limited | Wait for the time in the Retry-After header. |
| 502 | llm_error | Failure at the LLM provider; see details.reason. |
The full list is in Errors.
Latency
A question normally takes a few seconds, and up to twice as long when the automatic correction runs. Set the read timeout of your HTTP client to at least 60 seconds, and do not automatically retry a POST /v1/ask that timed out, because each attempt spends tokens from your LLM.
Example
curl -sS -X POST "https://app.datafala.com.br/v1/ask" \
-H "Authorization: Bearer $DATAFALA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"question": "Top 5 customers by revenue in 2026", "end_user": "'"$END_USER"'", "max_rows": 5}'
More examples in Integration examples.