Getting started

Ask questions about your Odoo, PostgreSQL or MySQL data, or about the files uploaded to your workspace, from your own code, scripts and automations. Get the answer back as text, tables and figures. Read-only: the API never changes your data.

/docs/api · Français

What the API does

  • Ask a question in plain language about one of your connections, and get the answer as text, as structured json (tables and figures) or as a ready-to-send email.
  • Ask about the documents and spreadsheets uploaded to your workspace (the Uploaded files connection). A documents answer lists its sources: file, page or section.
  • Read an answer later when it takes longer than you want to wait.
  • List your connections. Every new workspace also has sample connections with fictional data, so you can try the API before connecting your own.
  • Each question is answered on its own: a conversation (threadId) groups your questions for reading, but earlier questions are not used as context. Ask complete questions.
  • STYD answers in the language of the question, or in the one you ask for (“answer in German”). When the language is unclear, it answers in English. Names, codes and figures from your data are not translated.

Only asking costs a prompt: 1 prompt per answer. Every other call is free.

Create an API key

Get an API key

Only workspace owners and admins can create keys. The key is shown once: copy it then.

Send the key on every request, as x-api-key: <key> or Authorization: Bearer <key>. A key belongs to one workspace. Keep it secret: anyone with it can ask questions on your workspace.

A workspace can have 5 active keys. A key expires never, in 30 days, in 90 days or in 1 year: you choose when you create it. A revoked or expired key stops working at once. The workspace owners and admins can read every conversation a key creates, in the STYD web app.

Your first call

1. Check the key. The answer names your workspace and the prompts it has left (free).

curl https://speaktoyourdatabase.com/api/v1/context \
  -H "x-api-key: $STYD_API_KEY"

2. Ask a question. connectionId is an id from GET /connections (in a new workspace, try a sample connection). This uses 1 prompt.

curl "https://speaktoyourdatabase.com/api/v1/chat?format=json" \
  -H "x-api-key: $STYD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "connectionId": 3, "prompt": "How many customers are there?" }'

The answer comes back within 90 seconds (200). If it takes longer you get 202 with a runId: call GET /runs/{runId}?wait=30 until its status is complete or failed. A failed question is refunded (promptsUsed is 0).

JavaScript and Python

The same question from code: ask, then poll a 202 until the status is complete or failed.

// Node.js 20 or later. The key is in the STYD_API_KEY environment variable.
const BASE = "https://speaktoyourdatabase.com/api/v1";
const headers = { "x-api-key": process.env.STYD_API_KEY, "Content-Type": "application/json" };

async function call(url, options) {
  const res = await fetch(url, options);
  const body = await res.json();
  // Every error is { error, message, status }. A 429 also has a Retry-After header (seconds).
  if (!res.ok) throw new Error(`${res.status} ${body.error}: ${body.message}`);
  return body;
}

async function ask(connectionId, prompt) {
  // Keep the same Idempotency-Key when you retry this question: it is never asked or charged twice.
  let answer = await call(`${BASE}/chat?format=json`, {
    method: "POST",
    headers: { ...headers, "Idempotency-Key": crypto.randomUUID() },
    body: JSON.stringify({ connectionId, prompt }),
  });
  // 202: still being answered. Poll until the status is complete or failed.
  while (answer.status === "queued" || answer.status === "running") {
    answer = await call(`${BASE}/runs/${answer.runId}?wait=30&format=json`, { headers });
  }
  return answer; // outcome: answered, refused or failed (a failed question uses no prompt)
}

ask(3, "How many customers are there?").then((answer) => console.log(answer.text));
# Python 3 with the requests package. The key is in the STYD_API_KEY environment variable.
import os, uuid, requests

BASE = "https://speaktoyourdatabase.com/api/v1"
HEADERS = {"x-api-key": os.environ["STYD_API_KEY"]}

def call(method, url, **kwargs):
    res = requests.request(method, url, headers={**HEADERS, **kwargs.pop("headers", {})}, timeout=130, **kwargs)
    body = res.json()
    # Every error is {error, message, status}. A 429 also has a Retry-After header (seconds).
    if not res.ok:
        raise RuntimeError(f"{res.status_code} {body['error']}: {body['message']}")
    return body

def ask(connection_id, prompt):
    # Keep the same Idempotency-Key when you retry this question: it is never asked or charged twice.
    answer = call("POST", f"{BASE}/chat", params={"format": "json"},
                  headers={"Idempotency-Key": str(uuid.uuid4())},
                  json={"connectionId": connection_id, "prompt": prompt})
    # 202: still being answered. Poll until the status is complete or failed.
    while answer["status"] in ("queued", "running"):
        answer = call("GET", f"{BASE}/runs/{answer['runId']}", params={"wait": 30, "format": "json"})
    return answer  # outcome: answered, refused or failed (a failed question uses no prompt)

print(ask(3, "How many customers are there?")["text"])

Postman, Insomnia or a code generator: import the OpenAPI file from https://speaktoyourdatabase.com/openapi.json.

Limits

  • Questions: 30 per minute per key. Up to 8 questions can wait per key, and 20 per workspace (shared with AI apps such as Claude). Over a limit you get 429 with a Retry-After header in seconds: nothing is asked and no prompt is used. When STYD is very busy, a question can also get 429 (reason busy): try again later.
  • At the same time: 2 questions run at once per key, 3 per workspace and 10 across STYD. The others wait their turn, and that waiting counts in wait.
  • Waiting: POST /chat waits 90 seconds by default, at most 110. GET /runs/{runId} answers at once by default and waits at most 110 seconds.
  • Question length: at most 1,000 characters. A longer question gets 400 and is not asked.
  • Answer size: tables are cut at 50 rows × 12 columns and 200 characters per cell, and a whole answer at 32 KB. truncated says when something was cut.
  • Failed questions: 20 questions in an hour that used no prompt (failed, or stopped at a setup step) pause the key’s new questions for up to an hour (429 too_many_failed_questions).
  • Lists: GET /threads returns the key’s 100 newest conversations and GET /connections every connection, in one reply (no pages).

What the API does not do

  • It never changes your data: it only reads.
  • No streaming: you get the whole answer at once, or you poll for it.
  • No webhooks: STYD does not call you back. Ask, then poll GET /runs/{runId}.
  • No file upload yet: upload documents and spreadsheets in the STYD web app, then ask about them over the API.
  • No memory between questions, and no CSV export: the formats are json, text and email. The email’s greeting and closing are in French for a French answer, in English otherwise.
  • A key reaches the public API only: any other path answers 403 api_key_route_not_allowed.

Go further

Every endpoint, field and error code is in the API reference, where you can also try each request with your key. No code? Use the Make app.