Celiq
v0.12Open app โ†—
Docs/Developer/Orion API

Orion API

Query your governed metrics from anywhere โ€” a single REST endpoint takes a natural language question and returns a governed answer as prose plus structured JSON.

WeaverLuminaryDeveloperUpdated Jun 2026 ยท 14 min read
โœฆ
In this section
Part of Celiq's semantic layer platform. Connect your warehouse, model your data once, query it everywhere.
๐Ÿ’ก
Tip
What it is: A public REST API that exposes Orion โ€” ask a question in plain English, get a governed answer back as prose plus structured data.
Why use it: Query your governed metrics from a Slack bot, a mobile app, an internal tool, or a Google Sheet โ€” without rebuilding your semantic layer anywhere else.
Where to find it: Generate keys in Lumen โ†’ API Access. Call the API at https://app.celiq.co/api/v1.
Who can use it: Any workspace with an API key. Keys are workspace-scoped.

The Orion API turns Celiq into headless analytics. Instead of opening the app, you POST a question to a single endpoint and get back the same governed answer Orion would give you in chat โ€” grounded in your reveals, with provenance and freshness attached. One API call returns a natural language answer and the structured data behind it.

This is the same Orion you use inside Celiq. It respects the same governed reveals, the same metric definitions, and the same workspace memory. The difference is that the answer comes back as JSON you can parse, store, or forward.

How it differs from the REST API

Celiq has two APIs, and they serve different jobs:

Orion API (this page)REST API
Base URLhttps://app.celiq.co/api/v1https://api.celiq.co/v1
InputA natural language questionStructured query (node, dimensions, measures)
OutputProse answer + data + provenanceRaw query results
Auth headerAuthorization: Bearer celiq_sk_...Authorization: Bearer <key>
Best forBots, embedded Q&A, Sheets, ad-hoc questionsProgrammatic dashboards, model management, sync

Use the Orion API when you want a governed answer to a question. Use the REST API when you already know exactly which fields you want and just need the rows.

Authentication

All Orion API requests require an API key in the Authorization header:

Authorization: Bearer celiq_sk_your_key_here

Keys are workspace-scoped โ€” a key only ever sees the reveals and data in the workspace it was created in. There is no separate IP allowlist; access is controlled entirely by the key.

โš ๏ธ
Warning

Treat an API key like a password. Anyone with the key can query your governed metrics. Store keys in environment variables or a secret manager โ€” never commit them to source control or paste them into client-side code.

Creating a key

1

Open API Access in Lumen

Go to Lumen โ†’ API Access. You'll see the list of existing keys (by prefix only) and a + Create API Key button.

2

Name the key

Give the key a descriptive name like production-key or slack-bot. The name only helps you identify it later โ€” it has no effect on permissions.

3

Copy the key once

Celiq shows the full key โ€” celiq_sk_... โ€” exactly once, with a Copy button and the warning "This key will not be shown again." Copy it immediately and store it somewhere safe.

4

Use the prefix to identify it later

After you close the dialog, only the prefix (e.g. celiq_sk_abc...) is ever shown again, alongside the last-used time. If you lose the full key, revoke it and create a new one.

Managing keys

The API Access panel lists every key with its prefix, last-used time, and a Revoke button. Revoking a key takes effect immediately โ€” the next request using it returns 401. Keys can also carry an optional expiry; an expired key behaves exactly like a revoked one.

Key management itself happens through your authenticated Lumen session (cookie auth), not through the Orion API. The Orion API endpoints (/api/v1/*) are reachable only with a celiq_sk_ key, and the key endpoints (/api/keys) are reachable only from a signed-in session.

Base URL

https://app.celiq.co/api/v1

All endpoints below are relative to this base URL.


POST /api/v1/ask

Ask Orion a data question. This is the primary endpoint and the one you'll use most.

POST /api/v1/ask
Request body:
json
{
  "question": "what was revenue last week?",
  "domain": "ecommerce_bigquery"
}
FieldTypeRequiredDescription
questionstringYesThe question in plain English, exactly as you'd type it in Orion chat.
domainstringNoThe domain to scope the question to (e.g. ecommerce_bigquery). If omitted, Orion picks the most relevant domain automatically.
Example request:
curl
curl -X POST https://app.celiq.co/api/v1/ask \

-H "Authorization: Bearer celiq_sk_your_key_here" \

-H "Content-Type: application/json" \

-d '{"question": "what was revenue last week?"}'

Response:
json
{
  "answer": "Revenue last week was $284,000, up 11% from the prior week. The increase was driven mostly by the Home & Garden category.",
  "data": {
    "rows": [
      { "week": "2026-06-01", "revenue": 284000 },
      { "week": "2026-05-25", "revenue": 255800 }
    ],
    "columns": ["week", "revenue"],
    "metric": "revenue",
    "value": 284000,
    "period": "last_week"
  },
  "provenance": {
    "source": "semantic layer",
    "reveal": "orders_analysis",
    "reveal_backed": true,
    "freshness": "2026-06-12",
    "confidence": "high"
  },
  "usage": {
    "tokens_used": 847,
    "latency_ms": 1240
  }
}

Response fields

FieldDescription
answerThe natural language answer โ€” the same prose Orion shows in chat.
data.rowsThe raw result rows behind the answer.
data.columnsColumn names in the order they appear in each row.
data.metricThe primary metric the question was about, if Orion identified one.
data.valueThe headline numeric value for that metric.
data.periodThe time period the answer covers (e.g. last_week, last_30_days).
provenance.sourcesemantic layer when a governed reveal answered the question, or raw SQL when Orion fell back to ad-hoc SQL.
provenance.revealThe reveal that produced the answer (only when source is semantic layer).
provenance.reveal_backedtrue if the answer used a governed reveal; false for raw-SQL fallback.
provenance.freshnessThe date of the most recent data in the result.
provenance.confidencehigh for governed reveals, medium for raw-SQL approximations.
usage.tokens_usedLLM tokens consumed by this request.
usage.latency_msServer-side latency in milliseconds.
๐Ÿ“
Note

Always check provenance.reveal_backed before trusting a number programmatically. true means the answer used a metric definition your team wrote in CML. false means Orion approximated the metric from your schema โ€” fine for exploration, but verify the definition before wiring it into a report.


GET /api/v1/reveals

List the reveals available in your workspace. Use this to discover what you can ask about before sending a question.

GET /api/v1/reveals
Example request:
curl
curl https://app.celiq.co/api/v1/reveals \

-H "Authorization: Bearer celiq_sk_your_key_here"

Response:
json
{
  "reveals": [
    {
      "name": "orders_analysis",
      "domain": "ecommerce_bigquery",
      "description": "Revenue, orders, and AOV by day, channel, and category.",
      "metrics": ["revenue", "order_count", "aov"]
    },
    {
      "name": "orders_customers",
      "domain": "ecommerce_bigquery",
      "description": "Customer acquisition and retention metrics.",
      "metrics": ["cac", "ltv", "new_customers", "return_rate"]
    }
  ]
}

GET /api/v1/metrics/:reveal_name

Return the metric definitions for a single reveal. Use this to understand exactly how a metric is calculated before you ask about it.

GET /api/v1/metrics/orders_customers
Example request:
curl
curl https://app.celiq.co/api/v1/metrics/orders_customers \

-H "Authorization: Bearer celiq_sk_your_key_here"

Response:
json
{
  "reveal": "orders_customers",
  "domain": "ecommerce_bigquery",
  "metrics": [
    {
      "name": "cac",
      "label": "Customer Acquisition Cost",
      "description": "Marketing spend divided by new customers acquired.",
      "unit": "dollars"
    },
    {
      "name": "return_rate",
      "label": "Return Rate",
      "description": "Returned orders as a share of total orders.",
      "unit": "percent"
    }
  ]
}

Rate limits

Each API key is limited to 100 requests per hour by default. The limit is enforced per key, not per IP address โ€” so spreading calls across multiple machines using the same key does not raise the ceiling.

When you exceed the limit, the API returns 429 Too Many Requests. Wait until the start of the next hourly window and retry. If you need a higher limit, contact us.

๐Ÿ“
Note

Rate limits are per key. If you have separate keys for production and a Slack bot, each gets its own 100-requests-per-hour budget. This is a good reason to issue one key per integration rather than sharing a single key everywhere.


Errors

All errors are returned as JSON with an error field and the appropriate HTTP status code.

json
{
  "error": "Invalid or revoked API key."
}
CodeMeaning
400Bad request โ€” the body is missing question or is malformed JSON.
401Missing, invalid, revoked, or expired API key.
403The key is valid but does not have access to the requested resource.
404Reveal not found (for /metrics/:reveal_name).
429Rate limited โ€” more than 100 requests in the current hour for this key.
500Internal error. Retry; if it persists, contact support.

Code examples

curl

curl
curl -X POST https://app.celiq.co/api/v1/ask \

-H "Authorization: Bearer celiq_sk_your_key_here" \

-H "Content-Type: application/json" \

-d '{"question": "what was revenue last week?", "domain": "ecommerce_bigquery"}'

Python

ask.py
import os

import requests

API_KEY = os.environ["CELIQ_API_KEY"]

resp = requests.post(

"https://app.celiq.co/api/v1/ask",

headers={"Authorization": f"Bearer {API_KEY}"},

json={"question": "what was revenue last week?"},

timeout=30,

)

resp.raise_for_status()

result = resp.json()

print(result["answer"])

Only trust the number programmatically if it came from a governed reveal.

prov = result["provenance"]

if prov["reveal_backed"]:

print(f"{result['data']['metric']} = {result['data']['value']} "

f"(reveal: {prov['reveal']}, as of {prov['freshness']})")

else:

print("Warning: answer used raw SQL, not a governed reveal.")

JavaScript / Node.js

ask.js
const API_KEY = process.env.CELIQ_API_KEY;

const resp = await fetch("https://app.celiq.co/api/v1/ask", {

method: "POST",

headers: {

"Authorization": Bearer ${API_KEY},

"Content-Type": "application/json",

},

body: JSON.stringify({ question: "what was revenue last week?" }),

});

if (!resp.ok) {

throw new Error(Celiq API error ${resp.status}: ${await resp.text()});

}

const result = await resp.json();

console.log(result.answer);

if (result.provenance.reveal_backed) {

console.log(${result.data.metric} = ${result.data.value});

}

Google Sheets (Apps Script)

Add a custom =ASK_ORION(...) function to any sheet. Open Extensions โ†’ Apps Script, paste the code below, then store your key in Project Settings โ†’ Script Properties as CELIQ_API_KEY.

Code.gs
/**

* Ask Orion a question and return the headline value.

* Usage in a cell: =ASK_ORION("what was revenue last week?")

*/

function ASK_ORION(question) {

const apiKey = PropertiesService.getScriptProperties()

.getProperty("CELIQ_API_KEY");

const resp = UrlFetchApp.fetch("https://app.celiq.co/api/v1/ask", {

method: "post",

contentType: "application/json",

headers: { Authorization: "Bearer " + apiKey },

payload: JSON.stringify({ question: question }),

muteHttpExceptions: true,

});

const result = JSON.parse(resp.getContentText());

if (resp.getResponseCode() !== 200) {

return "Error: " + (result.error || resp.getResponseCode());

}

// Return the numeric value if there is one, otherwise the prose answer.

return result.data && result.data.value != null

? result.data.value

: result.answer;

}

For a richer, no-code Sheets experience, see the Celiq for Sheets add-on, which wraps this API in a sidebar.


Best practices

  • One key per integration. Issue a separate key for each app, bot, or environment so you can revoke one without breaking the others โ€” and so each gets its own rate-limit budget.
  • Store keys in secrets, not code. Use environment variables or a secret manager. Never embed a key in client-side JavaScript or a public repository.
  • Check reveal_backed before trusting numbers. A medium-confidence, raw-SQL answer is fine for exploration but should be verified before it drives a report or alert.
  • Scope with domain when you can. Passing the domain makes routing faster and more accurate, especially in workspaces with many reveals.
  • Cache where it makes sense. If many users ask the same question, cache the response for a few minutes rather than burning rate-limit budget on identical calls.

Limitations

  • Read-only. The Orion API answers questions and lists reveals and metrics. It cannot create reveals, edit your model, or write data.
  • Reveal-first, with raw-SQL fallback. When no governed reveal matches, Orion may answer with raw SQL at medium confidence. Check provenance to tell the difference.
  • English answers. Questions can be asked in any language, but answer prose is returned in English.
  • No streaming. Responses are returned as a single JSON payload once the answer is complete, not as a token stream.
  • Rate limited. 100 requests per hour per key by default.