factqlDocs
Get an API key
API · Ask a question

Ask a question

POST/query

Send one question and get one JSON answer: the SQL that ran, the rows, and a plain-English explanation of those rows. Each call stands alone; for follow-ups, use Conversations.

curl -X POST "https://api.factql.ai/query" \
  -H "X-API-Key: $FACTQL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"q": "What were the top 5 products by revenue?"}'
Headers
X-API-Keystring · required
Your API key.
X-Tenant-IDstring
Only for account-level keys that reach several tenants: the tenant to ask.
Body
qstring · required
The question, 1 to 4,096 characters.
workspace_idstring
The workspace to ask. Keys bound to a workspace, like the one from signup, don't need it.
dry_runboolean
Write the SQL without running it. Default false.

Response

200
{
  "type": "text2sql",
  "result": "success",
  "sql": "SELECT region, SUM(revenue) AS total_revenue FROM orders GROUP BY region ORDER BY total_revenue DESC",
  "data": {
    "columns": ["region", "total_revenue"],
    "rows": [["North", 43148.0], ["South", 41223.0], ["West", 41113.0], ["East", 40823.0]]
  },
  "explanation": "North leads with 43,148, followed by South (41,223), West (41,113) and East (40,823).",
  "lineage": {
    "tables": ["orders"],
    "measures": ["SUM(revenue)"],
    "grouped_by": ["region"],
    "text": "Computed SUM(revenue); from orders; broken down by region."
  },
  "trace_id": "c5f50cff-…"
}
Response fields
typestring
text2sql, analytics or rag: how the question was answered.
resultstring
success, or another value when there is no data answer, such as a clarifying question.
sqlstring
The SQL that ran.
dataobject
columns and rows, in column order.
explanationstring
Describes only the rows returned. Safe to show to your user.
lineageobject
Tables, measures, period, filters and groupings behind the numbers, with a one-line text summary.
breakdownobject
Only on a total question, such as “revenue across regions”. data holds the total; breakdown holds the per-group columns, rows and sql.
trace_idstring
Quote it when you contact support.

When FactQL can't answer

A question the data can't answer still returns 200, with "result": "error" and an explanationwritten for your user. Show the explanation; don't retry the same question.

{
  "type": "text2sql",
  "result": "error",
  "error": "Invalid or unsafe SQL generated",
  "error_type": "QUERY_ERROR",
  "decline_reason": "no_safe_sql",
  "explanation": "I couldn't find data that answers this. Try naming the table or column you mean.",
  "trace_id": "c5f50cff-…"
}
Errors
401The key is missing, invalid or revoked.
402A spend cap is reached. On the free plan that's $10 a month; it resets on the 1st.
403The key belongs to a different tenant, or the sample key tried something it can't do.
422The workspace has no data yet, or the body is invalid (for example an empty q).
429Too many requests: 30 a minute per key on the free plan. Wait Retry-After seconds.
503The model service is briefly unavailable. Retry after Retry-After seconds.